Calling an arduino function through pyserial in python - python

I am trying get an ultrasonic reading from a sensor thats connected to an arduino and the function which runs that sensor is in the ardunio sketch however I want to be able to call that function through a python script when I need to ? Can anyone help ?

well its fairly simple.. first, you need pyserial library with proper setup (correct COM port and baud). Then in arduino in main function, add a code like:
int incoming;
void loop() {
if(Serial.available() > 0) {
incoming = Serial.read();
}
if(incoming == 1) {
yourFunction();
} else {
errorFunction();
}
}
expecting value "1" to be send from python script. If its sent and received by arduino, yourFunction() will be executed, otherwise errorFunction() will be. Change if(incoming == 1) to anything you want to.

Related

sending serial data to arduino works in serial monitor, but not in python

I'm trying to flip a single bit on my arduino from 0 to 1 via python script. The following arduino code works great to turn on an LED if I type 1 into the serial monitor and hit enter:
int x;
void setup() {
// this code proves that the LED is working
digitalWrite(7, HIGH);
delay(300);
digitalWrite(7, LOW);
Serial.begin(115200);
}
void loop() {
Serial.print(x);
if(Serial.available()){
x = Serial.parseInt();
// if x is anything other than 0, turn the LED on
if (x){
digitalWrite(7, HIGH);
}
}
}
but when I try to use this python script, the variable x presumably stays 0 because the LED isn't turning on:
import serial
import time
arduino = serial.Serial(port='COM3', baudrate=115200, timeout=5)
time.sleep(5)
print(arduino.read())
arduino.write(b"\x01")
print(arduino.read())
arduino.close()
I put the two print statements in to try to figure out what was happening, but I can't make sense of the output. Usually it's
b'0'
b'0'
but sometimes it's
b'0'
b''
or if I run the script right after plugging the arduino in it's:
b'\x10'
b'\x02'
or some other random number.
What am I doing wrong here?
Using bytes("1", "<encoding>") instead of b"\x01" might work, where encoding is the encoding of your python file (like utf-8), although I'm not sure what the difference is.
Another possible error cause: your baud rate is enormous. For something as simple as this, you don't need such a huge baud; using the standard 9600 will work fine. Try changing the baud and see if that helps.

Unable to send byte from Python serial.write() to Arduino

I wrote a python script that sends and receive data to and from an Arduino. I can receive data without problems, but when I send data it seems like the Arduino is receiving only garbage.
The Arduino is connected to a linux pc via USB and the pc is running Python 3.8.
Since the original code contains a lot of unrelated stuff and may be distracting, I prepared the simplest possible sketch to demontrate the problem:
Python code:
#! /usr/bin/env python
import serial
import time
if __name__ == '__main__':
ser = serial.Serial("/dev/ttyACM0", 9600, timeout=0)
time.sleep(5) # Wait for Arduino to reset and init serial
ser.write(67)
Arduino sketch
const byte LED_PIN = 13;
void setup() {
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
}
void loop() {
if (Serial.available() > 0) {
byte b = Serial.read();
if (b == 67) {
digitalWrite(LED_PIN, HIGH);
delay(500);
digitalWrite(LED_PIN, LOW);
}
}
}
This code flashes the onboard LED when receives a byte with value 67 (uppercase C). This works with the Arduino Serial Monitor, but not when running the Python script.
I feel like my problem may be related to this question, but in that case the focus was on the user not considering the case of an empty serial input buffer, while I believe the problem is actually in the sent data. This is why I decided to leave out the receiving part and simplify the example using only the onboard led.
Update
I made it work changing the last line in the python script to:
ser.write(bytes([67]))
(note the [] added around the integer value).
Anyone can explain why this syntax produces the correct result? It seems like I'm passing a single entry array to the function bytes().
Pardon my poor skills in Pyton, I know the question is probably basic.
It's really simple; the methods you tried that worked all conform to the specification for the pyserial library.
Per the Pyserial documentation:
write(data)
Parameters: data – Data to send.
Returns: Number of bytes written.
Return type: int
Raises: SerialTimeoutException – In case a write timeout is configured for the port and the time is exceeded.
Write the bytes data to the port. This should be of type bytes (or compatible such as bytearray or memoryview). Unicode strings must be encoded (e.g. 'hello'.encode('utf-8').
This is why the b'C style worked as well as the bytes call.
Attribution: Pyserial Page

Pyserial out_waiting always returns zero

I'm working on a simple project which requires me to write a Python script that will send data through a serial port from a PC to an Arduino. As part of this project I want to be able to check how many bytes are in the 64 byte input buffer of the Arduino. The issue is that when I try to write data and then use pySerial's out_waiting method, it always returns zero. This is a toy model I have been using for testing.
Arduino code:
void setup() {
Serial.begin(9600);
}
void loop() {
}
Python code:
import serial
import time
Arduino = serial.Serial('COM5',9600)
for i in range(20):
Arduino.write('1'.encode())
time.sleep(1)
print(Arduino.out_waiting)
Arduino.flushOutput()
Arduino.flushInput()
print(Arduino.out_waiting)
Arduino.close()
I would expect the first print statement to output 20 and the second to output 0. They both output 0, and I really can't figure out why.
Here is a link to the pySerial documentation: pySerial docs
Thanks in advance for any help.
you can initialise write_timeout=0 before start writing message to port

How to Read Data from Arduino with Raspberry pi via I2C

I have connected Raspberry pi 2 model B with arduino uno via Bi-Directional Level shifter.
Raspberry pi GND ---------- GND Arduino
3.3v ---------- 5v
SCL ---------- A5
SDA ---------- A4
Hope my I2C connection is correct ?
and my Arduino is connected to 8-Channel Relay Board.
Now I have written code in which I can control the Relay board by Raspberry pi. For ex if i Press '1' the Relay 1 goes high.
Now I want to send data back from arduino to raspberry pi in order to cross check if Relay 1 is high or not, if Relay 1 is high then it should send some data back to Raspberry pi or else not.
My Rpi code is
import smbus
import time
# for RPI version 1, use "bus = smbus.SMBus(0)"
bus = smbus.SMBus(1)
# This is the address we setup in the Arduino Program
address = 0x04
def writeNumber(value):
bus.write_byte(address, value)
# bus.write_byte_data(address, 0, value)
return -1
def readNumber():
number = bus.read_byte(address)
# number = bus.read_byte_data(address, 1)
return number
while True:
var = input("")
if not var:
continue
writeNumber(var)
number = readNumber()
My Arduino code:
#include <Wire.h>
#define SLAVE_ADDRESS 0x04
#define RELAY1 9
int number = 0;
int state = 0;
void setup() {
pinMode(RELAY1, OUTPUT);
Serial.begin(9600); // start serial for output
// initialize i2c as slave
Wire.begin(SLAVE_ADDRESS);
// define callbacks for i2c communication
Wire.onReceive(receiveData);
Wire.onRequest(sendData);
Serial.println("Ready!");
}
void loop() {
delay(100);
}
// callback for received data
void receiveData(int byteCount){
while(Wire.available()) {
number = Wire.read();
Serial.print("data received: ");
Serial.println(number);
if (number == 1){
if (state == 0){
digitalWrite(RELAY1, HIGH); // set the LED on
state = 1;
}
else{
digitalWrite(RELAY1, LOW); // set the LED off
state = 0;
}
}
}
}
// callback for sending data
void sendData(){
Wire.write(number);
}
Now if I type 1 and due to some loose connection Relay 1 doesn't goes high, So in this case I want the arduino to take data from relay board and send it to Raspberry pi every time.
It will be great if someone can explain also that how it works.
Hope I was able to explain the problem. I have done lots of research but was not able to find some answer.
I am a beginner in python so please help me.
Thanks in advance.
The problem is that you are doing too much inside receiveData, which is called from the Interrupt Service Routine of the I2C utility code, twi.c. You must handle the data quickly, and don't call any other routines that depend on interrupts being enabled (they are disabled during this ISR).
This means you can't call Serial.print, and you can't call any other Wire sending methods. Even calling millis() or micros() is discouraged, as they do take a fair amount of time, and they depend on the TIMER interrupts being handled.
Of course, you are free call Wire.available() and Wire.read(). Actually, byteCount tells you how many bytes are available, so you don't need to call Wire.available() again.
Essentially, your receivedData routine can read the data inside the routine if you're quick about processing it. Otherwise, you can only set a (volatile) flag and then watch for it in loop. From what I see in your sketch, you could do something like this:
// variables that allow signalling between receiveData ISR and loop
volatile bool newData = false;
volatile uint8_t state = false;
// callback for received data
void receiveData(int byteCount)
{
// Read all the bytes; only the last one changes the relay state
while (byteCount-- > 0)
number = Wire.read();
if (state != number) {
state = number;
newData = true;
}
}
// callback for sending data
void sendData(){
Wire.write(number);
}
void loop()
{
if (newData) {
newData = false; // clear the flag for next time
if (number == 1){
digitalWrite(RELAY1, HIGH); // set the LED on
} else {
digitalWrite(RELAY1, LOW); // set the LED off
}
Serial.print("data received: ");
Serial.println( number );
}
}
The delay in loop is unnecessary, and may cause problems if you add something else to loop.
The volatile keyword keeps the compiler from optimizing loop. Without that keyword, the test for newData in loop would disappear because the compiler thinks that newData doesn't change during loop. Why test it? volatile newData tells the compiler that newData can change at any time, like during the receiveData ISR.
And be sure to print the number in the rpi code, as pholtz suggested!
In arduino code
change sendData() function like this
void sendData(){
int relay_status;
relay_status=digitalRead(4);
Wire.write(relay_status);
}
Also in hardware connect one 4th digital pin(or any other free I/O pins) to relay input.
hope it helps:)
Ok, looks like a pretty good start. Two things I want to suggest here.
First, in your python program you should print number so that you can see it's value changing. It's storing your feedback from the Arduino, so you want to display that feedback to the screen. This is as simple as changing number = readNumber() to print readNumber().
Second, in your Arduino program, are you sure that calling Wire.read() returns what you think it does? It looks to me like read() returns a byte. Chances are that when you type 1 it's really being sent as '1', not 1. Char vs. Int. Make sense?
So you might want to check if(number == '1') instead. Just my 2¢.
Your i2c bus is not connected properly. Remove the level shifter, and add 4.7k pull-ups to the 3.3v vcc on scl and sda lines. I2c chips only drive the line low, and require external resistors to pull the line high. This lets you mix voltage levels on your i2c bus fairly easily. Then you can go back to figuring out what your code is doing.

sending serial commands to arduino

I've been trying to find a way to send serial commands from my PC (Windows 7) to an Arduino Uno R3. I've been working on this simple program, where you're supposed to send a simple "1" through the USB cable, in order to turn on the onboard LED. My Arduino code should be working (what I know of, but I'll make sure to upload it).
I've been trying to send this command using Python and pySerial, but I can't seem to get pySerial to work. Also I've tried using CMD, but it seems like CMD freezes when i type in my command (ECHO 1 > COM3 BAUD:9600).
I'm the adminstrator of my PC.
This is my arduino coding
int var = 0;
int LEDPin = 13;
int val = 0;
void setup() {
Serial.begin(9600);
pinMode(LEDPin, OUTPUT);
}
void loop() {
if (Serial.available()>0){
val = Serial.read();
}
if (val == 1){
digitalWrite(LEDPin, HIGH);
}
digitalRead(LEDPin);
if (LEDPin==HIGH){
var ++;
delay (1000);
if(var==10){
digitalWrite(LEDPin,LOW);
var = 0;
val = 0;
}
}
}
And the short Python program I've got :)
import serial
ser = serial.Serial('COM3', 9600, timeout=0)
while 1:
var = 1
ser.write(var)
delay(12000)
With Kind Regards
Michael Vedel.
just to be sure - is Arduino really connected to COM3?
In order to check serial connection manually you can use for example Putty. With help of it you can connect to the needed serial port and send command manually. And in order to check whether Arduino received it, you can add Serial.println(val); just after val = Serial.read();, with this Arduino should sends back to serial whatever characters it receives.
Does the code work when you type a '1' into the Serial Monitor in the Arduino IDE? If so, check the COM port. On my PC (also Windows 7), COM3 is never the Arduino port, as it is used internally for something else.
By the way everything after the digitalRead() statement will do nothing, since you're comparing LEDpin (which is declared as 13) to HIGH (which is defined as 1). I think you mean to see if the digitalRead output is HIGH, but I'm not sure that will work, either, since the pin is declared as an OUTPUT. You'd be much better off putting that logic in the (val == 1) section.
just save this code as .bat and make sure that you are using com 6.
This code lets you enter and sends your commands to serial port at 9600bpm
mode com6:9600,N,8,1
#echo off
:start
cls
Set /p commands="enter command:"
echo|set /p= %commands% >com6
goto start
Now you have to read serial using this in Arduino
Serial.read();
I think the root problem is in the Python code in the call to write. The argument has to be bytes (or something compatible). You're passing an integer value. You need to explicitly convert that value to a bytes object. I'm not a Python expert, but I think one way to do that is to replace:
var = 1
with
var = b'\x01'
Oh, and you might need to call flush as well.
BTW, your experiment with ECHO doesn't do what you intended. It sends the character value for '1', which is 49. Since 49 != 1, your Arduino code won't respond to it.
I find the easiest way to test serial commands is to use the Arduino Serial Monitor, which will have all the right defaults as long as you match the baud rate. This will enable you to ensure the Arduino code is right before debugging the Python code. (Then again, it may be difficult to send control characters with the Serial Monitor, so consider using printable text in your protocol. For example 'A' for on and 'B' for off. Printable text can be easy to work with among all these tools (Python, C, Serial Monitor, ECHO, etc.).)
do this it works better for me, just change the com4 for the port you have connected to
#echo off
:start
cls
set /p commands="enter command:"
mode com4 baud=115200 parity=n data=8 stop=1 to=on xon=off odsr=off octs=off dtr=off rts=off idsr=off && echo %commands% > com4
cls
goto start
I think Arduino is stable and easy enough to handle the data from serial, the cause of that as I used same library with Pyserial in python 3 was that event of reading the serial get fired once which is way faster than Arduino board and to solve that you should use a thread method that runs in the background and make it always try to read the incoming data from serial.
I've made this function that recieved the opened serial :
def read_from_port(ser):
while True:
try:
reading = ser.read().decode()
handle_data(reading)
except EXCEPTION:
print("error while decoding" + str(EXCEPTION))
after I have got the serial from the list and assigned it with the desired baud rate and send it to the Thread :
serial_me = serial.Serial(serial_port1, 115200, timeout=3)
try:
thread_x = threading.Thread(target=read_from_port, args= (serial_me,))
thread_x.daemon = True
thread_x.start()
except EXCEPTION:
print('couldn\'t start thread')
print(Exception)

Categories