Pyserial readline() shows mix up of bits - python

I am very new to Arduino and Python. I'm working on the communication between an Arduino Uno and my laptop using Pyserial. The Python script in my laptop is called by an H3D Viewer, which is linked to a Novint Falcon.
My laptop sends the positional data of the Falcon to the Arduino, to be sent to the robot. Right now I'm merely trying to read back the data I sent from the Arduino, to make sure everything works. But it doesn't!
Here is part of my Python script (which is called by a .x3d file).
dp_text, = references.getValue()
class display (threading.Thread):
def __init__(self, delay):
threading.Thread.__init__(self)
self.delay = delay
def run(self):
while 1:
receivedFalconData=[]
tex=[]
tex.append("DevicePosX = " + str(localFalconData))
receivedFalconData=ser.readline()
ser.flush()
ser.flushInput()
tex.append("Input = " + str(receivedFalconData))
dp_text.string.setValue(tex)
time.sleep(self.delay)
displaying = display(0.1)
displaying.start()
The above thread is in charged of refreshing the text node in the H3D Viewer every 100ms. The Arduino's code is below:
#include <SoftwareSerial.h>
char Axispos[6] ={0,0,0,0,0,0};
int xvalue= 0;
SoftwareSerial mySerial(7,6); //RX, TX
void setup() {
Serial.begin( 9600 );
Serial.flush();
mySerial.begin(9600);
}
void loop(){
while( Serial.available() > 0 )
{
Serial.readBytes(Axispos,6);
Serial.flush();
xvalue = (Axispos[0] << 8) + (Axispos[1] & 0xff);
Serial.println(xvalue);
Serial.flush();
mySerial.write(Axispos);
mySerial.flush();
delay(500);
}
}
The x-axis value read by the Arduino is sent back to the laptop and stored in receivedFalconData. This value is supposed to be the same as localFalconData, which is the x-axis value in the script itself.
The results: receivedFalconData mimics localFalconData for a few seconds, before going wild.
It sometimes shows the correct value again, though, which tells me this is probably an issue with the Pyserial input buffer overflow? How could this be the case, since (if I'm correct) serial.readline() empties the input buffer after reading it? Furthermore, the Arduino only sends data every .5 second, whereas the laptop reads every .1 second.
But when the readline() part is placed in the class for sending data (which is called whenever the Falcon has new data available), receivedFalconData is perfectly correct.
Also, I get two of these ☐ symbols behind receivedFalconData when it is displayed in H3D...
Can someone who has experience with Pyserial please please help me? I would gladly give more info if my problem is still unclear to you.

Related

Pyserial only communicate to Arduino when there is a user input

My Python and Arduino code together are producing the most baffling behaviour. I am running a test to see if Pyserial is writing to and reading from my Arduino correctly, but it seems like Pyserial will only write to Arduino when a user input is taken. Note, this user input doesn't even need to be the message that is sent to the Arduino, but as long as such input is taken, whatever message that I meant to send to the Arduino will send. If no user input is taken, it seems that either Pyserial is not writing to Arduino, or Arduino is not responding to the serial input.
Here is my python code:
import serial
import time
arduino = serial.Serial(port='/dev/cu.usbmodem2101', baudrate=9600, timeout=.1)
def write_read(x):
#arduino.write(bytes(x, "utf-8"))
arduino.write(b'V6Com9P500zI0zO2zR50zG0zF8zT4zN1zC0r0zZ')
arduino.flush()
data1 = arduino.readline()
time.sleep(0.1)
data2 = arduino.readline()
return data1, data2
#num = input("Enter a number: ") # This is the trigger that allows the serial communication
message ="V6Com9P500zI0zO2zR50zG0zF8zT4zN1zC0r0zZ" # dummy message
value1, value2 = write_read(message)
value1 = value1.decode("utf-8").strip("\r\n")
print(value1) # printing the value
print(value2)
And here is my Arduino code:
int DAC = 1000;
int Velec = 300;
String dataread = "";
boolean readcomplete = false;
String inChar;
void setup() {
Serial.begin(9600);
Serial.setTimeout(1);
}
void serialEvent() {
while (Serial.available()) {
inChar = Serial.readString();
dataread += inChar;
if (inChar == "Z") {
readcomplete = true;
Serial.println(dataread); // a testing point
}
}
}
void loop() {
while (readcomplete) {
Serial.println(DAC);
Serial.println(dataread);
readcomplete = false;
}
}
Those are just testing code, I took out the lines that are irrelevant to this question. The Arduino code works fine by itself, if I manually enter the serial input in the serial monitor.
As I said earlier, it works fine if the 'num' input is not commented out in the Python code:
Enter a number: 8
V6Com9P500zI0zO2zR50zG0zF8zT4zN1zC0r0zZ
b'1000\r\n'
I don't even need to enter a number, I can basically enter anything, and it will work. But as soon as I comment out the 'num' line in the Python code, there will be no output, value1 and value2 in the python code will return empty, even though 'num' isn't even used anywhere else in the code:
b''
(first line is an empty line because it is decoded and stripped of \r\n)
I've tested this on both my Arduino Uno and Arduino Due, so it is not the board that is the problem. I am so confused, please accept my sincere gratitude for anyone that can shed even the tiniest of light on this problem.
One possible cause that I have encountered when using PySerial and Arduino is that the serial communication takes a small amount of time to be initialized, but in the provided code, it goes straight to the arduino.write() line. I'd try adding a time.sleep() of about 10 secs before writing and reduce the delay if it works.
I'd have added this as a comment if I could but I don't have enough reputation, yet.

Why would QtSerialPort not read after running more than once or twice?

I am just trying to read values from a QtSerialPort connection to an arduino after a value was went to signify the start. I could have a maximum count in the python code, but that doesn't change the outcome of it getting not reading the println from arduino after running the python script more than once. At first it'll print out 'start', 'to read', and 'V', and maybe again if I run it again soon. But after that, it stops reading after printing out 'start'. Here is the python code.
from PyQt5 import QtCore, QtSerialPort
import sys
app = QtCore.QCoreApplication([])
serial_port = QtSerialPort.QSerialPort('COM3')
serial_port.open(QtCore.QIODevice.ReadWrite)
def handle_ready_read():
print('to read')
while serial_port.canReadLine():
serialrd = serial_port.readLine().data().decode().strip()
print(serialrd)
serial_port.close()
app.quit()
serial_port.readyRead.connect(handle_ready_read)
serial_port.write(bytes([100]))
print('start')
sys.exit(app.exec_())
And here is the arduino code.
int measured;
int counter = 0;
int maxnum = 10;
void setup()
{
Serial.begin(9600);
}
void loop()
{
if(Serial.available() > 0)
{
while(counter < maxnum)
{
Serial.println("V");
counter++;
delay(100);
}
}
}
Any ideas that would explain why it won't read again would be useful. I have to unplug and replug in the arduino each time to get it to read, but avoiding having to do that is preferable. I also notice I can't run a PyQt5 serial port connection after a pyserial connection, with disconnecting the arduino first, but I can run pyserial after a PyQt5 serial port connection, though I don't know if that issue is related somehow.
Edit: I added in the 'to read' print statement in handle_ready_read, which sometimes would print out twice when running the first time, so though there is something to be read, it's not always readable by readline, so I added in to handle_ready_read at the end it,
while serial_port.bytesAvailable():
dataByte = serial_port.readLineData(1)
print(dataByte)
serial_port.close()
app.quit()
which will thus sometimes print out b'V' when that is read instead of just V. But it still doesn't read after one or two tries.
Edit 2: I added a timer and function to check if the bytes were written with,
attempts = []
def check_bytes_written():
print('bytes to write' , serial_port.bytesToWrite())
attempts.append(1)
if len(attempts) > 10:
serial_port.close()
timer.stop()
app.quit()
timer = QtCore.QTimer()
timer.setInterval(50)
timer.timeout.connect(check_bytes_written)
timer.start()
This doesn't print out anything with the first or second try, and then says 'bytes to write 0' with later tries, still not printing out any form of 'V', even though the port and app were ended, unless I still disconnect the arduino and plug it back in (at least the kernal doesn't need to be restarted each time now though). I also added code into the arduino to check if the value written can be used to change the voltage of an output pin, it works and the output voltage is changed each time the python script is run, whether a 'V' is read or not. So I changed the question title a bit and previous text, since it's not really crashing or freezing, it's just not reading what the arduino is printing.
Edit 3: I put if(Serial.available() > 0) { int counter = 0; in the arduino code, instead of initializing the counter at the start, the python code will read from the arduino when rerunning the script (though with a bunch of spaces around the V, or just printing b'\x00' from the readlineData part, but that may be another issue, which is cleared by putting serial_port.clear() before the readyRead, but then either V or b'V' is still printed). So it seems like the python script is not restarting the arduino code, maybe it is an issue with how qserialport closes or opens (like with DTR or RTS), unlike how pyserial does? More is discussed here.
Edit 4: If I add (while initializing the counter at the start in the arduino code, as originally shown, undoing the last edit)
serial_port.setDataTerminalReady(1)
serial_port.setDataTerminalReady(0)
serial_port.setDataTerminalReady(1)
after the serial_port.open statement and run it, it still doesn't print from the serial read (without disconnecting the arduino and reconnecting it first), but then if I comment the DTR sets out, then a V is printed out as expected when running again, no disconnect needed, but then it goes back to not printing V. The same effect occurs using just serial_port.setDataTerminalReady(1), commenting it in/out every other time.
Edit 5: I found that (once it has run properly at least once, by unplugging first or using the method in edit 4), if I put
serial_port.setDataTerminalReady(0)
serial_port.setDataTerminalReady(1)
serial_port.setDataTerminalReady(0)
right before serial_port.close() it can be run again multiple times and print out 'V'. I guess it is resetting the DTR to 0. If there is an explanation of why that needs to happen, that would be good to know.
Is your function logic indented correctly?
To me it looks like you read a single line and immediately close the serial port and quit your app.
Perhaps you intended:
def handle_ready_read():
while serial_port.canReadLine():
serialrd = serial_port.readLine().data().decode().strip()
print(serialrd)
# Close serial port and quit app when no more data is available
serial_port.close()
app.quit()

reducing delay on raspberry pi to arduino serial communication

I'm currently trying to rotate a motor on my Arduino Uno through serial communication from my Raspberry Pi 3. My code itself currently works however sometimes when running the python script the motor will not turn or indicate any response. From what I've been able to find online, I feel like I'm sending signals faster than the Arduino is reading them, and I can't seem to find a way to minimize the delay and make my motor response consistent.
Here is my Arduino code:
#include <Stepper.h>
#define STEPS 128
Stepper stepper(STEPS, 8, 10, 9, 11);
void setup() {
Serial.begin(9600);
}
void loop() {
if (Serial.read() == 50) {
stepper.setSpeed(8); // rpm
stepper.step(128); // do n steps -- corresponds to one revolution in one minute
}
else if (Serial.read() == 51) {
stepper.setSpeed(8); // rpm
stepper.step(-128); // do n steps -- corresponds to one revolution in one minute
}
}
Here is my Python code
import serial
import time
ser = serial.Serial('/dev/ttyACM0', 9600)
time.sleep(1)
num="3"
ser.write(bytes(num.encode()))
Also I'm not sure how Arduino is reading my ser.write, as when num = 2, I see "50" in my Serial Monitor, when num = 3, "51" appears in my Serial Monitor, and so forth.
Thank you!
Serial communication is always done in Bytes. So whatever you send through your interface will be received as a sequence of Bytes. As you encoded your "3" as UTF-8 it will be sent as 0x33 (51). Your "2" is 0x32 (50) respectively.
Increasing the baudrate as suggested in a comment won't help you as it will only increase the speed data is transmitted/received. Without a measurement you won't notice a difference betweeen sending a single byte with 9600 or 115200 baud.
As long as both device operate on the same baudrate and you do not exceed the max baudrate of any device (somewhere around 2 million baud for Arduino Uno) you cannot send to fast. (given a suitable cable and distance)
You might run into problems with too long cables but that's several meters for 9600 baud even in noisy industrial environment.
Usually you wait for data to be available in the receive buffer befor you read.
void loop() {
// send data only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();
// say what you got:
Serial.print("I received: ");
Serial.println(incomingByte, DEC);
}
}
Maybe not doing so causes some delay. I cannot tell for sure as you did not provide the length of the observed delay.
If the Arduino is already running when you execute your Python code I don't see any other issue with your code. If it is booting while you send you might observe some delay because of the Arduino bootloader. It will wait some time for possible firmware updates befor it starts the actual application code.
Small pieces of data may sit around in the output buffer before being sent. Try using Serial.flush() after write to let the os know you want the data sent asap.

Arduino Sketch works with Serial Monitor but not with pyserial

I am testing this simple arduino code in python but it works in arduino serial not in python.
User defines the number of blinks on the led.
This works on arduino serial monitor. But when I use it in python it does not work. can anyone help?
Thank you
Arduino code:
int ledPin = 13; // select the pin for the LED
int val = 0; // variable to store the data from the serial port
void setup() {
pinMode(ledPin,OUTPUT); // declare the LED's pin as output
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect (USB)
}
establishContact();
}
void establishContact(){
while (Serial.available() <= 0){
val = Serial.parseInt();
Serial.flush();
// Serial.println("Est");
}
}
void loop () {
if (val>0) {
for(int i=0; i<val; i++) {
digitalWrite(ledPin,HIGH);
delay(150);
digitalWrite(ledPin, LOW);
delay(150);
}
val=0;
}
}
Python code:
import serial
import time
ser = serial.Serial('/dev/tty.usbmodem1421', baudrate=9600,timeout =None)
def blinkLED(x):
ser.write(x)
return;
First, I'll assume that somewhere in your code you do call
blinkLED('10')
otherwise this entire discussion is pointless.
On this regard, I'd change your function blinkLED(x) as follows:
def blinkLED(x):
ser.write(x)
ser.flushOutput()
return
Second, I am not fully convinced by this code:
1: void establishContact() {
2: while (Serial.available() <= 0){
3: val = Serial.parseInt();
4: Serial.flush();
5: }
6: }
First of all, I am not exactly sure what you think that Serial.flush() is supposed to do there, since according to the documentation it "Waits for the transmission of outgoing serial data to complete", but you are not sending anything on Serial output.
Second, when you are at line 2: at the first loop iteration, there are two possible cases:
A. something is available on Serial input, therefore Serial.available() > 0, you don't enter the loop and you don't read val
B. nothing is available on Serial input, therefore Serial.available() == 0, and you enter the loop. Now there are two sub-cases:
B.1. if nothing arrives on Serial input, you keep reading 0 forever, and remain stuck in that loop
B.2. if something arrives on Serial input, there are 3 sub-cases:
B.2.I. the input data arrives right after you execute Serial.parseInt(), so at the next loop iteration Serial.available() <= 0 is false and you exit the loop without reading your val (that is, you end up in case A.)
B.2.II. the input data arrives right when you execute Serial.parseInt() and you successfully parse all of the input bytes into val. However, nothing remains within the Serial input, so the condition Serial.available() <= 0 still holds and you remain stuck in the loop
B.2.III. the input data arrives right when you execute Serial.parseInt() and you successfully parse some of the input bytes into val. A few more bytes that do not pertain an Int value (e.g. \r, \n, \t, space, alphabetic symbols, ...) are ignored by Serial.parseInt() and remain in the Serial input buffer. Therefore at the next loop iteration Serial.available() <= 0 is false and you exit the loop.
When you call blinkLED('10') you end up in one of the following cases: A., B.2.I. or B.2.II.. This explains why you don't see any blinking: either you are still stuck in the loop, or you got past it without reading anything and you still have val == 0.
Case B.2.III. is the only situation in which you end up with a working sketch. This is what happens when you use the Arduino serial monitor, since the latter sends an additional \n (or \r\n? I don't remember..) by default when you press enter on your keyboard.
So I think this explains why your sketch works when you use the serial monitor, but not when you use the python code. A quick test would be to modify blinkLED(x) as follows:
def blinkLED(x):
ser.write(x)
ser.write('\n')
ser.flushOutput()
return
Note: the fact that using the serial monitor works on a few tests, or that even pyserial might work with this fix, doesn't mean that your sketch is now correct and that it is always going to work. In fact, the code might still fail, e.g. if Serial.available > 0 too soon then you still don't enter the loop body and parse val.
#ArnoBozo proposed changing establishContact() as follows:
1: void establishContact() {
2: while (Serial.available() > 0){
3: val = Serial.parseInt();
4: Serial.flush();
5: }
6: }
I think that this design is flawed as well, because again you have no guarantee that by the time you check Serial.available() > 0 the python counterpart has already sent the data (or that it was received). If this is not the case, the loop body is simply not executed and you never parse val. Sure, you can try playing with delay(), but that makes the entire sketch quite fragile.
A final observation: if you look at the documentation of Serial.parseInt(), you find that:
Parsing stops when no characters have been read for a configurable time-out value, or a non-digit is read;
If no valid digits were read when the time-out (see Serial.setTimeout()) occurs, 0 is returned;
If you check the documentation of Serial.setTimeout() you find out that the timeout "defaults to 1000 milliseconds". Again, at the cost of repeating myself and appearing pedantic, one should not rely on timeouts and delays in a communication protocol unless strictly necessary (e.g. to heuristically decide that it is time to free resources allocated to communicate with an external entity that is no longer participating to the communication).
Thus, my advice is to either scratch Serial.parseInt() and write your own parser, or use it in a more robust way wrt. the goal you have in mind:
Serial.setTimeout(0); // disables timeout
while (val == 0) { // discard any 'garbage' input
val = Serial.parseInt(); // keeps trying to read an Int
}
This approach is rather brutal (but YOLO): Arduino won't stop trying to parse an int different from 0 until when it gets one. Again, you should send an invalid digit after your number (e.g. \n) because otherwise Serial.parseInt() won't return since timeout is now equal to 0.
(note that I didn't test this code, it might as well not work if I misinterpreted some parts of the library documentation.)
I think you python script calls blinkLED() somewhere and the message is received in establishContact() on your arduino.
But you should have written Serial.available() > 0 (how many bytes are waiting to be read in incoming buffer).
You can erase following lines:
while (!Serial) { } // DOES NOT wait for serial port to connect (USB)
Indeed Serial object is instanciated at compile time and immediately returns (except with one board: Leonardo ?).
Serial.flush() empty outgoing buffer you are not using here; it does nothing on incoming buffer.
In your python script when you open serial port to arduino, this will surely reset arduino board; that will last less than 2s. So:
python can wait that arduino sends you a hello messsage.
or python can wait for 2s, before sending blinkLED() message.
If you open your arduino IDE serial monitor, it resets your arduino board, but you can not type in and send message before the board is ready, so you dont see the problem.
What happens with that bugged code:
while (Serial.available() <= 0){
val = Serial.parseInt(); }
It enters the while loop if there is no data waiting on serial. Then the Serial.parseInt() will be waiting a timeout of 1s for incoming data. If nothing comes, it will return 0.

python doesn't read data sent from arduino correctly

i was working on a project that includes sending 4 characters from arduino to python so that the first variable in python reads the first sent character and the second variable reads the second character but when i print them on the screen it's not stable and out of sync.
i.e:
i send 'a','b','c','d',and i expect to receive the same in python : 'a','b','c','d'.
but what i receive might be like this : 'c','d','a','b' or 'b','c','d','a'.
Arduino
`
void setup()
{
Serial.begin(9600);
}
void loop()
{
Serial.write('a');
Serial.write('b');
Serial.write('c');
Serial.write('d');
}
`
Python
ser = serial.Serial('COM4',9600,timeout=0)
ser.close()
ser.open()
up = ser.read()
right = ser.read()
left =ser.read()
down = ser.read()
I'm guessing it is because there is no synchronization between the two. The ardi sits there and spits out a,b,c,d,a,b,c,d,a,b,c,d etc. When the python code connects, there's nothing to make the ardi start at 'a', it just keeps doing with it's doing and the python code gets data starting with whatever the ardi is on at the time. Notice in your problem description how the characters are always in order, if you allow for a repeating cycle?
Try having the python code (when it starts) to send something to the ardi so that it restarts the sequence from the beginning.

Categories

Resources