问题
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;
回答1:
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, thereforeSerial.available() > 0
, you don't enter the loop and you don't readval
- B. nothing is available on
Serial
input, thereforeSerial.available() == 0
, and you enter the loop. Now there are two sub-cases:- B.1. if nothing arrives on
Serial
input, you keep reading0
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 iterationSerial.available() <= 0
is false and you exit the loop without reading yourval
(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 intoval
. However, nothing remains within theSerial
input, so the conditionSerial.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 intoval
. A few more bytes that do not pertain an Int value (e.g.\r
,\n
,\t
, space, alphabetic symbols, ...) are ignored bySerial.parseInt()
and remain in theSerial
input buffer. Therefore at the next loop iterationSerial.available() <= 0
is false and you exit the loop.
- B.2.I. the input data arrives right after you execute
- B.1. if nothing arrives on
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.)
回答2:
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.
来源:https://stackoverflow.com/questions/42113932/arduino-sketch-works-with-serial-monitor-but-not-with-pyserial