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

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.

Related

Learning Arduino and Raspberry Pi 4

I'm trying to learn how to send messages from the Raspberry Pi 4 to the Arduino Mega 2650 device via USB cable for use in a Jukebox project. I'm starting by doing some tutorials and I'm stumped as to a certain behavior. I hope someone can help me.
Here is the code on the Arduino Mega device:
#define LED_2_PIN 2
#define LED_3_PIN 3
#define LED_4_PIN 4
void powerOffAllLEDs() {
digitalWrite(LED_2_PIN, LOW);
digitalWrite(LED_3_PIN, LOW);
digitalWrite(LED_4_PIN, LOW);
}
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
pinMode(LED_2_PIN, OUTPUT);
pinMode(LED_3_PIN, OUTPUT);
pinMode(LED_4_PIN, OUTPUT);
powerOffAllLEDs();
}
void loop() {
// put your main code here, to run repeatedly:
if (Serial.available() > 0) {
int ledNumber = Serial.read() - '0';
powerOffAllLEDs();
switch (ledNumber) {
case 2:
digitalWrite(LED_2_PIN, HIGH);
break;
case 3:
digitalWrite(LED_3_PIN, HIGH);
break;
case 4:
digitalWrite(LED_4_PIN, HIGH);
break;
default:
// wrong pin number, do nothing
// all LEDs will be powered off
break;
}
}
}
If I run this code on the Raspberry Pi, I see one of the LED's flash quickly on and off.
import time, serial
ser=serial.Serial('/dev/ttyACM0', 9600, timeout=1)
ser.flush()
while True:
ser.write(b"4\n")
time.sleep(1)
If I run this code on the Raspberry Pi, I don't see any LED's light-up.
import time, serial
ser=serial.Serial('/dev/ttyACM0', 9600, timeout=1)
ser.flush()
ser.write(b"4\n")
time.sleep(1)
If I comment out the "powerOffAllLEDs()" just before the switch statement on the Arduino, then the light stays on with the first Raspberry Pi code. It still does nothing with the second.
Its almost like the "Serial.available()" is picking up stuff from the Raspberry Pi even though it shouldn't be sending anything.
This is obviously just a tutorial I'm trying to understand...ultimately I need to be able to send a two character (1 letter and 1 number - ie. "A1") code and have the LED stay on until I send another code.

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.

pySerial and Arduino communication

So far, I was exploring Python with Arduino using pySerial. I made some projects where pySerial reads something from the serial port.
import serial
arduinoSerialData = serial.Serial('com11',9600) #Create Serial port object called arduinoSerialData
while (1==1):
if (arduinoSerialData.inWaiting()>0):
myData = arduinoSerialData.readline()
print (myData)
int trigPin=13; //Sensor Trig pin connected to Arduino pin 13
int echoPin=11; //Sensor Echo pin connected to Arduino pin 11
float pingTime; //time for ping to travel from sensor to target and return
float targetDistance; //Distance to Target in inches
float speedOfSound=776.5; //Speed of sound in miles per hour when temp is 77 degrees.
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
}
void loop() {
// put your main code here, to run repeatedly:
digitalWrite(trigPin, LOW); //Set trigger pin low
delayMicroseconds(2000); //Let signal settle
digitalWrite(trigPin, HIGH); //Set trigPin high
delayMicroseconds(15); //Delay in high state
digitalWrite(trigPin, LOW); //ping has now been sent
delayMicroseconds(10); //Delay in low state
pingTime = pulseIn(echoPin, HIGH); //pingTime is presented in microceconds
pingTime=pingTime/1000000; //convert pingTime to seconds by dividing by 1000000 (microseconds in a second)
pingTime=pingTime/3600; //convert pingtime to hourse by dividing by 3600 (seconds in an hour)
targetDistance= speedOfSound * pingTime; //This will be in miles, since speed of sound was miles per hour
targetDistance=targetDistance/2; //Remember ping travels to target and back from target, so you must divide by 2 for actual target distance.
targetDistance= targetDistance*63360; //Convert miles to inches by multipling by 63360 (inches per mile)
Serial.println(targetDistance);
delay(100); //delay tenth of a second to slow things down a little.
}
For now, my code looks something like this. How can I make use of my ultrasonic sensor in Python and send signals to my Arduino program? In other words, can I make my Arduino file read from the Python file that I have or it is only one direction?
pySerial allows for communication over serial in both directions. On the Arduino side, you need to call Serial.read where appropriate to read data stream from Python. In Python, you just need to send data using the function serial.write. If using Python 3, input to serial.write needs to be in byte format.

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.

How to make Raspberry pi respond to Arduino code via USB connection

I have tried connecting Arduino to the raspberry Pi through USB cable. The Arduino board is connected to an Ultrasonic sensor and sends a serial message of either 0 or 1 based on whether it finds a barrier in a certain distance (very simple code). The problem is this : I'm trying to get the Raspberry Pi to read the Arduino code AND play a mp3 file at the same time but for some reason that doesn't seem to work ! I'm not sure if the problem lies in coding or If maybe it's impossible for Pi to respond to a message sent from Arduino to the serial monitor(It would be really sad if that's the case). Any help would be very appreciated
This is the Arduino Code (I'm using UNO board) :
/*
HC-SR04 Ping distance sensor:
VCC to Arduino
Vin GND to Arduino GND
Echo to Arduino pin 12
Trig to Arduino pin 11 */
#include <NewPing.h> //downloaded from the internet & unzipped in libraries folder in Arduino Directory
#define TRIGGER_PIN 11 // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN 12 // Arduino pin tied to echo pin on the ultrasonic sensor.
int maximumRange = 70; // Maximum range needed
int minimumRange = 35; // Minimum range needed
long duration, distance; // Duration used to calculate distance
void setup() {
Serial.begin (9600);
pinMode(TRIGGER_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
}
void loop() {
/* The following trigPin/echoPin cycle is used to determine the distance of the nearest object through reflecting soundwaves off of it */
digitalWrite(TRIGGER_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIGGER_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIGGER_PIN, LOW);
duration = pulseIn(ECHO_PIN, HIGH);
distance = (duration/2) / 29.1; //formula to convert the value measured by the ultrasonic sensor into centimeters
if (distance >= maximumRange || distance <= minimumRange)
{
Serial.println("0"); //means the path is clear
}
else {
Serial.println("1"); //means there is an obstacle in front of the ultrasonic sensor !
}
delay(50); //Delay 50ms before next reading.
}
And this is the python code that I used in my Pi (I have Raspberry Pi 2):
NOTE : I have commented the parts that don't work since I tried many different code combinations shown below
import serial
import RPi.GPIO as GPIO
import sys
import os
from subprocess import Popen
from subprocess import call
import time
import multiprocessing
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
arduinoSerialData = serial.Serial('/dev/ttyACM0', 9600)
while True:
time.sleep(0.01)
if(arduinoSerialData.inWaiting()>0):
myData = arduinoSerialData.readline()
print(myData)
if myData == '1': #THIS IS WHERE THE PROBLEMS START
#os.system('omxplayer sound.mp3') #tried this didn't work
#os.system('python player.py') #which is basically a python program with the previous line in it, also not working!
# I even tried enclosing that part (after if myData == '1') in a while loop and also didn't work !
Firstly, your IF condition doesn't look right. I don't see how distance <= minimumRange means the path is clear.
Next, you are writing a line to the serial port; a line that could be either 0\r\n or 1\r\n. Then you're reading a line from the Arduino, returning one of the two aforementioned possibilities. You're then comparing the line you've read, to 1. Neither 0\r\n nor 1\r\n is equal to 1, so its no surprise that the condition is never true. You can fix this in a number of ways:
Change the Serial.println() to Serial.print()
Change arduinoSerialData.readline() to arduinoSerialData.readline().rstrip()
Change the condition to if 1 in myData:
Another thing to remember is, read() returns a bytes object in Python 3 not a string as in Python 2. So any comparison involving literals should make sure to include the requisite b'' envelope. Like, if you strip the CRLF from the read data, your condition should instead be if myData == b'1':.

Categories

Resources