Arduino Serial Buffer Flooding When Reading is Delayed using Pyserial - python

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!

Related

How do I get python IDLE/ GUI communicating with mbed board?

I need to have a python GUI communicating with an mbed (LPC1768) board. I am able to send a string from the mbed board to python's IDLE but when I try to send a value back to the mbed board, it does not work as expected.
I have written a very basic program where I read a string from the mbed board and print it on Python's IDLE. The program should then ask for the user's to type a value which should be sent to the mbed board.
This value should set the time between LED's flashing.
The python code
import serial
ser = serial.Serial('COM8', 9600)
try:
ser.open()
except:
print("Port already open")
out= ser.readline()
#while(1):
print(out)
time=input("Enter a time: " )
print (time)
ser.write(time.encode())
ser.close()
and the mbed c++ code
#include "mbed.h"
//DigitalOut myled(LED1);
DigitalOut one(LED1);
DigitalOut two(LED2);
DigitalOut three(LED3);
DigitalOut four(LED4);
Serial pc(USBTX, USBRX);
float c = 0.2;
int main() {
while(1) {
pc.printf("Hello World!\n");
one = 1;
wait(c);
two=1;
one = 0;
wait(c);
two=0;
c = float(pc.getc());
three=1;
wait(c);
three=0;
four=1;
wait(c);
four=0;
}
}
The program waits for the value to be entered in IDLE and sent to the mbed board and begins to use the value sent to it but suddenly stops working and I cannot figure out why.
You need to take this line:
c = float(pc.getc());
out of your loop.
The reason your program stops working is that line is holding until you send something again. If you only send once it waits forever.
If you want to dynamically set wait time after the program enters the while loop, I would suggest attaching a callback function to a serial RX interrupt.
RawSerial pc(USBTX, USBRX);
void callback() {
c = float(pc.getc());
}
Serial uses mutex and cannot be used in ISR on mbed OS5. Use RawSerial instead.
int main() {
pc.attach(&callback, Serial::RxIrq);
while(1) {
// your code for LED flashing
// no need to call pc.getc() in here
one = 1;
wait(c);
one = 0;
wait(c);
}
}
This way, the LED continues to blink and you can update c whenever the mbed receives a value.
Also, it looks like you are sending ASCII characters. ASCII 1 is 49 in decimal. So, pc.get() returns 49 when you send '1'. I don't think that is what you want. If you are always sending a single digit (1~9), an easy fix is pc.getc() - 48. But you better parse string to int and do error handling on python side.

PySerial slowdown on read from Arduino

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?

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)

Random character at the beginning of serial.readline() in python and arduino

I get random characters when I perform a serial.readline(), sometimes it is other umbers and sometimes it is whole messages. The output should be "1,2" or "2,2"
Here are a couple of screenshots of the output of serial.readline().
I have tried to put a delay at before serial.readline() but it did not make a difference.
There is usually a strange character at the beginning:
I have also received strange messages:
There is a problem later on in the program that causes the program to hand because sometimes I just receive a blank line.
Is there a way to get consistent output from serial?
Here is the arduino code:
void setup() {
// initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin, INPUT);
Serial.begin(9600);
}
void loop(){
// read the state of the pushbutton value:
buttonState = digitalRead(buttonPin);
// check if the pushbutton is pressed.
// if it is, the buttonState is HIGH:
if (buttonState == HIGH) {
// turn LED on:
digitalWrite(ledPin, HIGH);
Serial.println("1,2");
}
else {
// turn LED off:
digitalWrite(ledPin, LOW);
Serial.println("2,2");
}
}
And here is the python code:
ser = serial.Serial('/dev/ttyUSB0', 9600)
line=ser.readline()
coord= line.strip()
print coord
EDIT:
I tried putting ser.flushInput() after the ser.open() and I get the same output.
Have you flushed the serial buffer
ser.flushInput()
I've been having the same issue when interfacing between pyserial and Arduino. This may be an old post, but in case someone else is having the same trouble, I remedied my problem by inserting:
ser.flushInput()
...right before my ser.readline() call.
How to Read serial data from an Arduino in Linux
Nothing fancy here. I’m getting the current port configuration, setting the input/output speed to 9600 baud, setting the data expectations to be 8 bits per word, with no parity bit and no stop bit, setting canonical mode, and then committing those changes back to the serial port.
If I am not mistaken you have to change the mentioned settings when connecting through serial port.
You do not mention it, but I guess you are using the pySerial library. Regardless you can use it with the correct settings to connect through serial. The constructor of the API allows all the parameters which are noted below:
Pyserial Library
I have not tested this approach as I have had a similar problem in C not Python.
What happens if you break it down to something simpler?
On the Arduino:
void setup()
{
Serial.begin(9600);
}
int i = 0;
void loop()
{
delay(1000);
Serial.print("Hello World ");
Serial.println(i);
i++;
}
And in the Python REPL...
import serial
ser = serial.Serial('/dev/ttyUSB0', 9600)
while(1):
ser.readline()
The first behavior you should observe is that when you initiate the serial connection, the Arduino reboots. This takes a few seconds.
The second behavior you should observe is that (if you're typing this in the REPL slowly) when the while(1) loop begins, you get all of the serial data that had accumulated since you initiated the serial connection. It takes me a few seconds to type all that out, so when I hit Enter after ser.readline() I see:
'Hello World 1\r\n'
'Hello World 2\r\n'
'Hello World 3\r\n'
I only mention this to make sure you're familiar with two things that burned me a bit the last time I messed with serial communication to an Arduino: it needs time to reboot after connecting, and the serial data buffers. readline() will only give you one line in the buffer - not everything that's in the buffer.
Is it possible you're trying to sent/receive data before it's done rebooting? What about button bounce - could a dozen high/low state detections be messing something up?

Changing XBee from 57.6k to 115.2k Causing Problems with TTL Camera and PySerial

I've been working on a simple XBee/Arduino/Python transfer system. Here's how it works: A hex command is sent from python over xbee, which is received by the arduino. This triggers it to take a picture using an Adafruit TTL Serial Camera. The image is then held in memory and sent over the xbee to the computer, 32 bytes at a time. The Python script then adds the bytes to a .jpg, to be viewed when it finishes.
As of now, both run fine, albeit a tad slow (~25 sec round-trip) for my needs. The problem is, switching from 57600 baud to 115200 in the xbee firmware and programs causes them to fail. They will sometimes give a .jpg roughly half the normal size, or not transmit at all, in both cases being unreadable from a viewer. I've tried changing the timeout in the python side, and modifying the xbee's interfacing options, all to no avail.
Here is the Arduino sketch (adapted largely from the Snapshot example sketch in the Adafruit VC0706 examples library):
#include <Adafruit_VC0706.h>
#include <SoftwareSerial.h>
#define chipSelect 10
SoftwareSerial cameraConnection = SoftwareSerial(2,3);
Adafruit_VC0706 cam = Adafruit_VC0706(&cameraConnection);
void setup()
{
Serial.begin(57600);
pinMode(8, OUTPUT);
if (cam.begin()){}
else { return; } //Abort the transfer if camera does not initialize
cam.setImageSize(VC0706_640x480);
}
void loop()
{
if (Serial.read() == 0x01) //Wait for send command
{
snapAndSend();
cam.reset();
}
}
void snapAndSend()
{
cam.takePicture();
uint16_t jpgLen = cam.frameLength();
while (jpgLen > 0)
{ //Send off 32 bytes of data at a time
uint8_t *buffer;
uint8_t bytesToRead = min(32, jpgLen);
buffer = cam.readPicture(bytesToRead);
Serial.write(buffer, bytesToRead);
jpgLen -= bytesToRead;
}
}
And the Python script:
import serial
import time
link = serial.Serial('COM12', 57600)
print("Engage!")
while True:
answer = input("Press enter to take a picture (type ' to exit): ")
if answer == "'":
break
file = open('imageTest.jpg', 'wb')
link.write(b"\x01")
time1 = time.time()
while True:
if link.inWaiting() > 0:
file.write(link.read())
time1 = time.time()
else:
time2 = time.time()
if (time2 - time1) > .5:
break
print ("Complete! Closing file now...")
file.close()
I'm still a bit new to serial communication and xbees, so I may be overlooking something here. Anyone more experienced have any thoughts on why a switch in baudrate breaks it?
I'm fairly certain that this is going to be related to flow control.
The XBee module can't keep up with a steady stream of data at 115200, so you need to configure it to use hardware flow control. In your case, you need to watch the CTS (clear to send) line from the XBee module, and have your program hold off on sending more data whenever it's not asserted (held low).
You can also configure the XBee to use one of its inputs for an RTS (request to send) signal -- the line the host uses to tell the XBee module when it's ready to receive data.
You may need to modify your Arduino code to receive the entire image from the camera into a buffer, and then slowly send it back out to the XBee module. If you were to check the CTS line before sending each byte, you could hold off whenever the XBee module's buffers were full. You'd need to call a function in your main loop to check the status of your buffered data, and the status of the pin to see if it was possible to send another byte.

Categories