Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use struct in python to deserialize a byte array coming from serial

I have a class with all sorts of data in it, like:

class UARTMessage:
    Identification1 = int(0) #byte 0
    Timestamp1 = int(0) #bytes [1:5]
    Voltage1 = int(0) #bytes [6:7]
    Current1 = int(0) #bytes [8:9]
    Signal1= int(0) #bytes [10:11]
    Identification2 = int(0) #byte 12
    Timestamp2 = int(0) #bytes [13:17]
    Voltage2 = int(0) #bytes [18:19]
    Current2 = int(0) #bytes [20:21]
    Signal = int(0) #bytes [22:23]
    Identification3 = int(0) #byte 24   

The data to fill this structure up will come from a serial. I need to deserialize the data coming from the serial in the shape of this structure. I am reading from serial 40 bytes data chunks and I need to split itit. I tried pickle library but it seems that it's not fitted exactly for deserializing this type of data. I found struct but I cannot understand how to use it proprely in this case.
As the comments in the struct, I need to desearilize the chunks of data like: first byte is Identificator, bytes from 1 to 5 included is the timestamp and so on....
DO you have any ideea how can I achieve this?
Thanks

like image 985
Lucian Avatar asked Mar 11 '23 11:03

Lucian


1 Answers

First of all, we need to declare format of incoming bytes according to this list: https://docs.python.org/3/library/struct.html?highlight=struct#format-characters.

import struct
import sys


class UARTMessage:

    fmt = '@B5shhhB5shhhB'

    def __init__(self, data_bytes):
        fields = struct.unpack(self.fmt, data_bytes)
        (self.Identification1,
         self.Timestamp1,
         self.Voltage1,
         self.Current1,
         self.Signal1,
         self.Identification2,
         self.Timestamp2,
         self.Voltage2,
         self.Current2,
         self.Signal2,
         self.Identification3) = fields
        self.Timestamp1 = int.from_bytes(self.Timestamp1, sys.byteorder)
        self.Timestamp2 = int.from_bytes(self.Timestamp2, sys.byteorder)
        self.Timestamp3 = int.from_bytes(self.Timestamp3, sys.byteorder)

First character of the fmt is byte order. @ is python default (usually little endian), if you need to use network big-endian put !. Each subsequent character represents a data type which comes from the bytes stream.

Next, in the initializer, I unpack bytes according to the recipe in fmt into a fields tuple. Next, I assign the values of the tuple to object attributes. Timestamp has unusual length of 5 bytes, so it requires special treatment. It is fetched as 5-bytes string (5s in fmt) and converted to int using int.from_bytes function with system default bytes order (if you need a different bytes order enter 'big' or 'little' as a second argument).

When you want to create your structure, pass the sequence of bytes to the constructor.

like image 173
warownia1 Avatar answered Apr 20 '23 02:04

warownia1