Arduino Sketch works with Serial Monitor but not with pyserial - python

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.

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.

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

Controlling blinking of LED based on value returned by a function

I wrote a code to blink LED, based on the value returned by a function. If the function "blinktheLED" returns TRUE, then the LED have to blink and if the value returned is False, then the LED don't blink. What i wanted to do is, I want the LED to blink or not blink based on the value returned by the function. Currently when i run the code, LED just blink once and stops rather than keep on blinking as the function is returning TRUE . Any suggestions will be highly appreciated.
set_accepted_list = set(['1','3','112','29'])
set_list_LED_id = set(['1','3'])
def blinktheLED(set_accepted_list,set_list_LED_id):
if len(set_accepted_list & set_list_LED_id) >0:
print"yes,open the gate" #it will print, when i run the code
return True
else:
print"no,donot open the gate"
return False
def code_controlling_gate(True):
connected = False
ser = serial.Serial("COM11",9600)
while not connected:
serin = ser.read()
connected = True
ser.write('1')
while ser.read() == '1':
ser.read()
ser.close()
Arduino code:
void setup() {
Serial.begin(9600);
pinMode(10,OUTPUT);
Serial.write('1');
}
void loop() {
Serial.read();
if(Serial.available()>0){
digitalWrite(10,HIGH);
delay(500);
digitalWrite(10,LOW);
delay(500);
}
else{
Serial.available()==0;
}
}
Well it looks as though your Arduino loop blinking code is running based only on the condition Serial.available() and has no connection at all to the value that might be coming off the serial port. Your Serial.read() is at the end of that block; it should be at the beginning and you should blink or not blink based on the value you receive.
I'm not sure that's the only problem here, but certainly you need to make that change to make it work as you describe.
More tips:
Make sure your serial sender is sending what you think by using a terminal emulator or a trusted device to verify that you're sending what you think you're sending. You can tinker with the Arduino side all day long and if your sender isn't sending the right data, you'll just be frustrated. Another PC and a null modem cable can be invaluable here.
Make sure all the serial parameters are the same. speed, parity, databits all have to match. Check both sides of the connection to make sure. I don't see anywhere in your code where you're setting databits, parity or stopbits.
Once you get this working, if you have other projects that will use serial communications, consider making an Arduino that does just this -- make it a trusted device so that you can rule out these problems with a trusted device. I have a microcontroller on a permanent board set up to do exactly this, blink when it's getting 9600 bps serial traffic. That's my RS232 Hello World" device. ;)
On the Arduino side, consider commenting out the conditional code around the blinking so that the loop does nothing but blink the LED the way you want. That way you know at least that is working. Like:
digitalWrite(10,HIGH);
delay(500);
digitalWrite(10,LOW);
delay(500);
Finally, in your code, you're doing a Serial.read() but not saving the value anywhere to compare it. You only need one character to save off a '1' or '0' so assign the result of Serial.read() to a variable and then act on its value in your loop.

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