Sending a String from Raspberry Pi to Arduino via XBee modules - python

[SOLVED] the problem was with the USB-TTL PL2303 chip I was using to interface the XBee module with the Pi. It was creating the problem. It's drivers were not properly supported by the RPi2.
I am trying to send a string (possibly a number) from a python script on my Raspberry Pi2 through a XBee module connected to it, to an Arduino Uno. The data sent is being misinterpreted at the Arduino end. When I use the terminal on X-CTU and send strings through that it shows up correctly on the serial monitor of Arduino IDE.
Here is the Python Code I am using
import time
import serial
ser = serial.Serial("/dev/ttyUSB0",9600)
ser.isOpen()
x= '4'
ser.write(bytes(x, "ascii")) #writing as bytes
time.sleep(2)
ser.close()
Here is the Arduino code I used
#include <SoftwareSerial.h>
SoftwareSerial mySerial(10, 11); // RX, TX
void setup()
{
// Open serial communications
Serial.begin(9600);
// set the data rate for the SoftwareSerial port
mySerial.begin(9600);
}
void loop() // run over and over
{
int x;
if (mySerial.available())
{
x = char(mySerial.read()) - '0';
//reading data value from Software Serial port
//converting ASCII to int
//and storing it as x
Serial.print(x);
}

My guess, if you are already typecasting to char, you don't need to subtract '0', because then Serial.print() will interpret that value as an ascii code and print the corrosponding character. So just try, char(Serial.read()) and print it.

Well, you know the code on the receiving end is working correctly because you can test it by sending data with the X-CTU terminal. How are you sending from X-CTU? Just typing the number 4 in the window, or sending it as a hex value (0x04)?
What happens when you have the Python script send to the X-CTU terminal? What do you see? What if you just dump the value of the byte read on the Arduino side before doing any conversions to it? Compare what X-CTU sends to what Python sends.
Instead of using bytes() to convert your Python string, you could just assign x = b'4' and see what that does.

Related

Python not printing same as Arduino Serial Monitor

I am trying to read some float values from the flash memory of an ESP32. They are stored one per line. I want to plot these values in Python, but the readings from its serial monitor vs Arduino's are all different.
Arduino sample code:
while(file.available()){
num = file.parseFloat();
str = String(num, 3);
Serial.println(str);
//Serial.println(num);
}
Serial.println("\nDone");
Python code:
while(1):
line = ser.readline() # read a byte string
if line:
string = line.decode() # convert the byte string to a unicode string
if ("Done" in string):
break
#num = float(string) # convert the unicode string to a float
print(string)
#print(num)
I tried both printing the float directly in Arduino as well as converting it to string, both work well in Arduino's Serial Monitor, neither works for the Python reading.
In the photo you can see some values are just thrash (ex -0.50.000). Any ideas for a fix? Thank you!
In the end I solved this by changing the baudrate down from 115200 to 9600.

How to write and read binary (not ASCII encoded) to Arduino with pyserial

I need to make a Raspberry Pi communicate with an Arduino. I will only need values between 0 and 180, and this is within the range of a single byte so I want to only send the value in binary, and not send it with ASCII encoding for faster transfer (i.e.: if I want to write a "123" to the Arduino, I want 0x7B to be sent, and not the ASCII codes for 1, then 2, then 3 (0x31,0x32, 0x33).
In my testing I have been trying to write a Python program that will take an integer within that range and then send it over serial in binary.
Here is a test I was trying. I want to write a binary number to the Arduino, and then have the Arduino print out the value it received.
Python code:
USB_PORT = "/dev/ttyUSB0" # Arduino Uno WiFi Rev2
import serial
try:
usb = serial.Serial(USB_PORT, 9600, timeout=2)
except:
print("ERROR - Could not open USB serial port. Please check your port name and permissions.")
print("Exiting program.")
exit()
while True:
command = int(input("Enter command: "))
usb.write(command)
value = (usb.readline())
print("You sent:", value)
And here is the Arduino code
byte command;
void setup()
{
Serial.begin(9600);
}
void loop()
{
if (Serial.available() > 0)
{
command = Serial.read();
Serial.print(command);
}
}
All this gives me is this:
Enter command: 1
You sent: b'0'
Enter command: 4
You sent: b'0000'
usb.write(command) expects command to be of type bytes not int.
It seems the write method internally calls bytes(), since it sends the number of zero bytes that you called it with.
Calling bytes() with an integer, creates a zero filled byte array, with the specified number of zeroes.
If you want to send a single byte, you need to call it with a list with a single element.
You should do:
command = bytes([int(input("Enter command: "))])

I²C bytes received out of order using Raspberry Pi Python SMBus

I am setting up a Raspberry Pi to record data (CO2, humidity, and temperature) from the Sensirion SCD30 sensor. My code is in Python 3, using the SMBus library to communicate with the sensor over the I²C pins in the Raspberry Pi's GPIO. There is a command to determine whether the sensor is ready to send data.
Link to SCD30 interface datasheet
Link to SCD30 library for Arduino by Sparkfun
The value 0x0202 is sent over I²C, and three bytes of data are returned:
0x00 0x00 0x81 for data not ready
0x00 0x01 0xB0 for data ready
The first two bytes are the MSB and the LSB of the data ready value. Combined properly, they should be 0x0000 and 0x0001.
The third byte is the CRC8 of the first two bytes. This is calculated with a polynomial of 0x31 and an initialization of 0xFF.
About half the time, the bytes are sent in the wrong order. Instead of MSB LSB CRC it is sent MSB CRC LSB. For example, if the data is ready it might send 0x00, 0xB0, 0x01 instead of 0x00, 0x01, 0xB0. I can't figure out why this is happening, and I'm concerned there is some corruption or issues in sending data. I could change the code to recognize if the CRC is the second byte, but I would like to find the underlying problem.
I am sending and receiving I²C data using the smbus library. This is my code to send commands and read data:
bus = smbus.SMBus(0)
I2C_ADDRESS = 0x61
def sendCommand(self, cmd): # Sends a 2 byte command (cmd)
data = [0]*2
data[0] = cmd >> 8 # Splits 2 byte command into MSB and LSB
data[1] = cmd & 0xFF
bus.write_i2c_block_data(I2C_ADDRESS, data[0], data[1:])
def readRegister(self, reg, length): # Sends 2 byte command (reg) and receives (length) bytes
sendCommand(reg)
data = bus.read_i2c_block_data(I2C_ADDRESS, 0, length)
return data
For the example I gave above, I would run the following code:
ready = readRegister(0x0202, 3) # Returns a list of 3 bytes
print(ready)
And it would return a list of the three bytes demonstrated above.
What sort of tools and skills have you got?
(My first reaction was to search for SCD30 errata, but I can't find any, nor does quick web search reveal any similar problems.)
If you have an oscilloscope or logical analyser, look at SCL and SDA, and confirm the problem is on Raspberry Pi (could be on the sensor as well).
Can you replace any hardware components of the setup - just to eliminate the odd chance of something being faulty.
Can you rewrite the code in C (using /dev/i2c-x), and see if the problem still persists - this would either tell you problem is either in the kernel driver for the I²C master, wiring, SCD30 chip or in library smbus or other software that sits between your piece of code and the kernel driver.
Good luck
The SMBus library is not right, as the SCD30 requires longer I²C commands than the Linux i2c-dev library provides.
We are successfully using pigpiod for talking via Python to the sensor. Our code for the SCD30 and install instructions (as well as solutions for the clock stretching issue) can be found here on GitHub.
It could be a pull-up resistor problem.
See I²C Bus Pull-up Resistor Calculation
Pull-up Resistor Calculation
A strong pull-up (small resistor) prevents the I²C pin on an IC from being able to drive low. The VOL level that can be read as a valid logical low by the input buffers of an IC determines the minimum pull-up resistance [RP (min)]. RP (min) is a function of VCC, VOL (max), and IOL.
Rp(min) = (VCC - VOL(max)) / IOL
The maximum pull-up resistance is limited by the bus capacitance (Cb) due to I²C standard rise time specifications. If the pull-up resistor value is too high, the I²C line may not rise to a logical high before it is pulled low. The response of an RC circuit to a voltage step of amplitude VCC, starting at time t = 0 is characterized by the time constant RC.
Using SMBus for this I²C device is not right.
You should compare the SMBus 6.5.7 Block Read (which is read_i2c_block_data, if I am right) to your device datasheet 1.3.4 Get data ready status. They are different as the SMBus sends the command byte.
The device interface looks simple enough for you not to need a library. Just open the i2c-dev, do the I2C_SLAVE ioctl to set address, and then write of two bytes and read of three bytes. The kernel I²C device interface document tells the details.
I2C_SLAVE=0x703
with open('/dev/i2c-1', 'r+b') as f:
fcntl.ioctl(f, I2C_SLAVE, 0x61)
f.write(b'\2\2')
data = f.read(3)
To get the ioctl I2C_SLAVE value, compile a small C program to print it.
#include <stdio.h>
#include <linux/i2c-dev.h>
int main() { printf("%#x\n", I2C_SLAVE); return 0; }
Something like cc file.c && ./a.out to see the value.
The datasheet does say in 1.1 "the master has to support clock stretching." Without clock stretching you must be prepared for errors. There are ways to have clock stretching on Raspberry Pi using i2c-gpio. I'm sorry I did not have time to go through the details here, but Google finds at least some instructions.

Reading integer from arduino using pyserial

I am sending integer value from arduino and reading it in Python using pyserial
The arduino code is:
Serial.write(integer)
And the pyserial is:
ser=serial.Serial ('com3',9600,timeout =1)
X=ser.read(1)
print(X)
But it doesn't print anything except blank spaces
Does anyone knows how to read this integer passed from arduino in Python?
You probably need to use a start bit.
The problem might be that the arduino has already written the integer by the time pyserial is running?
So write a character from pyserial to arduino to signal start like
ser=serial.Serial ('com3',9600,timeout =1)
ser.write(b'S')
X=ser.read(1)
print(X)
And write the integer from the arduino once you get this start bit.
That is the not the right way to read an Integer from Arduino. Integer is a 32-bit type while your serial port would be set to EIGHTBITS (Both in pyserial and Arduino. Correct me if I'm wrong) in byte size, therefore you have to write the Character version of the Integer from Arduino while transmitting it through a Serial Port because a Character takes only EIGHTBITS in size which is also the convenient way to do the stuff that you need to, very easily.
Long story short, convert your Integer into a String or a Character Array before transmitting. (Chances are there are inbuilt functions available for the conversion).
On a side note here is the correct python code that you'd prefer to use:
ser = serial.Serial(
port='COM3',
baudrate=9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS
)
#RxTx
ser.isOpen()
while 1:
out = ''
while ser.inWaiting() > 0:
out += ser.read(1)
if out != '':
print ">>Received String: %s" % out
An easy program which I tested:
Arduino:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
}
void loop() {
int f1=123;
// print out the value you read:
Serial.println(f1);
delay(1000);
}
Python:
import serial
ser = serial.Serial()
ser.baudrate = 9600
ser.port = 'COM5'
ser.open()
while True:
h1=ser.readline()
if h1:
g3=int(h1); #if you want to convert to float you can use "float" instead of "int"
g3=g3+5;
print(g3)

Pyserial testing

I am new to Pyserial and Hardware area. I am trying to run the sample applications given in http://pyserial.sourceforge.net/shortintro.html#opening-serial-ports
import serial
ser = serial.Serial(0) # open first serial port
print ser.portstr # check which port was really used
ser.write("hello") # write a string
ser.close()
I have written this program in a python file and running it. Now if I want to test this application to check if I am sending correct string (eg: Hyperterminal or something) how can I do this. Can anyone guide me?
Use virtual serial port for your test.
For Windows I use com0com and socat for Linux.
Then, use Putty for visualization of your send.
Another quick way to test a physical serial port is to take a wire/screwdriver, crocodile clamp or anything that you have in hand and bridge the RX and TX (receive and transmit) together. At that point, everything that you send out will be looped back at you. YOu can receive it afterward by using this code snippet here:
import serial
ser = serial.Serial(0, timeout = 1) # open first serial port
print ser.portstr # check which port was really used
ser.write("hello") # write a string
msg = ser.read("100") #read the content of the input buffer until you get 100 byte or a timeout event
print(msg) #print the content you might need to decode it print(decode(msg))
ser.close()
The key aspect again for this code to work properly is to bridge RX and TX together. A lot of tutorial will show you how to do this.

Categories

Resources