Pyserial: readline() is blocking, although timeout is defined - python

I have an issue with readline() from pyserial.
My code is:
import serial
uart = serial.Serial(port='/dev/ttyS0', baudrate=9600, timeout=3)
data = uart.readline().decode()
print(data)
uart.close()
I'm receiving data from a microcontroller. The problem is, if no data is send from the mc, the program is waiting "forever", although I defined a timeout of 3 seconds.
What am I doing wrong?

Ok, I have discovered the solution
The problem is, that the Raspberry Pi 3 and 4 uses the "miniUART" as the primary UART and the Raspberry Pi 1 and 2 uses the "PL011".
You can the details here: https://www.raspberrypi.org/documentation/configuration/uart.md
To get the timeout break working, you have to change the "PL011" to UART0. By default UART0 (GPIO 14 and 15) is set to "miniUART" and "PL011" is used for the bluetooth modem.
You have to edit the /boot/config.txt and add dtoverlay=disable-bt.
You also have to disable the system service, that initialises the modem, so it does not connect to the UART, using sudo systemctl disable hciuart.
I have done that and now the program waits the timeout for a message and goes on, if no message is received.

timeout will only affect the maximum time that readline() will wait
timeout = x: set timeout to x seconds (float allowed) returns immediately when the requested number of bytes are available, otherwise wait until the timeout expires and return all bytes that were received until then
try this :
import serial
uart = serial.Serial(port='/dev/ttyS0', baudrate=9600, timeout=3)
while True:
data = uart.readline().decode()
print(data)
if not data :
break
uart.close()

Related

Python serial.readline() not blocking

I'm trying to use hardware serial port devices with Python, but I'm having timing issues. If I send an interrogation command to the device, it should respond with data. If I try to read the incoming data too quickly, it receives nothing.
import serial
device = serial.Serial("/dev/ttyUSB0", 9600, timeout=0)
device.flushInput()
device.write("command")
response = device.readline()
print response
''
The readline() command isn't blocking and waiting for a new line as it should. Is there a simple workaround?
readline() uses the same timeout value you passed to serial.Serial().
If you want readline to be blocking, just delete the timeout argument, the default value is None.
You could also set it to None before calling readline(), if you want to have a timeout for openening the device:
import serial
try:
device = serial.Serial("/dev/ttyUSB0", 9600, timeout=0.5)
except:
#Exception handeling
device.flushInput()
device.write("command")
device.timeout=None
response = device.readline()
print response
I couldn't add a commend so I will just add this as an answer. You can reference this stackoverflow thread. Someone attempted something similar to your question.
Seems they put their data reading in a loop and continuously looped over it while data came in. You have to ask yourself one thing if you will take this approach, when will you stop collecting data and jump out of the loop? You can try and continue to read data, when you are already collecting, if nothing has come in for a few milliseconds, jump out and take that data and do what you want with it.
You can also try something like:
While True:
serial.flushInput()
serial.write(command)
incommingBYTES = serial.inWaiting()
serial.read(incommingBYTES)
#rest of the code down here

Pyserial can't read device

I'm trying to read data off of a sensor that I bought, using a conversion module (SSI to RS232). I have the module plugged into my Windows laptop via USB/serial converter.
When I use Putty in Serial mode, I can send the command $2RD and receive the appropriate response from the sensor unit. When I run a script to try to do the same thing, the unit returns: ''
Here is the code I am using:
import sys
import serial
import time
ser = serial.Serial(
port='COM4',
baudrate=9600,
timeout=1,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
)
while True:
ser.write('$2RD'.encode())
#time.sleep(1)
s = ser.read(26)
print s
A few other notes:
I've tried some variations using flushInput, flushOutput, sleeping, waiting, etc...nothing seems to help.
I know I have the COM ports right/the hardware all works in Putty, so pretty sure this is something with my code.
I've also tries 13,400 BAUD with no difference in outcome.
If I connect the TX and RX lines from the USB, I can read the command I'm sending...so it should be at least getting to the RS232/SSI conversion device.
s = ser.read(26) should probably be ser.read(size=26) since it takes keyword argument and not positional argument.
Also, you can try to set a timeout to see what was sent after a specific time because otherwise the function can block if 26 bytes aren't sent as specified in the read docs of pyserial :
Read size bytes from the serial port. If a timeout is set it may return less characters as requested. With no timeout it will block until the requested number of bytes is read.

Errors in the data received through serial using Python

I want to send data to a peripheral using PySerial. However, errors sometime appear in the data received.
import serial
dongle = serial.Serial("/dev/ttyUSB0", 9600)
dongle.write("Some data\n")
And then, Some data\n is transmitted to the peripheral.
Sometime it works great, but sometime, errors appear in the data received: Somata\n, Som a\n, etc…
How to fix that issue?
I suspect you need to add an inter-char delay to your serial write. Unfortunately, such a thing is not available in PySerial. There is an inter_byte_timeout, but that's for reads.
Something like:
import serial
import time
def write_with_delay(command):
while len(command)>0: # Loop till all of string has been sent
char_to_tx = command[0] # Get a
dongle.write(char_to_tx)
command = command[1:] # Remove sent character
time.sleep(0.01)
dongle = serial.Serial("/dev/ttyUSB0", 9600)
write_with_delay('Some data\n')
Which will send the string with a 10ms (0.01s) delay between each character. Ordinarily, adding arbitrary delays into code is a bad thing, but for serial comms it is sometimes necessary.

Reset an open serial port

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.

Arduino serial timeouts after several serial writes

I noticed with my board from DIY drones a strange behavior when I use my custom firmware.
Here is an example function which is called in my firmware running on an Arduino board:
void send_attitude(float roll, float pitch, float yaw) {
hal.console->printf("{\"type\":\"sens_attitude\",\"roll\":%.4f,\"pitch\":%.4f,\"yaw\":%.4f}\n",
roll, pitch, yaw);
}
As you can see, the code just writing a message in the serial port set in setup (hal.uartA).
I call this function every 0.5s:
inline void medium_loop() {
static int timer = 0;
int time = hal.scheduler->millis() - timer;
// send every 0.5 s
if(time > 500) {
send_attitude(OUT_PIT, OUT_ROL, OUT_YAW);
timer = hal.scheduler->millis();
}
}
Now to the strange thing. If I use the serial monitor or read the board with another program or script everything is fine. Every 0.5s the proper LED is blinking and message is shown. But if I don't read it out, after appr. 10s the LED is flushing up continuously and no connection/communication is possible anymore. I have to unplug the board then. The same behavior is observed the other way round. If I send to my board over serial port (in my case USB) and don't flush the input buffer, the LED is flushing up continuously and I get a timeout. The following code works:
def send_data(line):
# calc checksum
chk = chksum(line)
# concatenate msg and chksum
output = "%s*%x\r\n" % (line, chk)
try:
bytes = ser.write(output)
except serial.SerialTimeoutException as e:
logging.error("Write timeout on serial port '{}': {}".format(com_port, e))
# Flush input buffer, if there is still some unprocessed data left
# Otherwise the APM 2.5 control boards stucks after some command
ser.flush() # Try to send old message
ser.flushInput() # Delete what is still inside the buffer
If I comment out this line:
ser.flushInput() # Delete what is still inside the buffer
I don't use more settings then this.
I get (depending on the message interval) a timeout sooner or later. In my case I send every 20ms a signal which results in a timeout after ~10s. Also dependent on the length of message. Bigger messages cause it faster than smaller ones.
My settings are shown in the following snippets. Client side python code:
com_port = '/dev/ttyACM0'
baud_rate = '115200'
try:
ser = serial.Serial(com_port, baud_rate, timeout=0.1, writeTimeout=0.1, rtscts=1)
The if these timeouts happen, then I also get one if I set the timeout to something like 2s. In my case I need a very low latency, which is indeed possible if I keep reading and flushing. Firmware code from my Arduino:
void setup() {
// Set baud rate when connected to RPi
hal.uartA->begin(115200);
hal.console->printf("Setup device ..\n");
// Followed by motor, compass, barometer initialization
My questions are:
What exactly happens with my board?
Why it is not reacting anymore if I just write in my serial port without reading or flushing the buffer?
Is it really a buffer or driver problem associated with this strange behavior and is this problem related to all Arduino boards or maybe just mine APM 2.5 from DIY drones?
Last but not least: I was finding no functions in the library which are targeting such problems. Are there maybe any I don't know?
The complete source code is #google code: https://code.google.com/p/rpicopter/source/browse/
What board are you using and what processor does it have? My guess would be that your board is based on the ATmega32U4, or some other microcontroller that has a built-in USB module. If so, I have seen similar behavior before here is what I think is happening:
There is a buffer on your microcontroller to hold serial data going to the computer. There is a buffer in the computer's USB serial driver to hold serial received from the chip. Since you are not reading bytes from the COM port, the buffer on the computer will fill up. Once the buffer on the computer fills up, it stops requesting data from the microcontroller. Therefore, the buffer on the microcontroller will eventually fill up.
Once the microcontroller's buffer is full, how do you expect printf command to behave? For simplicity, the printf you are using is probably designed to just wait in a blocking loop until buffer space is available and then send the next character, until the message is done. Since buffer space will never be available, your program gets stuck in an infinite loop.
A better strategy would be to check to see if enough buffer space is available before calling printf. The code might look something like this:
if(console_buffer_space() > 80)
{
hal.console->printf(...);
}
I don't know if this is possible in the DIY drones firmware, and I don't know if the max buffer space can actually ever reach 80, so you will have to research this a bit.
I don't understand the use of:
ser.flush() # Try to send old message
ser.flushInput() # Delete what is still inside the buffer
Lets say your device is connected to PC and the python code is writing the (line, chk):
ser.flush() - why are you using it?
ser.flushInput() - will "delete" the Serial input buffer at the PC
It looks like other people have the same problem. And thanks to the Mod-Braniac who deleted my minimal example. My bet is, that's a problem with Arduino USB controller chip or the firmware on it.

Categories