Reading an int value from Arduino with pySerial

Hi,

I am currently trying to read the speed of a DC motor with pySerial. It works well if I just pring the value on the serial monitor, but when I try to read it with Python is returns weirds hexadecimal values. Here is the part of the code on the Arduino :

// Return the motor speed
    if (input == 'g') 
    {
      // Init measurement values
      motor_speed_meas = 0;
      motor_speed_mean = 0;
     
      // Take 10 measurements and average 
      for (int i=0;i<10;i++) 
      {
        motor_speed_meas = motor_speed_meas + 500000/(float)pulseIn(7, HIGH);
      }
      
      motor_speed_mean = (int)(motor_speed_meas/10);
      
      // Write on the serial
      Serial.write(motor_speed_mean);
    }

And then the code I am using on Python :

import serial
import time

ser = serial.Serial('/dev/tty.usbmodemfd121', 9600)

time.sleep(1)

def read_speed():
	ser.write("g")
	time.sleep(0.01)
	speed = ser.read()

	return speed

I just made a test that gradually increases the speed of the motor, and measure the speed with the Python interface. Here is the result :

['\x90',
 '\xc5',
 '\xf5',
 '\x1b',
 '=',
 '\\',
 'v',
 '\x8d',
 '\xa2',
 '\xb4',
 '\xc3',
 '\xcf',
 '\xdd',
 '\xea',
 '\xf3',
 '\xfd',
 '\x05',
 '\x0c',
 '\x15',
 '\x1a']

This return values that doesn't make sense, whereas it is correct if I just use Serial.print() on the Arduino. What am I doing wrong ?

Thanks !

When you use Serial.write() to send data, it sends data in binary. The Serial class only has one write() method, and that method takes a uint8_t (also known as a byte). So, the most significant byte of your int gets discarded, limiting you to 256 values, from 0 to 255.

If you want to sent the int in binary, you need to send the most significant byte first, then the least significant byte, using two calls to Serial.write() (and the highByte() and lowByte() methods or bit shifting and masking).

On the python side, you need to read both bytes and put them back together.

Or, send the data as a string.

Just as PaulS said:

Arduino sends 2 bytes.

Serial.write(motor_speed_mean / 256);
Serial.write(motor_speed_mean % 256);

Python must read 2 bytes and merge them

import serial
import time

ser = serial.Serial('/dev/tty.usbmodemfd121', 9600)

time.sleep(1)

def read_speed():
	ser.write("g")
	time.sleep(0.01)
	speed = ser.read() * 256;
        speed = speed + ser.read()
	return speed

With binary data it is good practice to send delimiters with the stream. Start & stop symbols help to get the datastream in sync again if connection loses a byte
e.g. the < is a start char byte byte are 2 bytes and teh > is a stop char. A combination of >< identifies the begin of new data.
Furthermore it is good practice to send an identifier too.
< ID B B B B > e.g. some measurement that needs a long The first byte after the start symbol is the identifier byte. For speed this could be 'S' for RPM 'R' for Gas 'G' etc

A step further is to add a checksum ==> < ID B B B B C > The byte C is a value from a formula processing all bytes in one packet.''

Thanks a lot for the answers, I implemented the changed suggested by robtillaart, but I still have a last problem, here is an example of what I get in Python :

'\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01w'

How can I convert this back to a number ?

Thanks

Do you know enough about python to make it print each new value on a new line? It's hard to see what that mess corresponds to. We can't see over your shoulder, and see that you implemented rob's suggestions correctly, either.

You must add a counter how many bytes you received, and every 60 character you print a newline CRLF and reset the counter. (just like the good old lineprinter :slight_smile:

Furthermore it is not good programming t use time.sleep() in python, as you block the process manually. Furthermore it is overkill as serial.Read() allready blocks until it reads 1 byte (the Timeout flag is default set none) - http://pyserial.sourceforge.net/pyserial_api.html#module-functions-and-attributes -

import serial
import time

ser = serial.Serial('/dev/tty.usbmodemfd121', 9600)  -- default timeout = none

time.sleep(1)
def read_speed():
	ser.write("g")
	speed = ser.read(1) * 256;
        speed = speed + ser.read(1)
	return speed

Thanks to all, it works perfectly now !

Can you post your complete python script for reference...

well, thanks obama. This is literally issue what I have lol

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.