Go Down

Topic: Reading an int value from Arduino with pySerial (Read 2 times) previous topic - next topic

marco26

Mar 03, 2012, 11:43 am Last Edit: Mar 03, 2012, 01:58 pm by marco26 Reason: 1
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 :

Code: [Select]
// 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 :

Code: [Select]
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 :

Code: [Select]
['\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 !

PaulS

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.

robtillaart

Just as PaulS said:

Arduino sends 2 bytes.
Code: [Select]

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


Python must read 2 bytes and merge them
Code: [Select]

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. <bytebyte> 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.''

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

marco26

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 :

Code: [Select]
'\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

PaulS

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.

robtillaart


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 :)



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 -

Code: [Select]

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



Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

marco26


robtillaart


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

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Go Up