Pyserial only communicate to Arduino when there is a user input - python

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.

Related

Connecting Python to arduino - check if string is equal to do task

I am passing a string from a computer with python(using pyserial) to my arduino uno and if it is equal to a set value, I need it to perform a task - I haven't even made it far enough to get the set values, I can't even get something to happen if it is passed at all. I just copied some of the blink code, to run if anything is received on serial and sometimes it works, and sometimes it doesn't, seemingly without reason. If anyone has an example of how to do this or knows where I am going wrong help would be appreciated.
Python Code
import serial
import time
def write():
arduino = serial.Serial('/dev/ttyUSB0', 9600, timeout=.1)
time.sleep(1) #give the connection a second to settle
arduino.write(str.encode("Hello from Python!"))
write()
Arduino Uno Code
void setup() {
Serial.begin(9600);
}
void loop() {
if(Serial.available() > 0) {
char data = Serial.read();
char str[2];
str[0] = data;
str[1] = '\0';
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000);
}
}
Add a delay(100) before your if(Serial.available() > 0) { statement.
More about this:
Your Arduino only has 1 core. If you do not have a delay() in your loop(), then the loop will take up 100% cpu usage all the time. This stops it from being able to execute background concurrent threads, e.g. to listen to Serial port, do bluetooth/wifi stuff, etc..
The delay() allows the main loop (or any thread) to sleep, so that the arduino's interrupt can execute other concurrent threads.

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.

Strange behavior with arduino-PC communication using pySerial

Problem 1:
I'm trying to read serial data coming from my arduino using pySerial.
Sometimes the code seems to work well but sometimes it prints strange
things. The arduino monitor always prints the normal data, the strange
behavior just happens on the python code.
Another thing: this just happens
when I increase the baudrate from 9600 to 115200, but I really need this
higher baudrate.
Output example:
Opening COM port...
Port successfully opened
b'7\r\n'
b'133\r\n'
b'125\r\n'
b'114\r\n'
b'104\r\n'
(...) (and suddenly here comes again)
b'\xbaj\xea2\x93j\n'
b'\xa6\x13j\n'
b'&\x13j\n'
b'&\x93j\n'
b'\xa6\xd3j\n'
(...)
[Finished in 2.0s]
Sometimes I need to cancel the build because it gets stuck
in the for loop.
Problem 2:
Besides that, after reading the data I need to convert it to int.
Just tried "int(data)" but obviously It didn't worked. Certainly because of the Problem 1.
When I change this:
print(data)
To this:
print(int(data))
I get this:
ValueError: invalid literal for int() with base 10: b'\xa1j\n'
Python Code:
import serial
print("Opening COM port...")
# open and prepare serial port
ser = serial.Serial('COM3', 115200, timeout=8,
parity=serial.PARITY_EVEN, rtscts=1)
print("Port successfully opened")
for i in range(50):
data = ser.readline()
print(data) # print(int(data))
ser.close()
Arduino Code:
void setup() {
Serial.begin(115200); // set baudrate
}
void loop() {
int data = 0;
int analogPin = 1;
data = analogRead(analogPin); // read pin
Serial.println(data, DEC); // print data to the serial port
}
Data validity suffers at higher data transfer speeds because the hardware can't read the data sent in the right sequence. Try lowering your baudrate to 57600.

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.

Pyserial readline() shows mix up of bits

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.

Categories

Resources