Git Product home page Git Product logo

pyserialtransfer's Introduction

pySerialTransfer

GitHub version PyPI version

Python package to transfer data in a fast, reliable, and packetized form.

If using this package to communicate with Arduinos, see https://github.com/PowerBroker2/SerialTransfer for the corresponding and compatible library (also available through the Arduino IDE's Libraries Manager).

To Install

pip install pySerialTransfer

Example Python Script

import time
from pySerialTransfer import pySerialTransfer as txfer


if __name__ == '__main__':
    try:
        link = txfer.SerialTransfer('COM17')
        
        link.open()
        time.sleep(2) # allow some time for the Arduino to completely reset
        
        while True:
            send_size = 0
            
            ###################################################################
            # Send a list
            ###################################################################
            list_ = [1, 3]
            list_size = link.tx_obj(list_)
            send_size += list_size
            
            ###################################################################
            # Send a string
            ###################################################################
            str_ = 'hello'
            str_size = link.tx_obj(str_, send_size) - send_size
            send_size += str_size
            
            ###################################################################
            # Send a float
            ###################################################################
            float_ = 5.234
            float_size = link.tx_obj(float_, send_size) - send_size
            send_size += float_size
            
            ###################################################################
            # Transmit all the data to send in a single packet
            ###################################################################
            link.send(send_size)
            
            ###################################################################
            # Wait for a response and report any errors while receiving packets
            ###################################################################
            while not link.available():
                # A negative value for status indicates an error
                if link.status.value < 0:
                    if link.status == txfer.Status.CRC_ERROR:
                        print('ERROR: CRC_ERROR')
                    elif link.status == txfer.Status.PAYLOAD_ERROR:
                        print('ERROR: PAYLOAD_ERROR')
                    elif link.status == txfer.Status.STOP_BYTE_ERROR:
                        print('ERROR: STOP_BYTE_ERROR')
                    else:
                        print('ERROR: {}'.format(link.status.name))
            
            ###################################################################
            # Parse response list
            ###################################################################
            rec_list_  = link.rx_obj(obj_type=type(list_),
                                     obj_byte_size=list_size,
                                     list_format='i')
            
            ###################################################################
            # Parse response string
            ###################################################################
            rec_str_   = link.rx_obj(obj_type=type(str_),
                                     obj_byte_size=str_size,
                                     start_pos=list_size)
            
            ###################################################################
            # Parse response float
            ###################################################################
            rec_float_ = link.rx_obj(obj_type=type(float_),
                                     obj_byte_size=float_size,
                                     start_pos=(list_size + str_size))
            
            ###################################################################
            # Display the received data
            ###################################################################
            print('SENT: {} {} {}'.format(list_, str_, float_))
            print('RCVD: {} {} {}'.format(rec_list_, rec_str_, rec_float_))
            print(' ')
    
    except KeyboardInterrupt:
        try:
            link.close()
        except:
            pass
    
    except:
        import traceback
        traceback.print_exc()
        
        try:
            link.close()
        except:
            pass

Example Arduino Sketch

#include "SerialTransfer.h"


SerialTransfer myTransfer;


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


void loop()
{
  if(myTransfer.available())
  {
    // send all received data back to Python
    for(uint16_t i=0; i < myTransfer.bytesRead; i++)
      myTransfer.packet.txBuff[i] = myTransfer.packet.rxBuff[i];
    
    myTransfer.sendData(myTransfer.bytesRead);
  }
}

Example Python Script with Callback Functionality

Note that you can specify many callbacks, but only one per packet ID

import time
from pySerialTransfer import pySerialTransfer as txfer


def hi():
    '''
    Callback function that will automatically be called by link.tick() whenever
    a packet with ID of 0 is successfully parsed.
    '''
    
    print("hi")
    
'''
list of callback functions to be called during tick. The index of the function
reference within this list must correspond to the packet ID. For instance, if
you want to call the function hi() when you parse a packet with an ID of 0, you
would write the callback list with "hi" being in the 0th place of the list:
'''
callback_list = [ hi ]


if __name__ == '__main__':
    try:
        link = txfer.SerialTransfer('COM17')
        
        link.set_callbacks(callback_list)
        link.open()
        time.sleep(2) # allow some time for the Arduino to completely reset
        
        while True:
            link.tick()
    
    except KeyboardInterrupt:
        link.close()
    
    except:
        import traceback
        traceback.print_exc()
        
        link.close()

pyserialtransfer's People

Contributors

alpyen avatar emendir avatar inmerso avatar kyleb-imp avatar malyar0v avatar martincizek avatar powerbroker2 avatar robberwick avatar umaplehurst avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pyserialtransfer's Issues

Problem transmitting doubles from Arduino to Python

Hi great library, it is very useful, thank you.
I am having trouble sending some data from Arduino to Python. I have it working nicely if my data type is a single byte but if I change to an int or a double the python library isn't behaving as I would expect. Here is an example.
In Arduino I transmit two doubles each with a size of 8 bytes

double var1 = 1;
double var2 = 2;
baseStationSerial.txBuff[0]= var1;
baseStationSerial.txBuff[1]= var2;
baseStationSerial.sendData(16);

In python I read the received bytes into an array and print the array.

response = bytearray()
for index in range(model.link.bytesRead):
response.append(model.link.rxBuff[index])
print(response)

This is what I get in the array
bytearray(b'\x01\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')

This is what I would expect
bytearray(b'\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00')

Any ideas?

Thanks

Multiple Packets for One Array

Hey PowerBroker!

Things are moving along pretty smoothly with our project! I can send trial sets of 45 or smaller no problem and the lab is pretty happy with it. A new challenge has appeared for me, though. The lab would like it to be possible to double the amount of trials to 90 total as a definite ceiling.

I had thought that I was doing this correctly with what you have taught me so far but, after some testing, I've come to realize that while a second packet is getting sent by Python, my Arduino code doesn't seem to collecting the second packet's content. Here's an example of what I mean:

(bruker_control) C:\Users\jdelahanty>python Documents\gitrepos\headfix_control\bruker_control\bruker_control.py

First Half of Trials Sent
[1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0]
First Half of Trials Received
[1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0]
First Half Trial Array Transfer Successful!
Second Half of Trials Sent
[0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1]
Second Half of Trials Received
[1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0] # <--- Arduino sends first packet's contents!
Second Half Trial Array Transfer Failure!
Exiting...
Traceback (most recent call last):
  File "Documents\gitrepos\headfix_control\bruker_control\bruker_control.py", line 522, in serial_transfer_trialArray
    sys.exit()
SystemExit
Experiment Over!
Exiting...

Any advice? I had thought it would collect the packet independently but I'm clearly misunderstanding how multiple packets for the same object work. Is this something I can do even or should I create a second array in the Arduino code to store the second half of trials?

Here's some full Python and Arduino code:
Python

#### Trial Array Generation ####
# Import scipy for statistical distributions
import scipy
# Import scipy.stats truncated normal distribution for ITI Array
from scipy.stats import truncnorm
# Import numpy for trial array generation/manipulation and Harvesters
import numpy as np
# Import numpy default_rng
from numpy.random import default_rng

# -----------------------------------------------------------------------------
# Functions
# -----------------------------------------------------------------------------
#### Trial Generation ####
# Random Trials Array Generation
def gen_trial_array(totalNumberOfTrials):
    # Always initialize trial array with 3 reward trials
    trialArray = [1,1,1]
    # Define number of samples needed from generator
    num_samples = totalNumberOfTrials - len(trialArray)
    # Define probability that the animal will receive sucrose 60% of the time
    sucrose_prob = 0.5
    # Initialize random number generator with default_rng
    rng = np.random.default_rng(2)
    # Generate a random trial array with Generator.binomial
    # Use n=1 to pull one sample at a time, p=.6 as probability of sucrose
    # Use num_samples to fill out accurate number of trials
    # Use .tolist() to convert random_trials from np.array to list
    random_trials = rng.binomial(
    n=1, p=sucrose_prob, size=num_samples
    ).tolist()
    # Append the two arrays together
    for i in random_trials:
        trialArray.append(i)

    if len(trialArray) > 45:
        split_array = np.array_split(trialArray, 2)
        first_trialArray = split_array[0].tolist()
        second_trialArray = split_array[1].tolist()
    else:
        first_trialArray = None
        second_trialArray = None

    ## TODO: Write out the trial array into JSON as part of experiment config
    # Return trialArray or halves of trial arrays (if needed)
    return trialArray, first_trialArray, second_trialArray

#### Serial Transfer ####
# Import pySerialTransfer for serial comms with Arduino
from pySerialTransfer import pySerialTransfer as txfer


# Trial Array Transfer
def serial_transfer_trialArray(trialArray, first_trialArray, second_trialArray):
    # Check if two packets are necessary. If 'first_trialArray' is None,
    # the message is small enough to fit in one packet. There's no need to
    # check the second_trialArray as it too will be None by design in this case.
    if first_trialArray == None:
        try:
            # Initialize COM Port for Serial Transfer
            link = txfer.SerialTransfer('COM12', 115200, debug=True)

            # Send the trial array
            # Initialize trialArray_size of 0
            trialArray_size = 0
            # Stuff packet with size of trialArray
            trialArray_size = link.tx_obj(trialArray)
            # Open communication link
            link.open()
            # Send array
            link.send(trialArray_size, packet_id=0)

            print(trialArray)

            while not link.available():
                pass

            # Receive trial array:
            rxtrialArray = link.rx_obj(obj_type=type(trialArray),
            obj_byte_size=trialArray_size, list_format='i')

            print(rxtrialArray)

            if trialArray == rxtrialArray:
                print("Trial Array transfer successful!")

            else:
                link.close()
                print("Trial Array error! Exiting...")
                sys.exit()

            # Close the communication link
            link.close()

        except KeyboardInterrupt:
            try:
                link.close()
            except:
                pass

        except:
            import traceback
            traceback.print_exc()

            try:
                link.close()
            except:
                pass

    elif first_trialArray != None:
        try:
            # Initialize COM Port for Serial Transfer
            link = txfer.SerialTransfer('COM12', 115200, debug=True)

            # Send the first half of trials with packet_id = 0
            first_trialArray_size = 0
            first_trialArray_size = link.tx_obj(first_trialArray)
            link.open()
            link.send(first_trialArray_size, packet_id=0)

            print("First Half of Trials Sent")
            print(first_trialArray)

            while not link.available():
                pass

            # Receive the first half of trials from Arduino
            rxfirst_trialArray = link.rx_obj(obj_type=type(first_trialArray),
            obj_byte_size=first_trialArray_size, list_format='i')

            print("First Half of Trials Received")
            print(rxfirst_trialArray)

            # Confirm packet was sent correctly
            if first_trialArray == rxfirst_trialArray:
                print("First Half Trial Array Transfer Successful!")
            else:
                link.close()
                print("First Half Trial Array Transfer Failure!")
                print("Exiting...")
                sys.exit()

            # Send second half of trials with packet_id = 0
            second_trialArray_size = 0
            second_trialArray_size = link.tx_obj(second_trialArray)
            link.send(second_trialArray_size, packet_id=1)

            print("Second Half of Trials Sent")
            print(second_trialArray)

            # Receive second half of trials from Arduino
            rxsecond_trialArray = link.rx_obj(obj_type=type(second_trialArray),
            obj_byte_size=second_trialArray_size, list_format='i')

            print("Second Half of Trials Received")
            print(rxsecond_trialArray)

            if second_trialArray == rxsecond_trialArray:
                print("Second Half Trial Array Transfer Successful!")
            else:
                link.close()
                print("Second Half Trial Array Transfer Failure!")
                print("Exiting...")
                sys.exit()

            link.close()

        except KeyboardInterrupt:
            try:
                link.close()
            except:
                pass

        except:
            import traceback
            traceback.print_exc()

            try:
                link.close()
            except:
                pass
    else:
        print("Something is wrong...")
        print("Exiting...")
        sys.exit()

Arduino

// Use package SerialTransfer.h from PowerBroker2 https://github.com/PowerBroker2/SerialTransfer
#include "SerialTransfer.h"

// Rename SerialTransfer to myTransfer
SerialTransfer myTransfer;

const int MAX_NUM_TRIALS = 46; // maximum number of trials possible

int32_t trialArray[MAX_NUM_TRIALS]; // create trial array

boolean acquireTrials = true;

void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);

  myTransfer.begin(Serial1, true);
}


void loop(){
  trials_rx();
}

int trials_rx() {
  if (acquireTrials) {
    if (myTransfer.available())
    {
      myTransfer.rxObj(trialArray);
      Serial.println("Received Trial Array");

      myTransfer.sendDatum(trialArray);
      Serial.println("Sent Trial Array");

      acquireTrials = false;
    }
  }
}

How to receive list of integers from Python to Arduino?

I'm trying to send a list of int and boolean using txObj() in python and receive in Arduino using rxObj().

Here's my python test code snippet:

from pySerialTransfer import pySerialTransfer as txfer
link=txfer.SerialTransfer('COM6',9600)
link.open()
values=[128,128,128,128,False,False,False,False]
list_=link.tx_obj(values)
link.send(list_)

And here's my arduino code snippet:

struct data_package
{
  uint32_t go_x; //Right and left control
  uint32_t go_y ; //Forward and backward control
  uint32_t turn_x ; //Camera Control right and left
  uint32_t turn_y ; //Camera Control up and down
  bool square;
  bool x;
  bool o;
  bool triangle;   
};
data_package data;
void checkEvent()
{
    if(myTransfer.available())
  {
    myTransfer.rxObj(data, sizeof(data));
    lastReceiveTime = millis();

 
  }

}

It seems like the value I received is not consistent on the Arduino, perhaps because the integer bit size of Python is not the same as C in Arduino? How can I fix it? I am also using Arduino Uno with one port, so I cannot really debug it for now.

CRC ERROR keep occuring

Hello powrebroker

First of all really big thanks to your awesome library.

As I'm using your library for transmitting simulator data (python) to arduino(mega) CRC Error keep occur and pySerialTransfer Thread keep waiting for timeout and send CRC Error.
I lower the baud rate to 9600 and provide some delay to work properly

here Is my code
6

5

2

AttributeError occurs

Hello powerbroker

Nowadays, I'm making an AT command protocol using pyserialTransfer library

However, as I use link.available function, error occurs

Here is the error message and don't know what is the problem of this message

Traceback (most recent call last):
File "/home/jaycho/carla-simulator/PythonAPI/examples/serial_transmitter.py", line 142, in
st.transmit_data()
File "/home/jaycho/carla-simulator/PythonAPI/examples/serial_transmitter.py", line 103, in transmit_data
if self.link.available():
File "/home/jaycho/.local/lib/python2.7/site-packages/pySerialTransfer/pySerialTransfer.py", line 511, in available
recChar = int.from_bytes(self.connection.read(),
AttributeError: type object 'int' has no attribute 'from_bytes'

here is my python code

def transmit_data(self):
        if self.link.available():
            query_cmd = ''
            recSize = 0
            query_cmd = self.link.rx_obj(obj_type=str, start_pos=recSize,obj_byte_size=10)
            if query_cmd == 'at+qd':
                sendsize = 0

                sendsize = self.link.tx_obj(self.vehicle_speed, start_pos=sendsize)
                sendsize = self.link.tx_obj(self.vehicle_throttle, start_pos=sendsize)
                sendsize = self.link.tx_obj(self.vehicle_rpm, start_pos=sendsize)
                sendsize = self.link.tx_obj(self.vehicle_accelX, start_pos=sendsize)
                sendsize = self.link.tx_obj(self.vehicle_accelY, start_pos=sendsize)
                sendsize = self.link.tx_obj(self.vehicle_lat, start_pos=sendsize)
                sendsize = self.link.tx_obj(self.vehicle_lon, start_pos=sendsize)

                self.link.send(sendsize)
                time.sleep(0.01)
            else:
                print("Waiting for Data...")
#include "SerialTransfer.h"

SerialTransfer myTransfer;
char query_cmd[] = "at+qd";

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

void loop()
{
  // use this variable to keep track of how many
  // bytes we're stuffing in the transmit buffer
  uint16_t sendSize = 0;

  ///////////////////////////////////////// Stuff buffer with struct
  sendSize = myTransfer.txObj(query_cmd, sendSize);

  ///////////////////////////////////////// Send buffer
  myTransfer.sendData(sendSize);
  delay(500);
}

Error in example script

Hey there,
the given example script throws this error:

"D:\Programme\Portable Python 2.7.6.1\App\PyCharm\..\pythonw.exe" "D:/Python Skripte/Screen2Bulb/serialExample.py"
Traceback (most recent call last):
  File "D:/Python Skripte/Screen2Bulb/serialExample.py", line 7, in <module>
    link = txfer.SerialTransfer('COM17')
  File "D:\Programme\Portable Python 2.7.6.1\App\lib\site-packages\pySerialTransfer\pySerialTransfer.py", line 125, in __init__
    for p in serial_ports():
  File "D:\Programme\Portable Python 2.7.6.1\App\lib\site-packages\pySerialTransfer\pySerialTransfer.py", line 94, in serial_ports
    return [p.device for p in serial.tools.list_ports.comports(include_links=True)]
TypeError: comports() takes no arguments (1 given)

Process finished with exit code 0

I just downloaded the zip from github and copied this example.
Changing the COM-Port to a correct one doesn't change anything.

I don't think I made a mistake, did I?

Initialising SerialTransfer throws TypeError

Using the library under python 3i, when attempting to initialise an instance of the class, __init__ results in a TypeError and the following traceback:

Traceback (most recent call last):
  File "./duplex_struct.py", line 5, in <module>
    link = txfer.SerialTransfer('/dev/serial0', baud=57600)
  File "/home/pi/.virtualenvs/tauradigm/lib/python3.5/site-packages/pySerialTransfer/pySerialTransfer.py", line 98, in __init__
    self.txBuff = [' ' for i in (MAX_PACKET_SIZE - 1)]
TypeError: 'int' object is not iterable

Updating lines 98 & 99 to the following seems to work for me (assuming that it was the original intention of the code to create a MAX_PACKET_SIZE length array of space characters):

        self.txBuff = [' ' for i in range(MAX_PACKET_SIZE)]
        self.rxBuff = [' ' for i in range(MAX_PACKET_SIZE)]

Possible off by one bug.

I get the following error if data was previously sent with the maximum allowed size.

e.x. (Sending data from Arduino)
myTransfer.sendData(MAX_PACKET_SIZE);

Results in: (Receiving data on Raspberry Pi)
Traceback (most recent call last):
File "serialReadTest2.py", line 16, in
while not link.available():
File "/home/pi/.local/lib/python3.7/site-packages/pySerialTransfer/pySerialTransfer.py", line 537, in available
self.rxBuff[self.payIndex] = recChar
IndexError: list assignment index out of range

From the following code:
link = pySerialTransfer.SerialTransfer('serial0')
link.open()

while not link.available():

This issue does not occur if data is sent with a size one less than that of the MAX_PACKET_SIZE.

I'm not sure if this is a bug, or if I'm stupid.
Thanks

Declare dependencies

Your package has not properly declared dependency on the pyserial package. If that gets declared pip will install it.

How to transmit image from esp32-cam to python

Hello PowerBroker2!
Recently i want to made a project weight scale with esp32-cam taking the picture. the weight scale script is done by simple serial print and gui serial monitor with python. but had problem with transmitting the image because esp32cam (ai thinker) im using only have spare gpio pinout with adc channel not work when using the wifi board mentioned in here
Then i found this post, so in tx side i made if else separating the scale script and the image transmit with script copied from the post. then in rx side i make signal to stop reading the scale and send a char to esp32cam to start transmit the images
But based code in the post, how i receive that splitted data in python? did it also need struct to receive or there's another way to do that?

Issues sending list of integers

Hello PowerBroker2,
I am trying to use this library to control some LEDs. I can see that the arduino is getting something from the onboard LED, but I am not getting the correct values. Is there something I am missing in my code?
Python Code

class Hardware:
    def __init__(self):
        
        if comports:
            port_list=list(comports())
            for each in port_list:
                logger.debug(f"port at {each.name}")
                if "Arduino" in each.description:
                    logger.debug(f"arduino found on {each.name}")
                    self.type="arduino"
                    self.link=txfer.SerialTransfer(each.name)
    def open(self):
        self.link.open()
        time.sleep(2)
        self.is_open=True
    def close(self):
        self.link.close()
        self.is_open=False

    def send(self, cmd:Command):
        data=cmd.to_serial() #list of 5 ints
        size=0
        size=self.link.tx_obj(data[0], start_pos=size)
        size=self.link.tx_obj(data[1], start_pos=size)
        size=self.link.tx_obj(data[2], start_pos=size)
        size=self.link.tx_obj(data[3], start_pos=size)
        size=self.link.tx_obj(data[4], start_pos=size)
        logger.debug(size)
        logger.debug(data)
        
        self.link.send(size)

And the arduino:

#include <I2CTransfer.h>
#include <Packet.h>
#include <PacketCRC.h>
#include <SerialTransfer.h>
#include <SPITransfer.h>


#include <Adafruit_NeoPixel.h>


#define LED_PIN 6
#define LED_COUNT 60
SerialTransfer myTransfer;


struct STRUCT{
  int32_t red;
  int32_t green;
  int32_t blue;
  int32_t white;
  int32_t bright;
} command;


Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRBW + NEO_KHZ800);

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);
  myTransfer.begin(Serial);
  strip.begin();
  strip.show();
  strip.setBrightness(15);
}


void loop() {
  //format RGBWBrightness
  if(myTransfer.available())
  {
    uint16_t recSize=0;
    recSize=myTransfer.rxObj(command.red, recSize);
    recSize=myTransfer.rxObj(command.green, recSize);
    recSize=myTransfer.rxObj(command.blue, recSize);
    recSize=myTransfer.rxObj(command.white, recSize);
    recSize=myTransfer.rxObj(command.bright, recSize);
  }
  
  // put your main code here, to run repeatedly:
  for (int x=0; x<strip.numPixels(); x++){
    strip.setPixelColor(x, command.red, command.green, command.blue, command.white);
  }
  strip.setBrightness(command.bright);
  strip.show();

}

Thanks!

Getting crc errors as soon as I am not looping if myTransfer.available()

Hi, I'm trying to send a list of xbox controller values. I'm sure there could be a better option but I am not smart enough to figure that out. The list is currently 21 indexes long and I can send that to the arduino no problem.
But as soon as I try to do something outside of receiving serialtransfer packets, the code breaks. Could you help me a bit?

The python code.

from evdev import list_devices, InputDevice, categorize, ecodes, KeyEvent,event_factory
import time
from pySerialTransfer import pySerialTransfer as txfer

gamepad = InputDevice('/dev/input/event0')
analogValue = [0,0,0,0,0,0]
buttonValue = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
stickIndexes = [0,1,3,4]
buttonIndex = [304,305,307,308,310,311,314,315,316,317,318]
currentstate = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
oldstate = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
send_size = 0
list_size = 0

def map2(input,inmin,inmax,outmin,outmax):
	return int((input-inmin)*(outmax-outmin)/(inmax-inmin)+outmin)

def checkKeys():
	for x in gamepad.active_keys():
				for y in range(len(buttonIndex)):
					if x == buttonIndex[y]:
						buttonValue[y] = 1
	#print("checked keys")

def checkAnalog():
	for x in range(4):
		if gamepad.absinfo(stickIndexes[x]).value > 2000 or gamepad.absinfo(stickIndexes[x]).value < -2000:
			analogValue[x] = map2(gamepad.absinfo(stickIndexes[x]).value,-32767,32767,-1000,1000)
		else:
			analogValue[x] = 0

		analogValue[4] = gamepad.absinfo(2).value
		analogValue[5] = gamepad.absinfo(5).value

	if gamepad.absinfo(16).value != 0:
		if gamepad.absinfo(16).value == -1:
			buttonValue[11] = 1
		elif gamepad.absinfo(16).value == 1:
			buttonValue[12] = 1

	if gamepad.absinfo(17).value != 0:
		if gamepad.absinfo(17).value == -1:
			buttonValue[13] = 1
		elif gamepad.absinfo(17).value == 1:
			buttonValue[14] = 1
	#print("checked analog")



if __name__ == '__main__':
	try:
		link = txfer.SerialTransfer('/dev/serial0')
		
		link.open()
		time.sleep(2) # allow some time for the Arduino to completely reset
		
		while True:
			send_size = 0
			
			###################################################################
			# Send a list
			###################################################################


			checkKeys()
			checkAnalog()
			print(analogValue+buttonValue)
			

			list_ = analogValue + buttonValue
			list_size = link.tx_obj(list_)
			send_size += list_size
			buttonValue = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
			
			###################################################################
			# Transmit all the data to send in a single packet
			###################################################################
			link.send(send_size)
			
			###################################################################
			# Wait for a response and report any errors while receiving packets
			###################################################################
			
			while not link.available():
			#if False:
				if link.status < 0:
					if link.status == txfer.CRC_ERROR:
						print('ERROR: CRC_ERROR')
					elif link.status == txfer.PAYLOAD_ERROR:
						print('ERROR: PAYLOAD_ERROR')
					elif link.status == txfer.STOP_BYTE_ERROR:
						print('ERROR: STOP_BYTE_ERROR')
					else:
						print('ERROR: {}'.format(link.status))
			
			###################################################################
			# Parse response list
			###################################################################
			#rec_list_  = link.rx_obj(obj_type=type(list_),
										#obj_byte_size=list_size,
										#list_format='i')
			###################################################################
			# Display the received data
			###################################################################
			#print('SENT: {}'.format(list_))
			#print('RCVD: {}'.format(rec_list_))
			#print(' ')
			link.close()
    
	except KeyboardInterrupt:
		try:
			link.close()
		except:
			pass

	except:
		import traceback
		traceback.print_exc()
		
		try:
			link.close()
		except:
			pass

Arduino code

Incoming list variables

const int xboxInputAmount = 21; //How many xbox values there will be
int32_t inputArray[xboxInputAmount]; //This will contain every xbox controller outputs

const int xbLSTICKAX = 0;//Left stick X axis
const int xbLSTICKAY = 1;//Left stick Y axis
const int xbLBA = 4;//Left trigger axis
const int xbRSTICKAX = 2;//Right stick X axis
const int xbRSTICKAY = 3; //Right stick Y axis
const int xbRBA = 5;//Right trigger axis
const int xbA = 6;//Button A
const int xbB = 7;//Button B
const int xbX = 8;//Button X
const int xbY = 9;//Button Y
const int xbLB = 10;//Button Left Bumber
const int xbRB = 11;//Button Right Bumber
const int xbSHARE = 12;//Button Share
const int xbSETTINGS = 13;//Button Settings
const int xbEMBLEM = 14;//Button Emblem
const int xbLSTICKD = 15;//Left stick down
const int xbRSTICKD = 16;//Right stick down
const int xbDL = 17;//Dpad left
const int xbDR = 18;//Dpad Right
const int xbDU = 19;//Dpad up
const int xbDD = 20;//Dpad down
void setup()
{
	Serial1.begin(115200);
	Serial.begin(115200);
	Serial.println("Start");
	myTransfer.begin(Serial1);

}

Main and the code I am having troubles with.

void buttonsCheckMenuNavigation()//Menu navigation code
{
	if (inputArray[xbA])
	{
		menu_system.call_function(select);//Calls function attached to select
	}

	if (inputArray[xbDL])
	{
		menu_system.call_function(decrease);//Calls function to decrease current value
	}

	if (inputArray[xbDR])
	{
		menu_system.call_function(increase);//Calls function to increase current value
	}

	if (inputArray[xbDU])
	{
		menu_system.switch_focus(false);//Scrolls menu up
	}
	if (inputArray[xbDD])
	{
		menu_system.switch_focus(true);//Scrolls menu down
	}
}
void checkTransfer()
{
	if (myTransfer.available())
	{
		Serial.println("Check transfer");
		uint16_t recSize = 0;
		recSize = myTransfer.rxObj(inputArray, recSize); 

		//Increase and decrease speed value
		if (inputArray[xbLBA] < inputArray[xbRBA])
		{
			//Right trigger code
			speedmodifier = map(inputArray[xbRBA], 0, 1000, 0, 100);
			speed = speed + speedmodifier;
			//Checks if speed is over max
			if(speed > MaxTotalSpeed){
				speed = MaxTotalSpeed;
			}
		}
		else
		{
			//Left trigger code
			speedmodifier = map(inputArray[xbLBA], 0, 1000, 0, 100);
			speed = speed - speedmodifier;
			//If speed is negative
			if(speed < 0){
				speed = 0;
			}
		}
		//Serial.println(speed);

		//Maps stick analog values to the speed
		for (int i = 0; i < 4; i++)
		{
			inputArray[i] = map(inputArray[i], 0, 1000, 0, speed);
		}

		//Print inputArray
		
		for(int i = 0; i < xboxInputAmount; i++){
			Serial.print(inputArray[i]);
			Serial.print("|");
		}
		Serial.println();

		for(uint16_t i=0; i < myTransfer.bytesRead; i++)
			myTransfer.packet.txBuff[i] = myTransfer.packet.rxBuff[i];

		myTransfer.sendData(myTransfer.bytesRead);
		
	}
}

void loop()
{
	checkTransfer();
	buttonsCheckMenuNavigation();

}

Errors are
ERROR: STOP_BYTE_ERROR
ERROR: CRC_ERROR
ERROR: STALE PACKET

Also the menu code should work, I'm having troubles with sending values and acquiring the xbox button presses but I think I can fix that.

Only read when a new sample is ready?

Hi PB2,

I'm using your awesome library to read sensors connected to the serial port and save the data to an HDF5 file. My problem is that I cannot prevent the code "reading of non-existing samples" and writing zeros. The slower the sample rate, the more zeros it writes. I need to implement an instruction which checks if a new sample is ready.

Would you please tell how could I do it?

Best regards,
Adam

Edit: PySerial in_waiting is already embedded in the library, so it should wait until data is available in the buffer.

import struct
import h5py
import pandas as pd
import numpy as np
from pySerialTransfer import pySerialTransfer as txfer
import time

buffLen = 10
hdfFile = 'dtype.hdf5'
dataTypes = ['<i2', '<f4', '<f4', '<f4']
dataSets = ['r', 'x', 'y', 'z']

if __name__ == '__main__':
    try:
        # Define the serial connection
        link = txfer.SerialTransfer('COM3', 460800)
        # Connect to port and check
        if link.open():
            # Wait serial to start
            time.sleep(3)
            # Create an HDF5 file
            with h5py.File(hdfFile, 'w') as hf:
                # Create HDF5 datasets for all sensors
                for i in range(len(dataSets)):
                    hf.create_dataset(dataSets[i], (0,), dtype=dataTypes[i], chunks=True, compression="gzip", maxshape=(None,))

                # Write 500 batches of data
                for i in range(500):
                    # Batch buffer
                    buffer = []
                    # Check if buffer empty
                    if not buffer:
                        # Fill the buffer with lists of values
                        for count in range(buffLen):
                            dataList = [0 for x in range(4)]
                            if link.available():
                                dataList[0] = struct.unpack('h', bytes(link.rxBuff[0:2]))[0]
                                dataList[1] = struct.unpack('f', bytes(link.rxBuff[2:6]))[0]
                                dataList[2] = struct.unpack('f', bytes(link.rxBuff[6:10]))[0]
                                dataList[3] = struct.unpack('f', bytes(link.rxBuff[10:14]))[0]
                            elif link.status < 0:
                                print('ERROR: {}'.format(link.status))
                            buffer.append(dataList)
                    # Pandas data frame out of buffer
                    df = pd.DataFrame(buffer, columns=dataSets)
                    #print(df)
                    # Write buffer data frame to HDF5 file
                    for colName in df.columns:
                        hf[colName].resize((hf[colName].shape[0] + len(df.index),))
                        hf[colName][-len(df.index):] = df.loc[:, colName]

    except KeyboardInterrupt:
        link.close()

problem

Transferring Integers Rather than Float/Double

My project involves sending slew and luff motion control signals that range from 700 to 2300 from a Python program to an Arduino.
I have been able to successfuly transfer the data using the float(Python)/double(Arduino) object types.
I'd like to reduce the packet size as much as possible to maximise motion control scanrate, however I cannot seem to get integer transfers to work.
Any advice on how to transfer integers rather than floats is appreciated.

Arduino code and the test Python script below

'`#include "SerialTransfer.h"
#include <Servo.h>


Servo slewServo;
int slewServoPIN = 13;
int slewServoPosMin =  1100;
int slewServoPosMid =  1500;
int slewServoPosMax =  1800;


Servo luffServo;
int luffServoPIN = 12;
int luffServoPosMin =  1400;
int luffServoPosMid =  1600;
int luffServoPosMax =  2050;


SerialTransfer myTransfer;

struct STRUCT {
  double slew;
  double luff;
} ST_servoCtrlData;

bool BL_waitingForData = true;

int slewPos;
int luffPos;
int slewSV;
int luffSV;
int slewPosOld;
int luffPosOld;


void setup()
{
  Serial.begin(115200);
  myTransfer.begin(Serial);
  slewServo.attach(slewServoPIN);
  slewServo.writeMicroseconds(slewServoPosMid);
  luffServo.attach(luffServoPIN);
  slewServo.writeMicroseconds(luffServoPosMid);
  
  slewPos = slewServoPosMid;
  luffPos = luffServoPosMid;

  
}


void loop()
{
  // Receive data
  if(myTransfer.available())
  {
    uint16_t recSize = 0; 
    recSize = myTransfer.rxObj(ST_servoCtrlData, recSize);
    BL_waitingForData = false;
    slewPos = int(ST_servoCtrlData.slew);
    luffPos = int(ST_servoCtrlData.luff);
  }



  //Servo control
  if((slewPos >= slewServoPosMin) && (slewPos <= slewServoPosMax))
  {
    slewServo.writeMicroseconds(slewPos);
  }
  if((luffPos >= luffServoPosMin) && (luffPos <= luffServoPosMax))
  {
    luffServo.writeMicroseconds(luffPos);
  }


  // Send data
  if(!BL_waitingForData) 
  {
    uint16_t sendSize = 0;
    sendSize = myTransfer.txObj(ST_servoCtrlData, sendSize);
    myTransfer.sendData(sendSize);
    BL_waitingForData = true;
  }


}`
from time import sleep
from pySerialTransfer import pySerialTransfer as txfer


class struct(object):
    slew = 300.0
    luff = 123.0

BL_waitingForReady = False


if __name__ == '__main__':
    try:
        testStruct = struct
        link = txfer.SerialTransfer('COM5')
        print("Serial port successfully connected")
        link.open()
        sleep(5)
    
        while True:
            
            if not(BL_waitingForReady):

                sendSize = 0
                sendSize = link.tx_obj(testStruct.slew, start_pos=sendSize)
                sendSize = link.tx_obj(testStruct.luff, start_pos=sendSize)
                link.send(sendSize)
                BL_waitingForReady = True
                print("Data transmitted")
            

            elif link.available():

                recSize = 0
                testStruct.slew = link.rx_obj(obj_type='f', start_pos=recSize)
                recSize += txfer.STRUCT_FORMAT_LENGTHS['f']
                testStruct.luff = link.rx_obj(obj_type='f', start_pos=recSize)
                recSize += txfer.STRUCT_FORMAT_LENGTHS['f']
                print('{} {}'.format(testStruct.slew, testStruct.luff))
                BL_waitingForReady = False
                
            elif link.status < 0:
                if link.status == txfer.CRC_ERROR:
                    print('ERROR: CRC_ERROR')
                elif link.status == txfer.PAYLOAD_ERROR:
                    print('ERROR: PAYLOAD_ERROR')
                elif link.status == txfer.STOP_BYTE_ERROR:
                    print('ERROR: STOP_BYTE_ERROR')
                else:
                    print('ERROR: {}'.format(link.status))


        
    except KeyboardInterrupt:
        link.close()

pyserialtransfer vs pyserial

How does this library differ from pyserial? Sorry if this is wrong place to ask, I can't find a more appropriate place!

Typo in example file and logical error

Hi @PowerBroker2,

First off, amazing work, your library is quite impressive!

I'm trying to use it to send a large text file via Python over UART to a Teensy.

What I noticed is that there is a typo in your example file on this line, the ')' at the end prevents it from compiling.

file[fileIndex] = (char)myTransfer.packet.rxBuff[i]);

A bigger issue though, is that combined with the Python example file, I can't get any data through. What I noticed through some tests is that the condition myTransfer.currentPacketID() == 1 never seems to be true.

Just to give you a bit of insight, I'm trying to store the incoming file onto an SD card, below you can see my code.

#include <SD.h>
#include <SPI.h>
#include "SerialTransfer.h"

const int fileSize = 2000;
char file[fileSize];
uint16_t fileIndex = 0;
char fileName[10];

SerialTransfer myTransfer;
const int chipSelect = BUILTIN_SDCARD;

void setup()
{
  Serial.begin(115200);
  myTransfer.begin(Serial);

  Serial.print("Initializing SD card...");

  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    while (1) {
      // No SD card, so don't do anything more - stay stuck here
    }
  }
  Serial.println("card initialized.");

  pinMode(13, OUTPUT);
}

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

    // open the file.
    File dataFile = SD.open("datalog.txt", FILE_WRITE);
    if (!myTransfer.currentPacketID())
    {
      myTransfer.rxObj(fileName);
    }
    else if (myTransfer.currentPacketID() == 1)
    {
      myTransfer.rxObj(fileIndex);
      digitalWrite(13, HIGH);

      for (uint8_t i = sizeof(fileIndex); i < myTransfer.bytesRead; i++)
      {
        file[fileIndex] = (char)myTransfer.packet.rxBuff[i];
        if (dataFile) {
          dataFile.print(file[fileIndex]);
        } else {
          // if the file isn't open, pop up an error:
          Serial.println("error opening datalog.txt");
        }
        fileIndex++;
      }
    }
    dataFile.close();
  }
}

Any clue what could be going on here?

PS: The Python script is basically just like the one from the example, all I did was add some print logs

from time import sleep
from pySerialTransfer import pySerialTransfer as txfer


file = 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestib'
fileSize = len(file)
fileName = 'test.txt'


if __name__ == '__main__':
    try:
        link = txfer.SerialTransfer('COM13')
        
        link.open()
        sleep(5)
        print('start')
        while True:
            link.send(link.tx_obj(fileName))
            
            numPackets = int(fileSize / (txfer.MAX_PACKET_SIZE - 2))
            
            if numPackets % txfer.MAX_PACKET_SIZE:
                numPackets += 1
            
            
            
            for i in range(numPackets):
                fileIndex = i * txfer.MAX_PACKET_SIZE
                dataLen = txfer.MAX_PACKET_SIZE - 2
                
                if (fileIndex + (txfer.MAX_PACKET_SIZE - 2)) > fileSize:
                    dataLen = fileSize - fileIndex
                
                dataStr = file[fileIndex:dataLen]
                
                sendSize = link.tx_obj(fileIndex, val_type_override='h')
                sendSize = link.tx_obj(dataStr, start_pos=sendSize)
                link.send(sendSize)
                
                sleep(1)
            
            print('sleep')
            sleep(10)
                
    except KeyboardInterrupt:
        link.close()

Sending a Boolean

Hello glorious PowerBroker!

I'm trying to send along a boolean value in a struct but I'm not sure how to do it with pySerialTransfer! Any advice?

Receiving duplicate data. Can only read .rx_obj() 7 times before IndexError

Hi, thank you for providing this crucial library!
I'm sure I have overlooked something. I appreciate your help in resolving this.
Full Arduino code at the bottom of this post.

The Ultimate Goal:
Arduino reads one sample from each of 8 sensors, into an array[8], and transmits the array over serial, 80 times/sec.
Python parses the incoming array and plots the values on a graph in real time.

The good:
I believe everything is correct and working well on the arduino side. When I use Serial.print() to print the array of sensor values instead of using SerialTransfer, the values change each time like they should - so the Arduino is not sending duplicate packets.

The Python Code: (I'm working in Jupyter on Windows10)

import time
from pySerialTransfer import pySerialTransfer as txfer

link = txfer.SerialTransfer('COM7', baud=115200)
link.open()
time.sleep(2) # allow some time for the Arduino to completely reset

def get_packet(st_obj, byte_size):
    while True:
        st_obj.available()
        yield st_obj.rx_obj(obj_type = list,
                       obj_byte_size = 32,
                       list_format = 'l')
        
stream = get_packet(link, 32)

I can print 7 times...

for x in range(7):
    print(stream.__next__())

Prints the same packet 7 times:

[-179442, -161803, -204057, -239409, -198581, -230921, 16002, -176593]
[-179442, -161803, -204057, -239409, -198581, -230921, 16002, -176593]
[-179442, -161803, -204057, -239409, -198581, -230921, 16002, -176593]
[-179442, -161803, -204057, -239409, -198581, -230921, 16002, -176593]
[-179442, -161803, -204057, -239409, -198581, -230921, 16002, -176593]
[-179442, -161803, -204057, -239409, -198581, -230921, 16002, -176593]
[-179442, -161803, -204057, -239409, -198581, -230921, 16002, -176593]

If I try to print an 8th time:
print(stream.__next__())

I get an IndexError:

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-4-0991908dffd0> in <module>
----> 1 stream.__next__()

<ipython-input-2-d12a92ebb86c> in get_packet(st_obj, byte_size)
      5 def get_packet(st_obj, byte_size):
      6     while True:
----> 7         st_obj.available()
      8             yield st_obj.rx_obj(obj_type = list,
      9                        obj_byte_size = 32,
C:\Users\me\AppData\Local\Continuum\anaconda3\lib\site-packages\pySerialTransfer\pySerialTransfer.py in available(self)
    475                     elif self.state == find_payload:
    476                         if self.payIndex < self.bytesToRec:
--> 477                             self.rxBuff[self.payIndex] = recChar
    478                             self.payIndex += 1
    479 

IndexError: list assignment index out of range

What am I doing wrong here?

Full Arduino Code:

#include "HX711-multi.h"
#include "SerialTransfer.h"

// Hardware Config
#define CLK1 2    // clock pin to the first group of ADCs
#define DIN1 3    // data pin to the first lca
#define DIN2 4    // data pin to the second lca
#define DIN3 5    // data pin to the third lca
#define DIN4 6    // data pin to the fourth lca

#define CLK2 8    // clock pin to the 2nd group of ADCs
#define DIN5 7    // data pin to the fifth lca
#define DIN6 10   // data pin to the sixth lca
#define DIN7 11   // data pin to the seventh lca
#define DIN8 12   // data pin to the eighth lca

// System Config
#define CHANNEL_COUNT 4
#define NUM_GROUPS 2
#define GAIN 128                  // Gain level for HX711 to use ( 32, 64, 128 are your options)

// Construct scale objects
byte DINS1[CHANNEL_COUNT] = {DIN1, DIN2, DIN3, DIN4};
HX711MULTI scales1(CHANNEL_COUNT, DINS1, CLK1, GAIN);

byte DINS2[CHANNEL_COUNT] = {DIN5, DIN6, DIN7, DIN8};
HX711MULTI scales2(CHANNEL_COUNT, DINS2, CLK2, GAIN);

HX711MULTI scales[NUM_GROUPS] = {scales1, scales2};      // Add all scale instances to an array so we can iterate over them

// Program Variables
long raw[NUM_GROUPS * CHANNEL_COUNT];      // Array to hold raw samples collected from each sensor
uint16_t sendSize;
int i;

SerialTransfer myTransfer;

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

void loop() {
  //Read sensor values into an array
  scales[0].readRaw(raw);                  // Read values from first 4 sensors directly into the raw array
  scales[1].readRaw(raw + 4);           // Read values from last 4 sensors directly into the raw array
  // contents of raw looks like: {-180279, -160207, 57267, -238712, -198426, -232254, 14375, -176826}

  // Transfer the contents of raw to Python via serial
  sendSize = myTransfer.txObj(raw, sendSize);
  myTransfer.sendData(sendSize);

  // Loop around, read new values from sensors, and send new values.
}

Receiving from Arduino works but sending to Arduino doesn't

Hi there.
So I'll be frank, I'm not 100% sure it's the Python library but it looks like it is.

So the Python library can receive data without issues. But sending the data doesn't work.
From my testing when sending a single byte the arduino received 5 bytes (I checked Serial.available()) instead of the 7 I would expect when sending a single payload byte and cnsequently the SerialTransfer::available call on the Arduino always returns 0. (That's what's making me suspect the Python library)
Having the library in debug mode didn't throw any errors so I'm guessing it doesn't properly send the start byte in the first place.

I tested it with both Python 3.7.3 and Python 2.7.16, both with the same result.
The testing was conducted on a Debian 10 machine and the Arduino was connected over an internal USB-Port.

Saving List onto Arduino

Hello PowerBroker!

My name is Jeremy and I just discovered this package for reading/writing to an Arduino using Python. I'm trying to send a list of numbers that indicate a trial type for an experiment I'm working on. Generating the list externally through Python and sending it for use in the experiment would be so helpful for my team!

I've successfully gotten your package to communicate back and forth to the Arduino, but I'm not sure how to make sure the data I sent is actually saved and usable in the Arduino's memory. I'm not sure if this is the appropriate place to ask about this kind of testing since the package seems to work, but I didn't see a different way of trying to contact you.

Any advice would be greatly appreciated! It would be cool to contribute to the project as I learn and provide examples relevant for my team's use that others could use.

Sincerely,

Jeremy Delahanty
Salk Institute for Biological Studies, La Jolla CA

Portability vs. non-adjustable endianness in Python implementation

To ensure portability, SerialTransfer can either

  1. use network byte order on all implementations or
  2. let the user choose on all its implementations.

PowerBroker2/SerialTransfer API works with low-level integer types and uses native host byte order. This is OK, as the users in C++ have well-known ways how to ensure endianness compatibility on these low-level integer types, should they need it.

PowerBroker2/pySerialTransfer API works with high-level Python int type and uses struct packing in Python-host-native byte order. This might be an issue, as changing endianness on top of Python int type rapes its semantics and requires a hacky solution.

I'd suggest adding an optional parameter to tx_obj() and rx_obj() methods, allowing the users to pass byte order to the calls. The value would be according to https://docs.python.org/3/library/struct.html#byte-order-size-and-alignment, native by default.

Does it make sense?

pySerialTransfer Questions

Hello PowerBroker2,

We are a high school robotics team that came across your code.
One of our goals is to run serial communication between Python on a laptop and an Arduino over RS485.
We have tried your pySerialTransfer code to verify functionality.

We are able to get Python to send and receive data while using the USB connection to the Arduino, but not over RS485. At first, we thought our RS485 communication could be a hardware issue.
However, all of our hardware appears to work individually.

  1. We have tested our USB to RS485 adapter using a second adapter and two instances of "AccessPort" from Serialcomm.com. We are able to communicate between two laptops with this method.

  2. We replaced the USB to RS485 adapters using two Arduinos connected through their serial pins. The Arduinos both run an "empty" sketch with pins 0 and 1 set as inputs. We are able to communicate between two laptops with AccessPort as above.

  3. We have tested two Arduinos communicating over RS485 using TTL to RS485 converters. These work as well.

  4. We have tried using a USB to RS485 adapter with A, B and ground running to the TTL to RS485 converter. We are using Serial1 for RS485 communication and Serial to send debug statements to the Serial Monitor. The code never enters the "if(myTransfer.available())" loop on the Arduino.

  5. To try to simplify the connections, our latest test was to eliminate the RS485 connections. We returned to using two Arduinos. One Arduino used the setup described in "2" above to serve as a USB to TTL converter. The second Arduino ran your sample code. The result is the same as in "4" above. In both of these cases, we modified the code to use Serial1. After the "if(myTransfer.available())" we added "else Serial.println("Not available");" to give us feedback.

After reading through several web pages that you have offered advice on, we came across issue #32 (#32).
We tried several of the code examples you provide there with no luck.

Software is our weakest area this year. The addition of Python has made it more complicated for us.
We apologize if there is a simple solution that we are missing.

We are not understanding much of the code as it relates to SerialTransfer and pySerialTransfer, so we do not know how to further analyze the "if(myTransfer.available())" functionality. We have started reading through "Serial Input Basics" and your "Serial Input Advanced" tutorials on the Arduino website.

Another issue that came up for us was in trying one of your examples shared here: (https://forum.arduino.cc/t/arduino-to-arduino-serial/621141/15). Our Arduino would not compile your code and gave us the following error: "class SerialTransfer' has no member named 'txBuff'"

With all that said, we are wondering if you have any suggestions since pySerialTransfer seems like it would be helpful to us.

Thanks in advance for your time.

Arduino tx_data + pySerialTransfer rx_data example fails with esp32 Arduino

On multi-byte wide processors the compiler aligns data items to their native alignment, unless directed otherwise. Thus a float is aligned to a 4-byte boundary. Therefore the testStruct in tx_data is laid out in memory differently between an Arduino Uno & a board like an esp32. Since txObj uses sizeof(T) as the number of bytes to transmit, tx_data sends a different number of bytes from esp32 compared to Uno.

testStruct is shown below with the fix needed for processors with > 8-bit wide data bus
struct STRUCT {
char z;
float y;
//} testStruct;
} attribute((packed)) testStruct;

The original code (commented out line 4 above) causes 3 empty bytes to be placed after z, and before y on esp32, so txObj sends 8 bytes on an esp32, but only 5 on an Arduino Uno. pySerialTransfer is unaware of the packing difference, so tries to pull the float out of bytes 1-4 (counting byte 0 as z), regardless of the sender-architecture.

The example as provided prints the following correct message on pySerialTransfer + Uno:
b'$'4.5 | hello
but the following error message on pySerialTransfer with esp32, caused by pySerialTransfer reading the float out of bytes 1-4 then trying to read the char array out of byte 5, which is actually the 2nd byte of the float.:
Traceback (most recent call last):
File "./rx_data.py", line 30, in
arr = link.rx_obj(obj_type=str,
File "/home/bouchier/.local/lib/python3.8/site-packages/pySerialTransfer/pySerialTransfer.py", line 359, in rx_obj
unpacked_response = unpacked_response.decode('utf-8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x90 in position 1: invalid start byte

Removing the string transfer part of the example prints the following incorrect message on pySerialTransfer + esp32, :
b'$'0.0 |
Note the float, which should be 4.5, is 0.0 because pySerialTransfer is reading the float out of the 3 empty bytes plus first byte of the float.

The correct way to eliminate machine/compiler dependent packing is with the packed attribute as shown on line 5 of testStruct above. Making this change produces the correct output on both Uno & esp32:
b'$'4.5 | hello

Are you open to pull requests to fix the examples so they work on both Uno & esp32? I would test any such changes on both.

Check for CRC error

Hello,

thank you for this great package!

I wanted to understand your code and manipulated some lines in CRC.py.

But with your example Python script, nothing printed and I didn't get a Python error. After a while, I realized that:

CRC_ERROR = 0

So I think in the example script it should be:

if link.status <= 0:

 while not link.available():
                if link.status <= 0:
                    if link.status == txfer.CRC_ERROR:
                        print('ERROR: CRC_ERROR')
                    elif link.status == txfer.PAYLOAD_ERROR:
                        print('ERROR: PAYLOAD_ERROR')
                    elif link.status == txfer.STOP_BYTE_ERROR:
                        print('ERROR: STOP_BYTE_ERROR')
                    else:
                        print('ERROR: {}'.format(link.status))

Kind regards,
8sawte8

Problems with pySerialTransfer

Hi PowerBroker,

i want to transfer data from python to an arduino nano clone. Ive used your example sketches and changed them just a little bit. Im able to transfer one value but then it stops sending. Im using python 3.9 and SerialTransfer version 2.1.6 i think.

Python:

from time import sleep
from pySerialTransfer import pySerialTransfer as txfer
import time

class struct(object):   #data i want to transfer
    gear_f = 16383
    gear_r = 0
    gear_l = 0


if __name__ == '__main__':
    try:
        flight_data = struct
        link = txfer.SerialTransfer('COM8')

        link.open()
        sleep(5)

        while True:
            sendSize = 0

            sendSize = link.tx_obj(flight_data.gear_f, start_pos=sendSize)   #only sends once
            sendSize = link.tx_obj(flight_data.gear_r, start_pos=sendSize)   #doesnt send at all
            sendSize = link.tx_obj(flight_data.gear_l, start_pos=sendSize)   #doesnt send at all

            print("sending")
            link.send(sendSize)

            while not link.available(): #goes in this loop but not in if statement
                if link.status < 0:
                    print('ERROR: {}'.format(link.status))


    except KeyboardInterrupt:
        link.close()    #compiler message: Name 'link' can be undefined`

Arduino:

#include "SerialTransfer.h"


SerialTransfer myTransfer;

struct STRUCT{
   int gear_f;
   int gear_r;
   int gear_l;
} flight_data;

//int x=0;
//int y=0;
//int z=0;
const int ledpin=9;


void setup()
{
  Serial.begin(115200);
  myTransfer.begin(Serial);
  pinMode(ledpin, OUTPUT);
}


void loop()
{
  if(myTransfer.available())
  {
    // use this variable to keep track of how many
    // bytes we've processed from the receive buffer
    uint16_t recSize = 0;


    recSize = myTransfer.rxObj(flight_data, recSize);
    
    //recSize = myTransfer.rxObj(x, recSize);
    //recSize = myTransfer.rxObj(y, recSize);
    //recSize = myTransfer.rxObj(z, recSize);

    if(flight_data.gear_f==16383)
      {
        digitalWrite(ledpin, HIGH);  
      }
  }
}

Is there a maximum variable size for "x" when using myTransfer.sendDatum("x")?

Hi, I'm trying to send a structure from a microcontroller to my computer. The structure has 7 fields and each field is a "packetSize" element array. Here is the structure initialization on the microcontroller:

const int packetSize = 5;
struct STRUCT {
int ax[packetSize];
int ay[packetSize];
int az[packetSize];
int gx[packetSize];
int gy[packetSize];
int gz[packetSize];
int ts[packetSize];
} sendStruct;

The python code on my computer looks like this:
data = link.rx_obj(obj_type=list, obj_byte_size=457, list_format='i')

In the above python code, 4 is the bytes per number, 5 is the "packetSize," and 7 is the number of fields in my structure. I'm trying to maximize the "packetSize" here. When I upload my arduino code, I can put any number for packetSize, but if I want to receive the data through python the largest packetSize I can use is 9. A packetSize of 9 or less works perfectly, but anything greater than 9 gives the following error:

//
arr = array(list_format, buff)
ValueError: bytes length not a multiple of item size
//

Is there a way I can use a packetSize of more than 9 or is this the maximum amount of data that can be sent at once?

Error when tryinf to receive integer

Hi!

I want to transfer an Integer from my Arduino to my Computer. The Arduino script is based on:
https://github.com/PowerBroker2/pySerialTransfer/blob/master/examples/datum/Arduino/tx_datum/tx_datum.ino

#include "SerialTransfer.h"


SerialTransfer myTransfer;

int y;


void setup()
{
  Serial.begin(115200);
  myTransfer.begin(Serial);

  y = 4;
}


void loop()
{
  myTransfer.sendDatum(y);
  delay(500);
}

My Python Code is based on:
https://github.com/PowerBroker2/pySerialTransfer/blob/master/examples/datum/Python/rx_datum.py

from time import sleep
from pySerialTransfer import pySerialTransfer as txfer


y = 0


if __name__ == '__main__':
    try:
        link = txfer.SerialTransfer('/dev/ttyACM0')
        
        link.open()
        sleep(5)
    
        while True:
            if link.available():
                y = link.rx_obj(obj_type='i')
                print(y)
                
            elif link.status < 0:
                if link.status == txfer.CRC_ERROR:
                    print('ERROR: CRC_ERROR')
                elif link.status == txfer.PAYLOAD_ERROR:
                    print('ERROR: PAYLOAD_ERROR')
                elif link.status == txfer.STOP_BYTE_ERROR:
                    print('ERROR: STOP_BYTE_ERROR')
                else:
                    print('ERROR: {}'.format(link.status))
                
        
    except KeyboardInterrupt:
        link.close()

When I try to run the Python Code I get the error:

Traceback (most recent call last):
File "b.py", line 17, in
y = link.rx_obj(obj_type='i')
File "/home/clemens/.local/lib/python3.8/site-packages/pySerialTransfer/pySerialTransfer.py", line 346, in rx_obj
buff = bytes(self.rxBuff[start_pos:(start_pos + STRUCT_FORMAT_LENGTHS[obj_type])])
TypeError: 'str' object cannot be interpreted as an integer

Is this a bug or am I missing something?

Best Regards,
Clemens

Examples use double on Arduino side, format 'f' on python side

Hey PowerBroker - nice library! Thanks very much - it will save me much work compared with PacketSerial.

The last checkin comment for examples/datum/Arduino/ was that it fixed float precision. But Arduino declares doubles in the datum and data examples, but the python side reads it as 'f' (float). Double size is apparently platform-dependent; ghe Arduino language reference says double is 4 bytes (same as float) on Uno and ATMEGA but 8 bytes on Due. I am testing it on an esp32, where apparently doubles are 8 bytes, because I had to change the format specifier in the python example code to 'd' to get it to read the data from esp32-Arduino correctly.

I would think you should make the types match on the Arduino & python sides. Is there some deeper issue here that made you declare Due unsupported? Would such an issue also apply to esp32?

And on the subject of esp32, does the design take care to keep tx & rx logic in Arduino & python (and everything they use) separate such that a receive thread and a transmit thread could run through the classes simultaneously and not step on each other?

Want support for transferring bytes()

bytes() is its own type in python3, and is common enough IMHO that pySerialTransfer should support it. Since bytes() do not need packing with struct.pack(), the proposed solution is to change the logic in txObj to check for type(val) == bytes and not attempt to pack it.

Currently, byte strings must be unpacked into ints then passed in with one call to txObj per byte, giving val_type_override as:
send_size = link.tx_obj(self.left_drive_pct, val_type_override='b')

The alternative - to send values as ints and convert them to bytes on the Arduino side, would consume 4X the bandwidth on the serial link.

Thoughts? I am willing to contribute this fix in a pull request if you support the idea.

Need byte transfer.

I dint see byte type in your code. please implement it.
I have more efficiency when working with byte variable instead of send them by int.

Float problem

Hi im got a problem with float on python side (RX), got a this kind of output: b'P'2.1579996350602183e-43
When im sending from uC:
testStruct.z = 'P';
testStruct.y = 4.55;
Betwen uC is no problem work like a harm, on python floats crazy.
9600 baud / Python 3.9.2 / Debian

CRC.calculate() throws TypeError when running example code

Using the example code from readme.md, then following traceback is thrown by the CRC module:

Traceback (most recent call last):
  File "/home/pi/.virtualenvs/tauradigm/lib/python3.5/site-packages/pySerialTransfer/CRC.py", line 43, in calculate
    crc = self.cs_table[crc ^ arr[i]]
TypeError: unsupported operand type(s) for ^: 'int' and 'str'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/pi/.virtualenvs/tauradigm/lib/python3.5/site-packages/pySerialTransfer/pySerialTransfer.py", line 228, in send
    found_checksum = self.crc.calculate(self.txBuff, message_len)
  File "/home/pi/.virtualenvs/tauradigm/lib/python3.5/site-packages/pySerialTransfer/CRC.py", line 46, in calculate
    crc = self.cs_table[arr]
TypeError: list indices must be integers or slices, not list

I think there's actually two errors in there - the first is on L43 of calculate() where an attempt is made to raise crc to the power of arr[i] - but arr[i] is a zero length string (assuming my bodge to address #1 is correct ;-) )

secondly, when that throws a TypeError, it falls through to the exception handler and attempts to set crc to the value found at index arr of self.cs_table, but arr is of course an array, so this also fails.

Encoding Many Variables Correctly

Hello again PowerBroker!

The lab is happy with how the Serial Transfer works and has asked me to try and implement a transfer of all experimental variables to the Arduino. I'm wondering how I should best transfer many different variables using the package. Should I send things using individual functions one at a time or should I open communications once and send it all during the same transmission? Do you think it matters which way I do it?

Reading is slow

Hey there, I firstly want to say that this library (and its Arduino companion) is awesome and makes communicating with Arduinos so much easier.

I've been running into some issues reading data from multiple Arduinos at the same time. If I just read from one Arduino, it only takes 1-2ms for transfer.available() to complete (measured using time.perf_counter). But if I connect 2 devices that are both sending data to the computer at 10hz, the readings sometimes only take 1-2ms, but sometimes they take 20-70ms. Usually it starts at 1-2ms for a few seconds, then it starts taking 20-70ms for the rest of the time it is running.

The 2 transfer objects are on separate threads, so I imagine this is the root of the issue somehow... I am sending data on a separate thread, but I tried adding locks and that didn't fix the problem.

If I never send any data and just receive, then it always runs at 1-2ms. If I send data for a few seconds and then stop sending data, then it runs at 20-70ms, even well after I stopped sending data.

Each transfer receives in its own thread in this method:

    def run_serial_receiver(self, remote: transfer.SerialTransfer, lock):
        while True:
            time.sleep(.001)
            with lock:
                t0 = time.perf_counter()
                if remote.available():
                    dt = (time.perf_counter() - t0)
                    print(dt)
                    if(dt>.01):
                        print(f"EXCESSIVE TIME: {dt}")
                    sliced_buff = remote.rxBuff[0:remote.bytesRead]
                    rxbytes = bytes(sliced_buff)
                    self.process_RemoteMessage(rxbytes)

And data is sent using this method on the main thread:

    def send_output(self, output):

        packet_size = len(output)
        for i, remote in enumerate(self.remotes):
            with self.serial_locks[i]:
                remote.txBuff = output
                remote.send(packet_size)
        if self.send_callback:
            self.send_callback()

I'm running on Windows 10, with the SerialTransfer library running on a Teensy 4.0.

Exemple requested for processing bytes list with rx arduino

Hello ,
Firstly, thanks for your libs :) !
I've tested your exemple code and its work well.
But I need a little exemple for processing a list of byte with the arduino.
If we keep your exemple, you send this : list_ = [1, 3]
How on the arduino while receiving you can access first element on the list and second element ?

Thanks

Example code error. size mismatch?

Hi!

I have tried the example code in folder pySerialTransfer/examples/data/Python on my laptop. I use tx_data and rx_data over two virtual serial ports, but then I get an error on the receiver-side:

Traceback (most recent call last):
  File "rx_data.py", line 31, in <module>
    arr = link.rx_obj(obj_type=str,
  File "C:\Users\Luca\AppData\Local\Programs\Python\Python38\lib\site-packages\p
ySerialTransfer\pySerialTransfer.py", line 319, in rx_obj
    buff = bytes(self.rxBuff[start_pos:(start_pos + obj_byte_size)])
TypeError: 'str' object cannot be interpreted as an integer

I suppose this is happening because of obj_byte_size is set to 6 in rx_data.py

arr = link.rx_obj(obj_type=str,
                    start_pos=recSize,
                    obj_byte_size=6)

since if I set it to 5 the example code works fine!

Parsing char from Arduino side

Hello PB2 and a big thank you for your work!

I'm trying to trigger some action in the Arduino Leonardo side when a specific char is received from python. I could have use int instead of char message but I take this as an exercice :)

All the manipulation I tried failed... I'm starting to be tired of this and probably missing some perspective from this point.

So, here my Python:

X1 = [0, 0, 0, 0, 0, ... , 0]
X2 = [1, 1, 1, 1, 1, .... , 1] ---> X1 and X2 are long array

try:
    link = txfer.SerialTransfer('COM8', 115200, debug=True)
    link.open()

    start = 0
    step = 10
    counter = 0

    while counter < len(X1):
        sendSize = 0

        X1_= X1[start:start+step] ---> I split it in order to get under 254 bytes per packet.
        X2_= X2[start:start+step]
        message = "notes"
        
        messageSize = link.tx_obj(message) 
        sendSize += messageSize

        X1Size = link.tx_obj(X1_, sendSize) - sendSize
        sendSize += X1Size

        X2Size = link.tx_obj(X2, sendSize) - sendSize
        sendSize += X2Size

        link.send(sendSize, packet_id=0) ---> I recently tried to add packet_id information to don't messing up the receiving part.
        counter += len(X1_)

        #print('SENT: {} {} {}'.format(message, X1_, X2_))

        while not link.available():
            pass

        rec_message = link.rx_obj(obj_type=type(message), obj_byte_size=messageSize)
        rec_X1 = link.rx_obj(obj_type=type(X1_), obj_byte_size=X1Size, list_format='i', start_pos = messageSize)
        rec_X2 = link.rx_obj(obj_type=type(X2_), obj_byte_size=X2Size, list_format='i', start_pos = messageSize + X1Size)

        start += step
        print("Recu:\t{} - {} et {}".format(rec_message, rec_X1, rec_X2))


    while True:
        sendSize2 = 0
        message2 = "click"
        messageSize = link.tx_obj(message2)
        sendSize2 += messageSize

        print("Ready to click...")
        link.send(sendSize2, packet_id=0)
        #print('SENT: {}'.format(message2))

        while not link.available():
            pass

        rec_message = link.rx_obj(obj_type=type(message2), obj_byte_size=messageSize)
        print("Recu:\t{}".format(rec_message))

        raise KeyboardInterrupt

except KeyboardInterrupt:
    try:
        link.close()
    except:
        pass

except:
    import traceback
    traceback.print_exc()

    try:
        link.close()
    except:
        pass

And my Arduino:

#include "SerialTransfer.h"
#include "Keyboard.h"
#include "Mouse.h"

SerialTransfer myTransfer;

int32_t X1[10];
int32_t X2[10];

int32_t X1G[] = {};
int32_t X2G[] = {};

char message[5];

#Stuff I tried....
char notes[] = "notes";
char clickOk[] = "click";

void setup()
{
  Keyboard.begin();
  Mouse.begin();
  Serial.begin(115200);
  myTransfer.begin(Serial, true);
}

void loop()
{
  if(myTransfer.available())
  {
    uint16_t recSize = 0;
    recSize = myTransfer.rxObj(message, recSize);

    if (strcmp(message, clickOk) == 0) ---> Here my problem. How to check the received message and do action?
    {
      recSize = myTransfer.rxObj(X1, recSize);
      recSize = myTransfer.rxObj(X2, recSize);
  
      memmove(X1G + sizeof(X1G), noteTime, sizeof(X1));
      memmove(X2G + sizeof(X2G), noteType, sizeof(X2));
  
      uint16_t sendSize = 0;
      
      sendSize = myTransfer.txObj(message, sendSize);
      sendSize = myTransfer.txObj(X1, sendSize);
      sendSize = myTransfer.txObj(X2, sendSize);
      
      myTransfer.sendData(sendSize);
     
      delay(50);
    }
    else if (strcmp(message, clickOk) == 0)
    {
      uint16_t sendSize2 = 0;
      sendSize2 = myTransfer.txObj(message, sendSize2);
      myTransfer.sendDatum(sendSize2);
      Mouse.click();
    }
  }
}

My FTDI card will be coming soon, it should be easier for debugging, but in the meantime any help will be appreciated :)

I'm a beginner in this wide world. Maybe my mechanism is completely broken, so feel free to suggest another solution to get what I want.

Bye,
Vouncat

CPU load optimization of the time critical infinite loop.

I use the following Python script to record data at 500 Hz on Raspberry Pi. Since it constantly checks for samples in the loop, it overloads the CPU almost 100%.
I would like to ask if it could be done differently by reducing CPU usage.
Thanks!

#!/usr/bin/env python3
from pySerialTransfer import pySerialTransfer as txfer
import os
import time

# get file directory
__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))

buffLen = 2 ** 12                                                   # buffer size
maxFileSize = 100 * 10 ** 6                                         # maximum file size
print('Max file size: ' + str(maxFileSize/1000) + ' MB')

if __name__ == '__main__':
    try:
        link = txfer.SerialTransfer('/dev/ttyUSB0', 460800)
        if link.open():
            print("Connected")
        # Wait serial to reset
        time.sleep(3)

        while True:
            # create a new file and open
            fileName = str(round(time.time())) + 'cms.bin'
            filePath = os.path.join(__location__, fileName)
            file = open(filePath, 'wb')
            print('New file: ' + fileName)
            with open(fileName, 'wb') as file:
            # start writing data to new file
                while True:
                    rawBuffer = bytearray()
                    for count in range(buffLen):
                        # write only if new data available
                        if link.available():
                            rawBuffer.extend(bytes(link.rxBuff[0:11]))
                        elif link.status < 0:
                            print('ERROR: {}'.format(link.status))
                        else:
                            continue
                    file.write(rawBuffer)
                    # break to create a new file
                    if os.path.getsize(fileName) > maxFileSize:
                        break

    except KeyboardInterrupt:
        link.close()

Problem with the example scripts

Hello!

The Example Python Script works well with Arduino Nano 33 BLE Sense.

However, when I try to modify it to send an array of floats, using the "Send a list" way, the tx_obj() -method returns the following error:

Traceback (most recent call last):
  File "C:\Users\Klupi\AppData\Local\Temp\ipykernel_25544\2144615584.py", line 19, in <module>
    list_size = link.tx_obj(list_)
  File "C:\Users\Klupi\miniconda3\envs\deeplearning\lib\site-packages\pySerialTransfer\pySerialTransfer.py", line 272, in tx_obj
    start_pos = self.tx_obj(el, start_pos)
  File "C:\Users\Klupi\miniconda3\envs\deeplearning\lib\site-packages\pySerialTransfer\pySerialTransfer.py", line 289, in tx_obj
    self.txBuff[index + start_pos] = val_bytes[index]
IndexError: list assignment index out of range

Why is this, what am I doing wrong? My sample array is as follows:

[-0.27273, -0.33333, 0.066667, -0.10638, -0.27273, -0.33333, -0.066667, -0.10638, -0.045455, -0.28889, -0.022222, 0.021277, -0.045455, -0.46667, -0.2, 0.10638, -0.045455, -0.2, 0.066667, 0.14894, -0.045455, -0.33333, 0.42222, 0.14894, -0.59091, -0.33333, 0.33333, 0.021277, -0.59091, -0.28889, 0.066667, -0.31915, -0.59091, -0.28889, -0.55556, 0.021277, -0.59091, -0.24444, -0.066667, 0.021277, -0.18182, 0.066667, 0.11111, 0.14894, -0.18182, 0.066667, 0.066667, 0.14894, -0.18182, 0.022222, -0.022222, -0.021277, -0.18182, 0.066667, 0.066667, -0.021277, 0.13636, 0.066667, -0.022222, -0.021277, 0.36364, -0.33333, 0.022222, -0.021277, 0.090909, -0.066667, -0.33333, -0.44681, 0.090909, -0.42222, -0.2, -0.19149, 0.090909, -0.11111, -0.11111, 0.10638, 0.090909, -0.24444, 0.022222, -0.021277, 0.090909, -0.066667, -0.066667, -0.021277, 0.090909, 0.066667, 0.066667, -0.021277, 0.090909, -0.022222, -0.2, 0.021277, 0.090909, -0.46667, -0.33333, 0.021277, 0.090909, -0.066667, -0.33333, -0.3617, 0.090909, -0.066667, -0.2, -0.3617, 0.090909, -0.066667, -0.33333, -0.48936, 0.090909, -0.24444, -0.2, -0.48936, -0.18182, -0.68889, -0.55556, -0.3617, -0.18182, -0.28889, -0.33333, -0.3617, -0.090909, -0.022222, -0.42222, -0.3617, 0.0, -0.24444, -0.86667, -0.3617, -0.045455, -0.2, -0.2, -0.10638, -0.045455, -0.066667, -0.33333, -0.10638, 0.090909, -0.11111, -0.33333, -0.65957, 0.090909, 0.2, -0.73333, -0.3617, 0.090909, 0.33333, -0.73333, -0.3617, 0.13636, 0.066667, -0.91111, -0.3617, 0.22727, -0.15556, -0.68889, -0.44681, 0.22727, 0.24444, -0.55556, -0.44681, 0.090909, 0.28889, -0.6, -0.44681, 0.63636, 0.11111, -0.42222, -0.74468, 0.63636, 0.066667, -0.6, -0.74468, 0.72727, 0.33333, -0.37778, -0.61702, 0.72727, -0.066667, -0.6, -0.61702, 0.81818, 0.2, -0.77778, -0.61702, 0.81818, 0.2, -0.73333, -0.61702, 0.77273, 0.066667, -0.95556, -0.61702, 0.77273, -0.2, -0.82222, -0.61702, 0.81818, 0.022222, -0.95556, -0.61702, 0.77273, 0.066667, -0.6, -0.82979, 0.77273, 0.33333, -0.73333, -0.82979, 0.77273, -0.37778, -0.86667, -0.82979, 0.27273, -0.066667, -0.68889, -0.3617, 0.27273, -0.066667, -0.68889, -0.70213, 0.27273, 0.15556, -0.6, -0.70213, 0.27273, 0.33333, -1.0, -0.70213, 0.77273, -0.15556, -0.82222, -0.61702, 0.77273, 0.6, -1.0, -0.61702, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

Interestingly, sending a smaller array such as [1.2433333333334, 2.34222222221, 3.4255555555555] works fine. How would you proceed on sending the bigger array to Arduino?

Many thanks!

something weird in "find_last" function

According to your code

  def find_last(self, pay_len):
        '''
        Description:
        ------------
        Finds last instance of the value START_BYTE within the given
        packet array

        :param pay_len: int - number of bytes in the payload

        :return: int - location of the last instance of the value START_BYTE
                       within the given packet array
        '''

        if pay_len <= MAX_PACKET_SIZE:
            for i in range(pay_len - 1, 0, -1):
                if self.txBuff[i] == START_BYTE:
                    return i
        return -1

This function suppost to scan all data but range(pay_len - 1, 0, -1) just stop before reach index 0.
This is on purpose or not?

Error in example

Hi,

Running the example gives me this error.

.pio\libdeps\az-delivery-devkit-v4\SerialTransfer\src\SPITransfer.cpp: In member function 'uint8_t SPITransfer::available()': .pio\libdeps\az-delivery-devkit-v4\SerialTransfer\src\SPITransfer.cpp:111:29: error: 'SPDR' was not declared in this scope volatile uint8_t recChar = SPDR;

Any idea what could cause this? I am using an esp32 with platformio if that matters.

ValueError: bytes length not a multiple of item size

I'm trying to receive an array of uint16_t from the Arduino.

This is how I send from the Arduino:

    uint16_t sendSize = 0;
    uint16_t header[3] = {1, 1, 6};
    sendSize = myTransfer.txObj(header, sendSize);
    myTransfer.sendData(sendSize);

This is what I used to received on the Python side:

        header = link.rx_obj(obj_type=list,
                    obj_byte_size=3*2,
                    list_format="i")

And this is the error I get:

 File "C:\ProgramData\Miniconda3\lib\site-packages\pySerialTransfer\pySerialTransfer.py", line 287, in rx_obj
    arr = array(list_format, buff)
ValueError: bytes length not a multiple of item size

I tried debugging it by adding print statements in the library, and it fails at this point in pySerialTransfer.py:

            elif list_format:
                arr = array(list_format, buff)
                return arr.tolist()

This is the content of the buff variable when the data is received from the Arduino:
b'\x01\x00\x01\x00\x06\x00'

If I change the "list_format" variable to 'b', then it works, however it interprets th \x00 as individual 0s instead of combining two bytes together. This is what I get:

>>> array('b', b'\x01\x00\x01\x00\x06\x00')
array('b', [1, 0, 1, 0, 6, 0])

I would expect this:

>>> array('i', b'\x01\x00\x01\x00\x06\x00')
array('b', [1,  1, 6])

This is happening on Python 3.7 64 bit
Any ideas?

Arduino File Example

Hello,

I am quite new to sending large amounts of data over Serial, but I have found myself in need of doing it for a project. I am using an Ardunio MKR1000 and I am currently trying to get the Loren Ipsum file transfer example working on my computer. There was a small syntax error on line 34 of the rx_file.ino (a missing paren) but otherwise the code is unchanged.
I am wondering how I can actually see that the file is being transferred since the MKR has only one serial (and software serial does not seem to be supported easily). I tried using the [uart_rx_file.ino example] from the "sister" arduino library but I can't see anything printing and really have no clue if it is working at all. Any guidance would be extremely appreciated!

How to transmit large array of floats fast and reliably?

Hi PowerBroker,

Thank you for your great library. I'm using it to transmit a big array of floats with more than 1000 elements between the laptop and an Arduino Nano BLE board (bi-direction).

I successfully make the transmission work by formatting each float as a string and sending each element one by one. However, it was a bit slow when sending from the laptop to the board (more than 15 seconds).

Now, I chunk the array to fit the packet size of 254 bytes and try to send 63 floats each time. In this way, I try to minimize sending frequency with the hope to increase speed.

However, the transmission is not unreliable now. Some packets are not received by the board and got lost.

Could you please help me?

Arduino code:

#include <Arduino.h>
#include "SerialTransfer.h"

#define weightsSize 1153 // Unit is byte
#define transferSize 254
#define floatSize 4

// ---------------------------------- Serial transfer parameter ---------------------------------------

SerialTransfer myTransfer;
bool train = false;
bool receive = true;  // First, receive weights from the server
bool startReceive = false;
bool send = false;
int weightIndex = 0;
char numString[11];
char command[6];
char floatWeight[11];
int receiveFloatsPerRound = transferSize / floatSize;
int receiveRound = int(weightsSize / receiveFloatsPerRound) + 1; 
int currentReceiveRound = 0;
int lastRoundFloatsNumber = weightsSize - (receiveRound - 1) * receiveFloatsPerRound;
// float floatsHolder[int(transferSize / floatSize)]; 
float flatWeights[1153] = {0};
// float myFloat = 0;
// -------------------------------------------------------------------------------------------

void setup() {
  Serial.begin(115200); //115200 baud rate (If necessary, change in the serial monitor)
  myTransfer.begin(Serial);
  delay(2000);
}

void loop() {

  
    // Send the weights
    if(send){

      myTransfer.sendDatum("start");
      delay(500);

      for(int i = 0; i<weightsSize; i++){
        snprintf_P(floatWeight, 11, "%.6f", flatWeights[i]);
        uint16_t sendSize = myTransfer.txObj(floatWeight[0], 0, 11);
        myTransfer.sendData(sendSize, 1);
        delay(1);
      }

      myTransfer.sendDatum("end");
      send = false;
      receive = true;
      weightIndex = 0;
    }

    if(receive){
      if(myTransfer.available()){

        if(!myTransfer.currentPacketID()){
          myTransfer.rxObj(command);

          if(strstr(command, "end")){
            weightIndex = 0;
            train = true;
            startReceive = false;
            receive = false;
            currentReceiveRound = 0;
          }else if(strstr(command, "start")){
            startReceive = true;
          }
        }
        
        if(myTransfer.currentPacketID() == 1 and startReceive){
            float floatsHolder[int(transferSize / floatSize)]; 
            myTransfer.rxObj(floatsHolder);
            if(currentReceiveRound == receiveRound-1){ // The last round
              for(int j=0; j < lastRoundFloatsNumber; j++){
                flatWeights[currentReceiveRound*receiveFloatsPerRound + j] = floatsHolder[j];
              }
            }else{
              for(int j=0; j < receiveFloatsPerRound; j++){
                flatWeights[currentReceiveRound*receiveFloatsPerRound + j] = floatsHolder[j];
              }
            }
            currentReceiveRound++;
        } 
      }
    }

    if(train)    
    {
      train = false;
      send = true;
      for(int i=0;i<weightsSize;i++){
        flatWeights[i] = flatWeights[i] * 2;
      }
    }
}

Python code:

import random
import time
from pySerialTransfer import pySerialTransfer as txfer
import numpy as np

start_sending = True
com_name = 'COM10'
weights = [np.float16(random.random()) for w in range(1153)] // Here an array with a size of 1153

receiveFloatsPerRound = int(254 // 4)
receiveRound = len(weights) // receiveFloatsPerRound + 1
lastRoundFloatsNumber = len(weights) - (receiveRound - 1) * receiveFloatsPerRound

try:
    start_receive = False
    # Open a COM given its name
    link = txfer.SerialTransfer(com_name)
    link.open()
    time.sleep(2)

    t = 0
    print("Open link...")
    while True:
        if link.available() and not start_sending:
            if not link.idByte:
                state_indicator = link.rx_obj(str, obj_byte_size=link.bytesRead)
                # Central is notified for the start of receiving weights from the device
                if "start" in state_indicator:
                    print('Elapsed time between two rounds (with device) is {:.2f} seconds...'.format(time.time() - t))
                    t = time.time()
                    print("Start receiving...")
                    start_receive = True
                # Central is notified for the end of receiving weights from the device
                if "end" in state_indicator and start_receive:
                    print("End receiving...")
                    print('Elapsed time for receiving is {:.2f} seconds...'.format(time.time() - t))
                    print("Weights received...: ")
                    print(weights)
                    print("length: {}".format(len(weights)))
                    start_receive = False
                    if len(weights) == 1153:
                        print("Devide weights by 2...")
                        weights = [w / 2 for w in weights]
                    start_sending = True
            else:
                if start_receive:
                    # Receive the weights as a string one by one
                    next_content = link.rx_obj(str, start_pos=0, obj_byte_size=link.bytesRead)
                    weights.append(float(next_content.replace('\x00', '')))

        if start_sending:

            # Notify the device for receiving incoming weights
            link.send(link.tx_obj("start"))
            time.sleep(2)
            t = time.time()
            print("Start sending...")
            for i in range(receiveRound):
                if i == receiveRound - 1:
                    list_size = link.tx_obj(weights[i*receiveFloatsPerRound:i*receiveFloatsPerRound+lastRoundFloatsNumber])
                    print(weights[i*receiveFloatsPerRound:i*receiveFloatsPerRound+lastRoundFloatsNumber])
                    link.send(list_size, packet_id=1)
                else:
                    print(weights[i*receiveFloatsPerRound:(i+1)*receiveFloatsPerRound])
                    list_size = link.tx_obj(weights[i*receiveFloatsPerRound:(i+1)*receiveFloatsPerRound])
                    link.send(list_size, packet_id=1)
                time.sleep(0.1)
            weights.clear()
            link.send(link.tx_obj("end"))
            print("End sending...")
            print('Elapsed time for sending is {:.2f} seconds...'.format(time.time() - t))
            t = time.time()
            # After send weights to the device, toggle flag
            start_sending = False

        if link.status < 0:
            if link.status == txfer.CRC_ERROR:
                print('ERROR: CRC_ERROR')
            elif link.status == txfer.PAYLOAD_ERROR:
                print('ERROR: PAYLOAD_ERROR')
            elif link.status == txfer.STOP_BYTE_ERROR:
                print('ERROR: STOP_BYTE_ERROR')
            else:
                print('ERROR: {}'.format(link.status))


except KeyboardInterrupt:
    print("KeyboardInterrupt")
    link.close()
except:
    import traceback

    traceback.print_exc()
    print("Error")
    try:
        link.close()
    except:
        pass

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.