I'm attempting to write a handshaking procedure to allow my python program to automatically detect which port the Arduino is on. It works, however sometimes it runs extremely slow. Here's the problematic part of my code:
import serial, serial.tools.list_ports, time
portList = list(serial.toools.list_ports.comports())
conn = 0
while conn == 0:
for port in portList:
print(port[0])
try:
arduino = serial.Serial(port[0], 4800, timeout=.1)
time.sleep(.2)
sig = arduino.read()
signum = int.from_bytes(sig, byteorder='little')
if signum == 7:
global comport
comport = port[0]
conn = 1
break
except:
print('Could not read from ' + str(port[0]))
Essentially I have the Arduino constantly sending the arbitrary number '7' to the serial port. The python script scans each port for a 7 until it finds it. What's happening is that when it gets to the port that the Arduino is on, the code seemingly pauses executing for about 10 seconds at the arduino = serial.Serial(...) line right underneath the try statement. Since it's in a try loop, I know it's not throwing an error because it does eventually make it through. Is it just having trouble opening the port? How can this be fixed? I am on Python 3.4.3 and pySerial 2.7.
I think the issue is more with how arduino does serial ... it waits for a serial connection to open then it does a bunch of setup you can see this with a relatively simple arduino sketch
int i;
void setup(){
i=8;
Serial.begin(9600);
}
void loop(){
Serial.print(i);
Serial.print(",");
i+=1;
}
and I think that you will always see 8 as the first number when you connect to the port ... I dont have an arduino on hand but I seem to recall seeing this behaviour
Just to check: Is the baudrate the same for both your Arduino and Python? Are there any other programs trying to access the Arduino?
Related
I am writing a program to communicate between my laptop (Python) and an Arduino. The Arduino code, with which I have no issue, reads the serial data form my laptop and returns a reply. The code below works when I am calling the function which starts the serial communication from within the same file. However, when I import the file as a module in another file, using lal the same commands, it does not work!
To provide more detail, although Python thinks it has connected and even prints out the correct port number, it does not really connect. I know this because in the scenario that does work, when the serial communication is open, the Arduino IDE cannot speak to the Arduino as the Arduino is busy. However in the scenario which is not working, even after Python thinks it has opened serial communication, the Arduino IDE can still communicate with the Arduino.
Is there a way to pass the ser variable when called from a function in an imported module?
def connect():
for n in range(0,21):
try:
ser = serial.Serial('COM'+str(n), 115200 ,timeout=0.1)
status=1
port=n
return ser,port,status
except:
pass
time.sleep(0.05)
return 0, 0, 0
if __name__ == "__main__":
ser,port,status=connect()
n=0
while n<3:
num = input("Enter a word: ") # Taking input from user
ser.write(bytes(num, 'utf-8'))
time.sleep(0.05)
data = ser.readline()
print(data) # printing the value
n+=1
ser.close()
print('closed')
I have found the reason my code was not working! Notice in the code I posted, I use the input function to get a user input which is sent to the Arduino. This effectively results in a delay. In the scenario that was not working, I did not use the input function and so my code went straight from serial.serial to serial.write. The Arduino runs at 16 MHz and just couldn't keep up! All I needed to do was add a delay and now it works!
I'm working on a project where an RFID-RC522 reader reads an RFID card, pulls that read number in, and relays in out via serial. The code for that is as follows:
#include <SPI.h>
#include <RFID.h>
#define SS_PIN 10
#define RST_PIN 9
RFID rfid(SS_PIN, RST_PIN);
String rfidCard;
void setup() {
Serial.begin(9600);
//Serial.println("Starting the RFID Reader...");
SPI.begin();
rfid.init();
}
void loop() {
if (rfid.isCard()) {
if (rfid.readCardSerial()) {
rfidCard = (String(rfid.serNum[0]) + String(rfid.serNum[1]) + String(rfid.serNum[2]) + String(rfid.serNum[3]));
Serial.print(rfidCard);
}
rfid.halt();
}
}
That part works great. On the python side, the following code runs on my computer and reads everything in fine:
import serial
import time
connected = False
#This port below is specific to my usb port I think
port = '/dev/cu.usbserial-14620'
baud = 9600
serial_port = serial.Serial(port, baud, timeout=0)
while True:
reading = serial_port.readline().decode()
if reading != '':
print(reading)
So the end result when I plug in the RFID reader to my arduino Nano, and the Nano into my computer, and run the Python code is: When I put an RFID card up to the reader, it continuously prints the number, and when I pull it away, it prints nothing. Looks like this:
786179181
786179181
786179181
786179181
786179181
786179181
Exactly what I want.
Here's where the problems start. I want to introduce a period of time in the python script where it won't read anything. A sort of delay where the RFID reader is essentially not useable. This code is a minimally reproducible example of the effect I'm trying to get:
i = 5
while i > 0:
print(i)
time.sleep(1)
i-=1
When I add this code right before the Serial initialization line, it prints a countdown from 5 to 1, and then opens the port and starts reading and printing. But it looks like if I place the card on the reader before the countdown is up, it adds all that it reads to a buffer, and then spams it all at once, once the port is allowed to print again. What's most strange to me is that it actually begins to spam it in a loop, even when I take the card away. And from here on out, the arduino is now stuck in this state of spamming the data - even if I terminate the python script and rerun it, and dont put any card up to the reader, it's still spamming that old data in an infinite loop. It Looks more like this, stacking into one line at times:
786179181786179181786179181786179181
786179181
786179181786179181786179181786179181786179181
I've tried using commands like flush(), reset_input_buffer(), and reset_output_buffer(), but they don't seem to do what I want, or maybe I don't completely understand how they work. I'm not entirely sure if my problem lies in the arduino code output or the python code input.
Alright, I was able to get this to work using the following methods.
In the arduino, the write code only executes if it's receiving something from python:
#include <SPI.h>
#include <RFID.h>
#define SS_PIN 10
#define RST_PIN 9
RFID rfid(SS_PIN, RST_PIN);
String rfidCard;
void setup() {
Serial.begin(9600);
SPI.begin();
rfid.init();
}
void loop() {
if (rfid.isCard()) {
if (rfid.readCardSerial()) {
if (Serial.available()){
rfidCard = (String(rfid.serNum[0]) + String(rfid.serNum[1]) + String(rfid.serNum[2]) + String(rfid.serNum[3]));
Serial.println(rfidCard);
}
}
rfid.halt();
}
}
In python, it sends data to the arduino only when I want to receive data back, i.e., after the delay has passed:
import serial
import time
port = '/dev/cu.usbserial-14620'
baud = 9600
serial_port = serial.Serial(port, baud, timeout=1)
i = 5
while i > 0:
print(i)
time.sleep(1)
i-=1
while True:
serial_port.write(b'1')
try:
reading = serial_port.readline().decode().rstrip()
print(reading)
except:
pass
This has the desired effect of printing one line of data at a time without any collected buffer data.
Thank you everyone for you assistance!
I'm currenty working on an interface between arduino and raspberry pi. The arduino is connected via usb cable to the Rasbperry Pi. The Arduino has an LED connected to a digital Pin that may be turned on and off by sending specified bytes over the USB serial connection. The Raspberry Pi then runs the following python script:
import serial
import time
arduino = serial.Serial('/dev/ttyACM0', baudrate=115200, timeout=.1)
time.sleep(2)
start = time.time()
for _ in range(10):
arduino.write(bytes([0x58])) //Turns LED on (on Arduino)
print(time.time()-start,'on')
time.sleep(.5)
arduino.write(bytes([0x18])) //Turns LED off (on Arduino)
print(time.time()-start,'off')
time.sleep(.5)
arduino.write(spam_the_COM_port) # will let the arduino start sending
# infinite amout of bytes
print('started spamming')
for _ in range(10):
arduino.write(turn_led_on) #Actual byte is 0x58
print(time.time()-start,'on')
time.sleep(.5)
arduino.write(turn_led_off) #Actual byte is 0x18
print(time.time()-start,'off')
time.sleep(.5)
So I'm switching on and off an LED that is connected to an arduino by sending it the specified bytes: 0x58 and 0x18 The Arduino runs code that looks like this:
bool spamming = false;
void setup()
{
Serial.begin(115200);
pinMode(24, OUTPUT);
}
void loop()
{
while(Serial.available() > 0)
{
unsigned char x = Serial.read();
if (x == 0x58)
{
digitalWrite(24, HIGH); //Turn on LED
}
else if (x == 0x18)
{
digitalWrite(24, LOW); //Turn off LED
}
else if (x == 0x00)
{
spamming = true;
}
}
if (spamming)
{
if (Serial.avialiableForWrite() > 0) Serial.write(0x01);
}
}
I get the expected result (which is just the console printing on and off 10 times (on the raspberry side) and the Arduino actually turning the LED on and off 10 times in a row.
I then send a command that will make the Arduino start sending an infinite amount of bytes to the Raspberry Pi whilst still checking the Serial incoming buffer for new commands. After that I repeat the same process as before and try to send the LED switching command.
Now the python console output stays the same (runs without any error raising), yet the program blocks at each arduino.write() command for a long time (around 10s or longer) even though there is a timeout specified (0.1s). After blocking, the console prints as usual and the LED on the arduino switches as usual. I just added timesamps to the python console output and the results look like this:
2.002582311630249 on
2.502887725830078 off
3.003848075866699 on
3.5050783157348633 off
4.005117177963257 on
4.5058839321136475 off
5.006282806396484 on
5.50661826133728 off
6.008156776428223 on
6.509922504425049 off
7.010948181152344 on
7.512096166610718 off
8.012341737747192 on
8.513236999511719 off
9.013462543487549 on
9.514604091644287 off
10.015231370925903 on
10.515642404556274 off
11.01583743095398 on
11.516374349594116 off
started spamming
12.018024444580078 on
12.518478155136108 off
13.01881217956543 on
13.519550085067749 off
28.621582508087158 on
44.68230104446411 off
57.40802550315857 on
63.064852714538574 off
65.58953928947449 on
68.61984610557556 off
77.50833535194397 on
96.19384908676147 off
102.6581130027771 on
109.2223105430603 off
132.55511474609375 on
155.684588432312 off
171.44103860855103 on
185.78372859954834 off
199.51972341537476 on
216.58949422836304 off
So the problem must be on the raspberry pi. I tried the same code on my windows machine (plugging the arduino in there of course) and the same thing happened, which led to the conclusion that it must have something to do with the virtual COM/Serial port.
So now to the actual question: Why does it take so long for the serial.write() function to write something to the output buffer (on the pi) when the input buffer (on the pi) is beeing spammed by the arduino?
As far as I know, a UART Chip has two physically seperate buffers that are completely independent. Does a virtual COM port not have this independence of the two buffers? (Maybe just some allocated memory for both of them in a stack-heap kind of way?) Or is the problem that USB 1.0 and 2.0 are only half-duplex?
(And then lastly is there a way to fix the fucntion block?)
[update]
I managed to stop the function from blocking by adding
if arduino.inWaiting() > 1000: arduino.flushInput()
before every arduino.write() statement. I'm still interested in how the Serial/COM port is managed by the operating system, be it linux/raspbian or windows.
I am reading data from a serial port, sent by an arduino.
I have two files, which I use separately to write some code and try differents things. In one of them, I read the data and I draw it using a matplotlib figure. After I finish using it, it remains connected to my computer and sending data. So, what i need to do is to "reset" the port. This is, close the opened port and open it again, and stop it from sending data so I can use the arduino to try some modifications in the code of this file.
So to accomplish this, i mean, to reset the port, i created another file and wrote this code:
import serial
print "Opening port"
try:
serial_port = serial.Serial("com4", 9600)
print "Port is open"
except serial.SerialException:
serial.Serial("com4", 9600).close()
print "Port is closed"
serial_port = serial.Serial("com4",9600)
print "Port is open again"
print "Ready to use"
But this code does not seems to work.The port is still connected and sending data. So, it means that I can not close the port with my code,and then reopen it again.
What am i doing wrong? How can I stop the arduino from sending data? Or how can I reset thw arduino, maybe?
Hope you can help me.
----- EDIT -----
I accomplish to identify the real problem that i am having, and it is not what i thought. The problem was not that the port was open despite that i use the closefunction that Pyserial have. The real thing is that the port is closing as I want, but the device (the arduino) is still sending data. So, i changed the code to reproduce the situation.
This is the code:
print "Abriendo puerto"
ser = serial
try:
ser = serial.Serial("com4", 9600, timeout = 1)
serial_port = "Open"
print "The port %s is available" %ser
except serial.serialutil.SerialException:
print "The port is at use"
ser.close()
ser.open()
while ser.read():
print "Sending data"
ser.setBreak(True)
time.sleep(0.2)
ser.sendBreak(duration = 0.02)
time.sleep(0.2)
ser.close()
time.sleep(0.2)
print "The port is closed"
exit()
With this code, what i do is:
1) I open the serial port
2) If the device is sending data, I print "Sending data"
3) After 1 sec, I try to close the port and stop the device from sending data
I tried these last two thing with the close function to close the port, and reading the docs I tried with setBreak and sendBreak as you can see in the code above (i left them on purpose). But the device is still sending the data, which means that the code does not work.
So, is there a way to tell the arduino "stop sending data", or can i reset the device?
I do a very similar thing, two ways with success.
The first way is to let the Arduino send data continuously. The problem here is when your python code wakes up and starts to read from the serial port, the Arduino might be anywhere in its procedures. The simple solution is to modify the Arduino code to send some kind of "restarting" line. All your python code needs to do in this case is wait for "restart", then read real data until it again sees "restart". I had noisy lines so my code read (and parsed) through multiple cycles to make sure it got good data.
resetCount = 0;
while resetCount < 3:
line = s.readline().rstrip("\r\n")
if string.find(line, "restart") != -1 :
resetCount += 1
elif resetCount > 0 :
fields = string.split(line, " ")
dict[fields[0]] = fields
The second way is to implement a command-response protocol with the Arduino, wherein the Arduino sends data only when requested. In this case your python code sends a command to the Arduino ("RT" in the example below) and then reads data from the Arduino until it sees a "completed" line or it times out.
dict = {}
regex = re.compile('28-[0-9A-Fa-f]{12}') # 28-000005eaa80e
s = serial.Serial('/dev/ttyS0', 9600, timeout=5)
s.write("RT\n");
while True:
line = s.readline().rstrip("\r\n")
print line
if string.find(line, "completed") != -1:
break;
fields = string.split(line)
if (regex.match(fields[0]) != None and len(fields) == 4) :
dict[fields[0]] = fields
s.close()
It is possible that when you close the port, data is still coming from the arduino and being buffered by the operating system. There is a short delay between your script calling close() and the device driver actually shutting stuff down.
An immediate re-open may allow the driver to carry on without resetting its buffer. This is the device driver buffer, not the one seen by the Python serial port instance.
If you wait for at least a couple of seconds after the call to close() before you try to call open() then the behaviour should be as you hope.
I have just spent most of the day working out that this is what had been preventing my code from working properly.
I think you have to do a serial_port.open() immediately after creation to actually open the port.
It also looks like it just opens the port and exits if successful. Maybe I'm missing something here. I've never used pySerial, I'm just going by the docs.
Try using the handle to close the port instead of invoking the constructor again.
If you the port is open and you call serial.Serial("com4", 9600) it will attempt to re-open the port again and fail.
If serial_port was assigned successfully then serial_port.close() should close it.
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)