0

I have an industrial sensor which provides me information via telnet over port 10001.

It has a Data Format as follows:

Table Data Format

Also the manual:

All the measuring values are transmitted int32 or uint32 or float depending on the sensors

Code

import telnetlib
import struct
import time

# IP Address, Port, timeout for Telnet
tn = telnetlib.Telnet("169.254.168.150", 10001, 10)


while True:
    op = tn.read_eager() # currently read information limit this till preamble
    print(op[::-1]) # make little-endian
    if not len(op[::-1]) == 0: # initially an empty bit starts (b'')
        data = struct.unpack('!4c', op[::-1]) # unpacking `MEAS`
    time.sleep(0.1)

my initial attempt:

  1. Connect to the sensor

  2. read data

  3. make it to little-endian

OUTPUT

b''
b'MEAS\x85\x8c\x8c\x07\xa7\x9d\x01\x0c\x15\x04\xf6MEAS'
b'\x04\xf6MEAS\x86\x8c\x8c\x07\xa7\x9e\x01\x0c\x15\x04\xf6'
b'\x15\x04\xf6MEAS\x85\x8c\x8c\x07\xa7\x9f\x01\x0c\x15'
b'\x15\x04\xf6MEAS\x87\x8c\x8c\x07\xa7\xa0\x01\x0c'
b'\xa7\xa2\x01\x0c\x15\x04\xf6MEAS\x87\x8c\x8c\x07\xa7\xa1\x01\x0c'
b'\x8c\x07\xa7\xa3\x01\x0c\x15\x04\xf6MEAS\x87\x8c\x8c\x07'
b'\x88\x8c\x8c\x07\xa7\xa4\x01\x0c\x15\x04\xf6MEAS\x88\x8c'
b'MEAS\x8b\x8c\x8c\x07\xa7\xa5\x01\x0c\x15\x04\xf6MEAS'
b'\x04\xf6MEAS\x8b\x8c\x8c\x07\xa7\xa6\x01\x0c\x15\x04\xf6'
b'\x15\x04\xf6MEAS\x8a\x8c\x8c\x07\xa7\xa7\x01\x0c\x15'
b'\x15\x04\xf6MEAS\x88\x8c\x8c\x07\xa7\xa8\x01\x0c'
b'\x01\x0c\x15\x04\xf6MEAS\x88\x8c\x8c\x07\xa7\xa9\x01\x0c'
b'\x8c\x07\xa7\xab\x01\x0c\x15\x04\xf6MEAS\x8b\x8c\x8c\x07\xa7\xaa'
b'\x8c\x8c\x07\xa7\xac\x01\x0c\x15\x04\xf6MEAS\x8c\x8c'
b'AS\x89\x8c\x8c\x07\xa7\xad\x01\x0c\x15\x04\xf6MEAS\x8a'
b'MEAS\x88\x8c\x8c\x07\xa7\xae\x01\x0c\x15\x04\xf6ME'
b'\x15\x04\xf6MEAS\x87\x8c\x8c\x07\xa7\xaf\x01\x0c\x15\x04\xf6'
b'\x15\x04\xf6MEAS\x8a\x8c\x8c\x07\xa7\xb0\x01\x0c'
b'\x0c\x15\x04\xf6MEAS\x8a\x8c\x8c\x07\xa7\xb1\x01\x0c'
b'\x07\xa7\xb3\x01\x0c\x15\x04\xf6MEAS\x89\x8c\x8c\x07\xa7\xb2\x01'
b'\x8c\x8c\x07\xa7\xb4\x01\x0c\x15\x04\xf6MEAS\x89\x8c\x8c'
b'\x85\x8c\x8c\x07\xa7\xb5\x01\x0c\x15\x04\xf6MEAS\x84'
b'MEAS\x87\x8c\x8c\x07\xa7\xb6\x01\x0c\x15\x04\xf6MEAS'
b'\x04\xf6MEAS\x8b\x8c\x8c\x07\xa7\xb7\x01\x0c\x15\x04\xf6'
b'\x15\x04\xf6MEAS\x8b\x8c\x8c\x07\xa7\xb8\x01\x0c\x15'
b'\x15\x04\xf6MEAS\x8a\x8c\x8c\x07\xa7\xb9\x01\x0c'
b'\xa7\xbb\x01\x0c\x15\x04\xf6MEAS\x87\x8c\x8c\x07\xa7\xba\x01\x0c'
  1. try to unpack the preamble !?

How do I read information like Article number, Serial number, Channel, Status, Measuring Value between the preamble?

The payload size seems to be fixed here for 22 Bytes (via Wireshark)

Wireshark Picture

Shan-Desai
  • 3,101
  • 3
  • 46
  • 89

2 Answers2

1

Parsing the reversed buffer is just weird; please use struct's support for endianess. Using big-endian '!' in a little-endian context is also odd.

The first four bytes are a text constant. Ok, fine perhaps you'll need to reverse those. But just those, please.

After that, use struct.unpack to parse out 'IIQI'. So far, that was kind of working OK with your approach, since all fields consume 4 bytes or a pair of 4 bytes. But finding frame M's length is the fly in the ointment since it is just 2 bytes, so parse it with 'H', giving you a combined 'IIQIH'. After that, you'll need to advance by only that many bytes, and then expect another 'MEAS' text constant once you've exhausted that set of measurements.

J_H
  • 17,926
  • 4
  • 24
  • 44
0

I managed to avoid TelnetLib altogether and created a tcp client using python3. I had the payload size already from my wireshark dump (22 Bytes) hence I keep receiving 22 bytes of Information. Apparently the module sends two distinct 22 Bytes payload

  1. First (frame) payload has the preamble, serial, article, channel information
  2. Second (frame) payload has the information like bytes per frame, measuring value counter, measuring value Channel 1, measuring value Channel 2, measuring value Channel 3

The information is in int32 and thus needs a formula to be converted to real readings (mentioned in the instruction manual)

(as mentioned by @J_H the unpacking was as He mentioned in his answer with small changes)

Code

import socket
import time
import struct

DRANGEMIN = 3261
DRANGEMAX = 15853
MEASRANGE = 50
OFFSET = 35

# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server_address = ('169.254.168.150', 10001)
print('connecting to %s port %s' % server_address)
sock.connect(server_address)


def value_mm(raw_val):

    return (((raw_val - DRANGEMIN) * MEASRANGE) / (DRANGEMAX - DRANGEMIN) + OFFSET)


if __name__ == '__main__':
    while True:
        Laser_Value = 0
        data = sock.recv(22)
        preamble, article, serial, x1, x2 = struct.unpack('<4sIIQH', data)
        if not preamble == b'SAEM':
            status, bpf, mValCounter, CH1, CH2, CH3 = struct.unpack('<hIIIII',data)
            #print(CH1, CH2, CH3)
            Laser_Value = CH3
            print(str(value_mm(Laser_Value)) + " mm")

        #print('RAW: ' + str(len(data)))
        print('\n')
        #time.sleep(0.1)

Sure enough, this provides me the information that is needed and I compared the information via the propreitary software which the company provides.

Shan-Desai
  • 3,101
  • 3
  • 46
  • 89