I want to display a string on an Arduino LCD 16x2 using python, but I’ve encountered problems with serial communication.
Here is the code running in Arduino:
Arduino Code
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
String stringa;
const unsigned long TimeOut = 10; // timeout 10 ms
String stringa1;
String stringa2;
void setup() {
lcd.begin(16, 2);
Serial.begin(9600);
}
void loop() {
stringa = "";
unsigned long T = 0; // timer
T = millis(); // timer running
while (millis() - T < TimeOut) {
// waiting timeout
while (Serial.available() > 0) {
// receiving Serial
stringa += char(Serial.read()); // add char
T = millis(); // reset timer
}
}
if (stringa.length() > 32) {
lcd.setCursor(0, 1);
lcd.print("stringa length: " + stringa.length());
delay(2000);
lcd.print(" ");
} else {
stringa1 = stringa.substring(0 , 16);
stringa2 = stringa.substring(16);
lcd.setCursor(0, 0);
lcd.print(stringa1);
lcd.setCursor(0, 1);
lcd.print(stringa2);
delay(5000);
}
}
It works perfectly with Serial communication from Keyboard provided in Arduino IDE. But it doesn't work when I try to send a string using the Python script below:
Python Code
import serial
import sys
import time
arduino = serial.Serial('COM3', 9600, timeout=0)
stringa = 'hello'
arduino.write(bytes(stringa,'utf-8'))
arduino.close()
Where is the problem? I can't find a solution! Thanks.
Take a look at the difference between the timeouts in the C file above and the python script below.
The timeout is 10 milliseconds in your C file wheareas it’s 0 in your Python script. Also check the result of the arduino.write() to make sure that it was successful.
Possibly implement something like the following:
import serial
import sys
import time
arduino = serial.Serial('COM3', 9600, timeout=10)
stringa = 'hello'
try:
arduino.write(stringa.encode())
except OsError:
print "Write failed!"
arduino.close()
If this does not work then try checking the serial ports between both the C file and the Python script. Make sure they are the same. Hope this helps!
Related
I am trying to send float values from Python on Windows to an Arduino. The first problem I encounter is that I cannot view the serial monitor when I think I am sending data from my Python script. I have read online that this is becuase only one application can manage the port at once: https://forum.arduino.cc/t/serial-communication-only-working-when-serial-monitor-is-opened/601107/12
However I have seen examples where the user is viewing the serial monitor to see data coming in over serial from Python and serial.print outs from the Arduino. So I am unsure what is the case... not being able to view the serial monitor sure does make debugging this senario hard.
My Python code:
import struct
import serial
import time
print('test123')
x=0.8
y=0.2
ser = serial.Serial('COM5', 9600, timeout=1)
#print(ser)
time.sleep(3)
def sendmess():
bin = struct.pack('ff',x,y) #Pack float value into 4 bytes
data = ser.write(bin)
#print(bin)
#print(len(bin))
#print([ "0x%02x" % b for b in bin])
#ser.close()
while True:
sendmess()
My Arduino Code:
int d = 250;
float incomingByte = 1;
void setup() {
// initialize the serial communication:
Serial.begin(9600);
pinMode(2, OUTPUT);
}
void loop() {
digitalWrite(2, HIGH);
delay(d);
digitalWrite(2, LOW);
delay(d);
// reply only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
d = 1000;
float incomingByte = Serial.read();
// say what you got:
Serial.println(incomingByte);
}
else{
Serial.println(incomingByte);
d = 20;
}
}
I see the LED flash every second so I know the serial buffer is >0, but I cannot get any data out :(.
Thanks in advance!
Mark
I have tried examples online, but I never get any data to display in the serial monitor. Nor can I say have a pin turn HIGH when I think I am getting the data I think I have sent. But without the Serial Monitor how can I debug this?
Unfortunately, you aren't able to connect more than two devices to the serial connection. However, using Python you should be able to read a response from your arduino and then print that to the python Terminal (or a file) and take a look at that.
The easiest way to do that would be to use pySerial and either ser.read(4) to read back 4 bytes of your float or ser.readline() which will look for either a '\n' which you can add to your arduino code, or a timeout. Then just print it to the python terminal with print().
As for actually reading the float, Serial.read() will only read in the next byte in the buffer. Since a float is 4 bytes long, you need to either read the 4 bytes into an array or use Serial.parseFloat()
Python Code
import struct
import serial
import time
x=0.8
y=0.2
ser = serial.Serial('COM5', 9600, timeout=1)
#print(ser)
time.sleep(3)
def sendmess():
bin = str(x) + str(y) #Pack float value into 4 bytes
data = ser.write(bin.encode())
echo = ser.readline()
print("Echo: " + echo)
#ser.close()
while True:
sendmess()
Arduino Code:
int d = 250;
float X;
float Y;
char buffer[40];
void setup() {
// initialize the serial communication:
Serial.begin(9600);
pinMode(2, OUTPUT);
}
void loop() {
digitalWrite(2, HIGH);
delay(d);
digitalWrite(2, LOW);
delay(d);
// reply only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
d = 1000;
X = Serial.parseFloat();
Y = Serial.parseFloat();
// say what you got:
sprintf(buffer, "X: %f Y: %f", X, Y);
Serial.println(buffer);
}
else{
Serial.println(buffer);
d = 20;
}
}
I have two programs that communicate through a serial USB interface with a CNC machine running grbl. The first program, written in Python using the pyserial library, produces intelligible output:
b'ok\r\n'
b'ok\r\n'
The second program, written in C using the libserialport library produces mangled output:
Found port: /dev/ttyUSB0
in:
in: M3 S100
out: �]�
in: M3 S0
out: �WH�
I've been staring at this for days trying to find substantive differences that could explain why the Python program works and the C one doesn't, but I haven't come up with anything yet. Both programs flush their input buffers, both programs send the same data with the same line endings, and both programs wait for the same amount of time in the same places. Any ideas on what I am doing wrong?
Python program:
import serial
import time
s = serial.Serial('/dev/ttyUSB0', 115200)
s.write(b"\r\n\r\n")
time.sleep(2)
s.flushInput()
s.write(b'M3 S100\n')
out = s.readline()
print(out)
time.sleep(1)
s.write(b'M3 S0\n')
out = s.readline()
print(out)
time.sleep(2)
s.close()
C program:
#include <libserialport.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
struct sp_port **port_list;
struct sp_port *main_port;
void ports_init() {
sp_list_ports(&port_list);
for (int i = 0; port_list[i] != NULL; i++) {
struct sp_port *port = port_list[i];
char *port_name = sp_get_port_name(port);
printf("Found port: %s\n", port_name);
if (strcmp(port_name, "/dev/ttyUSB0") == 0) {
sp_open(port_list[i], SP_MODE_READ_WRITE);
sp_set_baudrate(port_list[i], 112500);
main_port = port_list[i];
return;
}
}
}
void send_command(char *c) {
sp_blocking_write(main_port, c, strlen(c), 1000);
printf("in: %s\n", c);
}
void read_response() {
char hi[256];
bzero(hi, 256);
sp_blocking_read(main_port, hi, 255, 1000);
printf("out: %s\n", hi);
}
int main() {
ports_init();
send_command("\r\n\r\n");
usleep(2 * 1000 * 1000);
sp_flush(main_port, SP_BUF_BOTH);
send_command("M3 S100\n");
read_response();
usleep(1 * 1000 * 1000);
send_command("M3 S0\n");
read_response();
usleep(2 * 1000 * 1000);
}
You have a typo. In your python program you use 115200 as your baud rate, but in your C program you use 112500. Just change this line:
sp_set_baudrate(port_list[i], 112500);
to
sp_set_baudrate(port_list[i], 115200);
I am attempting to use pyserial to read the data from an arduino on Windows.
import serial
device = 'COM3'
baud = 9600
with serial.Serial(device,baud, timeout = 0) as serialPort:
while True:
line = serialPort.readline()
line = line.decode("utf-8")
if line:
print(line)
void setup() {
Serial.begin(9600);
}
void loop() {
int x = 12;
int y = 34;
int z = 56;
Serial.print(x);
Serial.print(',');
Serial.print(y);
Serial.print(',');
Serial.println(z);
}
The arduino Serial monitor is outputting exactly what I expect.
12,34,56
12,34,56
12,34,56
The python script on the other hand is outputting:
1
2,34
,56
12,
34,5
6
1
2,34
,56
12,
34,5
6
I have tried delaying the output from the Arduino, I have tried making a buffer in the arduino code and only output the data when the buffer was full, thinking maybe python would have time to read it correctly.
I have see numerous people on this site and others make similar code and suggest it works fine, I however cannot get coherent data from python. Anyone know my issue?
Try to do like this
Python
import serial
device = 'COM3'
baud = 9600
with serial.Serial(device, baud) as port:
while True:
print(port.readline().decode("utf-8"))
Arduino
void setup() {
Serial.begin(9600);
}
void loop() {
int x = 12;
int y = 34;
int z = 56;
Serial.println(x + ',' + y + ',' + z);
}
I am trying to send an int number from Python to an Arduino using PySerial, using .write([data]) to send with Python and Serial.read() or Serial.readString() to recieve on the Arduino, then .setPixelColor() and .show() to light a LED on a matrix which position corresponds to the int sent by the arduino (I am using the Duinofun Neopixel Shield).
But It does not seem to work properly, and I can't use the Serial Monitor as I am sending my data as the port would be busy.
I have tried to input a number myself using Serial.readString() then converting the string to an int and finally putting in into my function that displays the LED.
It does work properly when I do this, but when I send some data over, all the previously lit LEDs suddenly switch off which can only be caused by a reset of the Arduino board as far as I know.
This is the python code, it simply sends an int chosen by the user
import serial
a = int(input('Enter pixel position : '))
ser = serial.Serial("COM16", 9600)
ser.write([a])
And this is the part of the Arduino program that reads the incoming data.
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(40, 6, NEO_GRB + NEO_KHZ800);
void setup() {
// put your setup code here, to run once:
pixels.begin();
Serial.begin(9600);
}
void loop() {
String a = Serial.readString();
int b = a.toInt();
pixels.setPixelColor(b, 30,30,30);
pixels.show();
Serial.println(a);
delay(1000);
}
All the LED switch off when I send some data, except the first LED which position corresponds to a 0 used in the .setPixelColor() function.
Problem is, the LED should light to the corresponding int sent by Python (e.g light the fifth LED for an int of 4).
You don't need to send an int from your Python script. Just send a string and then convert it back to int on your Arduino. Also, you can verify the number simply on your Arduino code if the received value is valid.
Another problem with your Arduino code is you are not checking the Serial port availability which would return an empty string by Serial.readString().
A simple approach is shown below but you can extend it for other pixels.
Python script:
import serial
ser = serial.Serial("COM16", 9600)
while True:
input_value = input('Enter pixel position: ')
ser.write(input_value.encode())
Arduino code:
#define MIN_PIXEL_RANGE 0
#define MAX_PIXEL_RANGE 100
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(40, 6, NEO_GRB + NEO_KHZ800);
void setup()
{
// put your setup code here, to run once:
pixels.begin();
Serial.begin(9600);
}
void loop()
{
if (Serial.available())
{
String a = Serial.readString();
Serial.print("Received Value: ");
Serial.println(a);
int b = a.toInt();
if ((b >= MIN_PIXEL_RANGE) && (b <= MAX_PIXEL_RANGE))
{
pixels.setPixelColor(b, 30, 30, 30);
pixels.show();
delay(1000);
}
}
}
You can communicate between Ardinos and Python really easily and reliably if you use the pip-installable package pySerialTransfer. The package is non-blocking, easy to use, supports variable length packets, automatically parses packets, and uses CRC-8 for packet corruption detection.
Here's an example Python script:
from pySerialTransfer import pySerialTransfer as txfer
if __name__ == '__main__':
try:
link = txfer.SerialTransfer('COM13')
link.txBuff[0] = 'h'
link.txBuff[1] = 'i'
link.txBuff[2] = '\n'
link.send(3)
while not link.available():
if link.status < 0:
print('ERROR: {}'.format(link.status))
print('Response received:')
response = ''
for index in range(link.bytesRead):
response += chr(link.rxBuff[index])
print(response)
link.close()
except KeyboardInterrupt:
link.close()
Note that the Arduino will need to use the library SerialTransfer.h. You can install SerialTransfer.h using the Arduino IDE's Libraries Manager.
Here's an example Arduino sketch:
#include "SerialTransfer.h"
SerialTransfer myTransfer;
void setup()
{
Serial.begin(115200);
Serial1.begin(115200);
myTransfer.begin(Serial1);
}
void loop()
{
myTransfer.txBuff[0] = 'h';
myTransfer.txBuff[1] = 'i';
myTransfer.txBuff[2] = '\n';
myTransfer.sendData(3);
delay(100);
if(myTransfer.available())
{
Serial.println("New Data");
for(byte i = 0; i < myTransfer.bytesRead; i++)
Serial.write(myTransfer.rxBuff[i]);
Serial.println();
}
else if(myTransfer.status < 0)
{
Serial.print("ERROR: ");
Serial.println(myTransfer.status);
}
}
Lastly, note that you can transmit ints, floats, chars, etc. using the combination of these libraries!
I am facing problems, that pyhton throws me on my raspberry pi 3 sometimes this IOError during starting a script which is requesting data from an Arduino over I2C.
Electrical connection is perfect so this is not the issues.
Furthermore I also dont get any errors while using i2cget -y 1 0x04
Only the python scripts sucks sometime and I dont know why.
This is my Arduino Code:
I register an onReceive and an onRequestEvent.
onReceive Callback will define what kind of data should be send back to the raspberry.
onRequest Callback does the response.
#include <CommonFunction.h>
#include <Wire.h>
#define I2C_ADDRESS 0x4
commonFunc GetCountsEverySecond;
int g_iOnRequestActionCode = 0;
unsigned long g_lSecondsSinceStart = 0;
void setup()
{
Wire.begin(I2C_ADDRESS);
Wire.onRequest(sendDataOverI2CGateway);
Wire.onReceive(defineOnRequestAction);
}
void loop()
{
tickSeconds();
}
void tickSeconds()
{
if (GetCountsEverySecond.TimeTriggerAt(1000))
{
g_lSecondsSinceStart++;
}
}
void sendOperationTimeDataOverI2C()
{
unsigned long longInt = g_lSecondsSinceStart;
byte size = sizeof(longInt);
byte arr[size];
for (int i = 0; i < size; i++)
{
int iBitShift = 8 * (size - i - 1);
if (iBitShift >= 8)
arr[i] = ((longInt >> iBitShift) & 0xFF);
else
arr[i] = (longInt & 0xFF);
}
Wire.write(arr, size);
g_bI2CSending = true;
}
void sendDataOverI2CGateway()
{
switch(g_iOnRequestActionCode)
{
case 0:
sendRainDataOverI2C();
break;
case 1: // send firmware version
sendVersionDataOverI2C();
break;
case 2: // send operation time of arduino in seconds from start
sendOperationTimeDataOverI2C();
break;
default: break;
}
}
void defineOnRequestAction(int iBuffer)
{
while (Wire.available())
{
g_iOnRequestActionCode = Wire.read();
}
}
Here is my python Code.
Pretty straight forward but it causes some headache.
import smbus
import time
bus = smbus.SMBus(1)
while True:
data = bus.read_i2c_block_data(0x04,0x02,4)
result = 0
for b in data:
result = result * 256 + int(b)
print(result)
time.sleep(1)
After executing my python script I am getting sometime this error:
pi#WeatherStation:~/workspace $ sudo python readTimeOperationData.py
Traceback (most recent call last):
File "readTimeOperationData.py", line 5, in <module>
data = bus.read_i2c_block_data(0x04,0x02,4)
IOError: [Errno 121] Remote I/O error
Can anyone help me to fix this issue?
Cheers Dieter
I solved it!!
I got a hint from this post:
https://www.raspberrypi.org/forums/viewtopic.php?t=203286
By adding a delay after bus = smbus.SMBus(1) solved this issue.
It seems that a short delay is somehow needed so that the I2C can settle.
Working Code tested by calling script 100times without issues.
import smbus
import time
bus = smbus.SMBus(1)
time.sleep(1) #wait here to avoid 121 IO Error
while True:
data = bus.read_i2c_block_data(0x04,0x02,4)
result = 0
for b in data:
result = result * 256 + int(b)
print(result)
time.sleep(1)
This worked for me
import smbus
import time
bus = smbus.SMBus(1)
time.sleep(1)
if you use RPLCD library, add this code before initialize the object
bus = smbus.SMBus(1)
time.sleep(1) #wait here to avoid 121 IO Error
lcd = CharLCD('PCF8574', 0x27)
lcd.cursor_pos = (0,0)
thank you