Search code examples
pythonstructpyserialunpack

How to reconstruct the structure in python from pyserialtransfer's list of numbers


I am trying to communicate bi-directionally between my laptop & my arduino via passing a common struct back and forth, but am having trouble unpacking the bytes returned from the Arduino. Using pySerialTransfer, it seems rxBuff returns a list of numbers that I can't figure out how to convert into a byte-string for struct.unpack to render back into the numbers I started with.

At present, the Arduino code simply receives the data and spits it back, no transformations or action on the Arduino side. Here's my Arduino code:

#include "SerialTransfer.h"

SerialTransfer myTransfer;

struct POSITION {
  long id;
  float azimuth;
  float altitude;
} position;


void setup()
{
  Serial.begin(9600);
  myTransfer.begin(Serial);
}

void loop()
{
  if(myTransfer.available())
  {

    myTransfer.rxObj(position, sizeof(position));

    myTransfer.txObj(position, sizeof(position));
    myTransfer.sendData(sizeof(position));

  }
  else if(myTransfer.status < 0)
  {
    Serial.print("ERROR: ");

    if(myTransfer.status == -1)
      Serial.println(F("CRC_ERROR"));
    else if(myTransfer.status == -2)
      Serial.println(F("PAYLOAD_ERROR"));
    else if(myTransfer.status == -3)
      Serial.println(F("STOP_BYTE_ERROR"));
  }
}

And my python code:

import time
import struct
from pySerialTransfer import pySerialTransfer


def StuffObject(txfer_obj, val, format_string, object_byte_size, start_pos=0):
  """Insert an object into pySerialtxfer TX buffer starting at the specified index.

  Args:
    txfer_obj: txfer - Transfer class instance to communicate over serial
    val: value to be inserted into TX buffer
    format_string: string used with struct.pack to pack the val
    object_byte_size: integer number of bytes of the object to pack
    start_pos: index of the last byte of the float in the TX buffer + 1

  Returns:
    start_pos for next object
  """
  val_bytes = struct.pack(format_string, *val)
  for index in range(object_byte_size):
    txfer_obj.txBuff[index + start_pos] = val_bytes[index]
  return object_byte_size + start_pos


if __name__ == '__main__':
  try:
    link = pySerialTransfer.SerialTransfer('/dev/cu.usbmodem14201', baud=9600)

    link.open()
    time.sleep(2) # allow some time for the Arduino to completely reset
    base = time.time()

    while True:
      time.sleep(0.2)

      sent = (4, 1.2, 2.5)
      format_string = 'iff'
      format_size = 4+4+4
      StuffObject(link, sent, format_string, format_size, start_pos=0)
      link.send(format_size)

      start_time = time.time()
      elapsed_time = 0
      while not link.available() and elapsed_time < 2:
        if link.status < 0:
          print('ERROR: {}'.format(link.status))
        else:
          print('.', end='')
        elapsed_time = time.time()-start_time
      print()

      response =  link.rxBuff[:link.bytesRead]
      print(response)
      response = struct.unpack(format_string, response)

      print('SENT: %s' % str(sent))
      print('RCVD: %s' % str(response))
      print(' ')

  except KeyboardInterrupt:
    link.close()

When I run the python code, I get the following error:

Traceback (most recent call last):
  File "double_float.py", line 54, in <module>
    response = struct.unpack(format_string, response)
TypeError: a bytes-like object is required, not 'list'

But just before the error, I print the response: [4, 0, 0, 0, 154, 153, 153, 63, 0, 0, 32, 64].

How do I convert that list into something that struct.unpack can use? Or how do I unpack that numeric list directly? Thanks so much!


Solution

  • Is this what you are looking for:

    import struct
    data = [4, 0, 0, 0, 154, 153, 153, 63, 0, 0, 32, 64]
    binary_str = bytearray(data)
    result = struct.unpack("<lff", binary_str)
    print(result)
    
    

    Output

    (4, 1.2000000476837158, 2.5)