How to wait for a large number packet erminator pyserial - python

I am debugging packets sent from a device with pyserial. The packets have a specific terminator pattern to differentiate between packets of data. The pattern is a large number (0x3423fe67). I am able to receive bytes and concatenate them as they come in with the code below.
ser = serial.Serial(
ports="/dev/ttyUSB0",
baudrate=115200,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS
)
y = b''
while True:
bytes = ser.inWaiting()
if bytes > 0:
x = ser.read(bytes)
y += binascii.hexify(x)
print(y)
I can see the pattern I'm looking for but how do I grab the packet and terminator once I see the terminator. I tried indexing the bytes object but that returns strange values. Everything received before the terminator is part of the packet.

Here is code that accumulates packets in a list. The code can be used as a guide.
The serialData is data that would be received over the serial port as an example (i.e. ser.read() which would replace the for piece in x: loop). Note that an arbitrary number of bytes can be received, and the test variable n is only used to test the algorithm at arbitrary incoming received character lengths.
The term variable needed hexlify to match the hexlify on the incoming data.
import binascii
import re
term = binascii.hexlify(b'\x34\x23\xfe\x67')
serialData = b'packet0\x34\x23\xfe\x67packet1\x34\x23\xfe\x67'
n = 5
x = [serialData[i:i+n] for i in range(0, len(serialData), n)]
y = b''
packets = []
for piece in x:
y += binascii.hexlify(piece)
for i in re.finditer(term, y):
packets.append(y[:i.start()])
y = y[i.end():]
print('packets = ', packets)

Related

Problem with the delimiter of data packet

The following example reads data from the UART. In my case, the delimiter where packet starts with b '\x02' everything works, but the problem appears when b '\x02' appears in the packet and it is not the beginning of the packet because uart data often comes in one string. I will add that the packet that needs to be read always starts with b '\x02\x84' the only question is how to check if the first byte b '\x02’ followed by b '\x84' and if so then do a split.
Sample package consisting of several and with the problem:
\x02\x84"\x00\x19\x03\x00l\xe0\x02D\x00\x02\x84"\x00\x19\x03\x00l
I want to get two as handle_packet:
b'\x84"\x00\x19\x03\x00l\xe0\x02D\x00'
b'\x84"\x00\x19\x03\x00l'
rx_buff = bytes()
while True:
recv = reader.read(-1)
if not recv:
continue
rx_buff += recv
packets = rx_buff.split(bytes(b'\x02'))
for packet in packets:
if not packet:
continue
msg = mod.handle_packet(packet)
if (msg):
get_response(msg)
rx_buff = (bytes() if mod.handle_packet(packet) else packet)
I spent a little more time reading. If you do it this way, your problem is going to be the first packet you split, because, I am assuming it doesn't follow the splitting pattern b'\x02\x84". If that is the case, you can just take the first of split_bytes and remove the first byte.
my_bytes = b'\x02\x84"\x00\x19\x03\x00l\xe0\x02D\x00\x02\x84"\x00\x19\x03\x00l'
split_bytes = my_bytes.split(b'\x02\x84"')
packets = []
for e in split_bytes:
if e != b'':
packets.append(b'\x84"' + e)
print(packets)
Yields:
[b'\x84"\x00\x19\x03\x00l\xe0\x02D\x00', b'\x84"\x00\x19\x03\x00l']
Your desired packets:
[b'\x84"\x00\x19\x03\x00l\xe0\x02D\x00', b'\x84"\x00\x19\x03\x00l']
If I am missing something, please point it out.

on-the-fly parsing of binary serial data in python

I'm new to using Python3 for data acquisition. I'm trying to find a way to parse binary data from a serial port on Linux.
import serial
ser = serial.Serial(
port='/dev/ttyS0',
baudrate = 9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1)
counter = 0
while 1:
x = ser.read(31)
print (x)
This gives me a string which I'm not sure about the format of:
x='\x00\x00\x91\x00\x02\x88BM\x00\x1c\x00\x00\x00\x01\x00\x01\x00\x00\x00\x01\x00\x01\x00\xe1\x00K\x00\x1a\x00\x02\x00\x00'
using
x.encode('hex')
gives a string of hex values
x='000091000288**424d**001c00000001000100000001000100e1004b001a00020000'
where 0x42 is the end of message and 0x4d is start of message.
I can convert it into a base 10 list using
y = map(ord,x)
print(y)
Then I have a way to re-order the message using the indexes but surely there is a neater way? How do I create a list which starts at 0x4d to parse with?
If you are using python3, this is likely already bytes:
x='\x00\x00\x91\x00\x02\x88BM\x00\x1c\x00\x00\x00\x01\x00\x01\x00\x00\x00\x01\x00\x01\x00\xe1\x00K\x00\x1a\x00\x02\x00\x00'
It likely looks this way because Python printed it for you, and all of the non-ascii characters are shown in hex. Your start of message is in 0x42, 0x4d which is BM in ascii and can be seen in the data above between 0x88 and 0x00 as \x88BM\x00.
I would suggest just iterating over the byte array in x to do your parsing. The encoding and mapping should not be needed.
for b in x:
if b == 0x4d:
found_byte1 = True
... # etc

how to send an hexadecimal from python to the UART?

I need to understand how to send a message in hexadecimal format from UART for example:
msg='99' +'70b4c55ad8cdb7806a7b043069c4e0d8'
'99'is to distinct the start of message and the rest is the data.
As
import serial
import time
#serial port
ser = serial.Serial(
port='COM4',\
baudrate=230400,\
parity=serial.PARITY_NONE,\
stopbits=serial.STOPBITS_ONE,\
bytesize=serial.EIGHTBITS,\
timeout=0)
msg=b'\x99\x70\xb4\xc5\x5a\xd8\xcd\xb7\x80\x6a\x7b\x04\x30\x69\xc4\xe0\xd8'
ser.write(msg)
time.sleep(0.4)
while True:
print(ser.read(30))
ser.close() # close ports
But i don't unerstand th error:
msg= b'\x99\x70\xb4\xc5\x5a\xd8\xcd\xb7\x80\x6a\x7b\x04\x30\x69\xc4\xe0\xd8'
^
IndentationError: unexpected indent
I need then to compare by using c langage, to compare the first received byte 99 with 0x99.
int lenght = dev_uart_ptr->uart_read((void*)buffer,34 );
if (lenght<34)
{
if buffer [0]='0x99'
}
Thanks in advance.
It seems like you are loading to buf but using buffer in your c code.
You are reading 34 bytes to the buffer, then checking if the length is less than 34. Shouldn't work well. And judging by your example, it should be 17.
Encode will not convert msg to hex string. It will treat every character as a char, i.e. '0' is 48, '1' is 49 etc
Create bytes object using
msg = b'\x99\x70\xb4\xc5\x5a\xd8\xcd\xb7\x80\x6a\x7b\x04\x30\x69\xc4\xe0\xd8'
ser.write(msg)

Error reading serial data transmission

Using a raspberry pi I am having issues reading data that is being transmitted serially. My code was working when I tested it on a different machine but isn't working now.
The baud rate is 9600 w/ no parity, 8 data bits, 1 stop bit and I want the program to handle a variable length of characters (Sometimes 100K+). The reading portion of the code is as follows:
ser = serial.Serial('/dev/ttyAMA0', 9600, parity = serial.PARITY_NONE, timeout=1)
While True:
data = ser.read(1)
bytesToRead = ser.inWaiting()
if bytesToRead:
data = data + ser.read(bytesToRead)
encodedData = data.encode('hex')
With this code, Shouldn't I be able to read all the characters as Hex as long as the baud/parity/etc match up with the transmitting system?
While True:
data = ser.read(1)
This waits for a single character to be read (times out after 1s due to the timeout specified in the Serial constructor) and stores it in data
bytesToRead = ser.inWaiting()
if bytesToRead:
data = data + ser.read(bytesToRead)
encodedData = data.encode('hex')
Now instantly check for any other characters in buffer - this will usually be zero. Due to the fact that you're running at 9600 baud, Python will usually see the characters come in one at a time. So your if bytesToRead statement will mostly be false as each incoming character is consumed by the above ser.read(1).
If you just want to process each character individually, you can do:
While True:
data = ser.read(1)
if data:
encodedData = data.encode('hex')
Or if you want to keep adding it to a buffer, use something like:
data = ''
While True:
bytesToRead = ser.inWaiting()
if bytesToRead:
data += ser.read(bytesToRead)
encodedData = data.encode('hex')
if encodedData.startswith('1234deadb33f`):
data = data[6:] # strip 6 chars from start of data

leading zero / zero padding doesn't work properly

I am getting data from arduino via Serial (pySerial). It is then split in to 3 value by a delimiter(:). I am using slice ([0:5]) to remove 2 extra characters (I think it the new line).
So far so good. But then rjust or zfill or even format() simply does not work properly. If I use zfill(5) for example and the value output by arduino is 8.00 there is no change and I get 3 characters. If I use zfill(7) then it works and I get 7 total characters.
What is going on?
Arduino is outputting this every ~1 seconds: 22.00:36.00:58.00
import time
import serial
connected = False
port = '/dev/ttyACM0'
baud = 9600
ser = serial.Serial(port, baud)
# loop until arduino is ready
while not connected:
serin = ser.read()
connected = True
# read aruino output
while ser.readline():
readings = ser.readline().split(':', 2)
lum = readings[2][0:5].rjust(5, '0')
#lum = readings[2][0:5].zfill(5) same result as with rjust
print(lum)
time.sleep(1)
I am using Arduino Uno connected to Raspberry Pi running Rasberian OS with IDLE as python editor.
readline() contain newline. Strip newline.
readings = ser.readline().rstrip().split(':', 2)
>>> '0.00\n'.rjust(5, '0')
'0.00\n'
>>> '0.00\n'.rstrip().rjust(5, '0')
'00.00'

Categories

Resources