Stop waiting for serial response after 5 seconds in Python - python

I am trying to get a response from a serial port, but sometimes there will be none. This makes my script wait forever, thus I have to kill it.
Is there a simple way to make my code wait for example 5 seconds and if there was no response continue with the script?
My code looks like this:
import serial
ser = serial.Serial('COM3', 9600)
test = '2ho0'
ser.write(str.encode(test))
data = ser.readline()
print(data)
I have tried this solution, but it does not stop after 5 prints of Hello world. Instead, it continues writing Hello World, until I get a runtime error.
Over here and here I tried severeal answers with no success either.
EDIT: I am wondering if it is possible to have something like:
ser.serialReplyAvailable()
This would do the job for me as well.
Thanks for any advice.

You can specify a read timeout which will make the ser.readline() either return immediately if the requested data is available, or return whatever was read until the timeout expired. Check the pySerial docs there is more on this there.
ser = serial.Serial('COM3', 9600, timeout=5)

You could try wrapping the readline into a while Statement and have a timer tick inside if the reading was not successful. If it is, break out of it.
from time import sleep
tries = 0
while tries < 4:
data = ser.readline()
if data != None: #Or "", whatever the return data
break
sleep(5); tries += 1

Related

How to terminate ser.read in pyserial python when using with while 1 without entering any commands?

I am trying to write a script currently I am using ser.read() directly and printing values but I want to write a function where it waits for some time reads all char and than gets out of loop. Current loop prints all char but doesnt get out of the loop and I have to either edit and make it exit by keyboard click. But I want a script where it reads and exit and user doesnt need to do anything. Sorry if a noob question but new with Python and Pyserial
if(ser.isOpen()):
try:
def read():
ser.flushInput()
while 1:
try:
check = ser.read()
print check
except Exception:
print("Error")
else:
print("Port not open")
If you want to wait for a certain amount of time before exiting, try something like this. In this example, the code will wait for 5 seconds.
import time
def read():
ser.flushInput()
start_time = time.time()
while time.time() - start_time < 5:
try:
check = ser.read()
print check
except Exception:
print("Error")

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

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.

cleaning up function looping

I am looping a function in python as I created my own constant listener. My question is how can I ensure I don't overflow / crash? Logically this is telling me I am leaving a lot of open functions in the background. Once parameters of my application are met I exit(0) for a clean shut down. but how can I ensure this doesn't eat up memory?
This code works fine btw, but I am simply trying to improve it because it feels really wrong to me that I am calling the function within its self without closing the previous use of it and it just feels dirty. constructive comments please.
e.g. (this is now my actual code)
import serial
import sys
import time
def enterdata():
ser = serial.Serial(sys.argv[1], sys.argv[2])
ser.write("\r")
time.sleep(0.5)
while True:
data = ser.read(ser.inWaiting())
if (len(data) > 0):
a = []
for i in range(len(data)):
a.append(data[i])
if "Please press Enter to activate this console." in "".join(a):
print ("1")
exit(0)
break
ser.close()
enterdata()
ser = serial.Serial(sys.argv[1], sys.argv[2])
ser.write("\r\n")
enterdata()
NEW VERSION SO FAR FROM POSTS MADE:
import serial
import sys
import time
def enterdata():
ser = serial.Serial(sys.argv[1], sys.argv[2])
ser.write("\r")
time.sleep(0.5)
while True:
data = ser.read(ser.inWaiting())
if (len(data) > 0):
a = []
for i in range(len(data)):
a.append(data[i])
if "Please press Enter to activate this console." in "".join(a):
print ('1')
return True
exit(0)
break
ser.close()
ser = serial.Serial(sys.argv[1], sys.argv[2])
ser.write("\r\n")
state = False
while state is not True:
state = enterdata()
This code you showed will give a "RuntimeError: maximum recursion depth exceeded" Error because of Python sets a default value for how many recursions can occur in one function at a time.
Any yours is infinite and definitely will cause problems even if you change this default limit.
Why not make a loop yourself and call the function at will ?
found = False
while not found:
extracted_data = lookfordata()
if extracted_data == "I want it to be equal to this":
found = True
I see you edited the post. Whatever you are trying to do, that method is not efficent, not recommended and not pretty. All the good reasons not to use it.
Imagine that you will handle some data larger than you are using now, you won't know if the recursion stopping condition will come to pass before you exceed the limit. It's also no use to increase limit all the time you encounter a bigger data. I think it should be any programmer's goal to avoid repetition and coming up with programs that can handle any type of unexpected input.
You updated your post again. This way of input handling is much better than raw recursion. Just to mention, instead of;
for i in range(len(data)):
a.append(data[i])
use
for i in data:
a.append(i)

Multiple writes to file

I have the following python code that expects data coming from the serial port, and writes it to a file.
import time
import serial
def write_log ( text ):
f = open('logger.log', 'a')
f.write( text )
f.close()
ser = serial.Serial()
ser.port = "/dev/ttyS0"
ser.baudrate = 4800
ser.open()
if ser.isOpen():
while 1:
while ser.inWaiting() <= 0:
time.sleep(1)
response = ser.read(ser.inWaiting())
if len ( response ):
write_log( response )
print response
It works to an extent, as after some time, it starts to hang, bringing the CPU all the way up, and not writing anything (or sometimes writing only pieces of text) to the .log file.
The process here is pretty intensive, as my serial port will be writing an 8 bytes string every second, and this python script is supposed to then receive it, and write its contents to the log file.
I'm thinking the problem here is the fact that I'm opening and closing the file too much, and this is somehow making the whole process slow. I'm no python wizz, so any help or advice on improving this code would be greatly appreciated.
Thanks in advance,
You have an infinite loop in your code, and you don't break out from it when there is a problem - or the serial device is no longer open.
Probably use:
while ser.isOpen():
while ser.inWaiting() <= 0:
time.sleep(1)
response = ser.read(ser.inWaiting())
if len(response):
write_log(response)
print response
or even:
while ser.isOpen() && ser.inWaiting() <= 0:
time.sleep(1)
response = ser.read(ser.inWaiting())
if len(response):
write_log(response)
print response
I'm not sure about the sleep, either; you'd do better just waiting in the read for data to become available.
As I think about it, not knowing the methods available in the serial class, the more I think the main loop should be attempting to read from the serial device, hanging happily if there is nothing currently available, and only terminating when the input method indicates there is no more input to come - the device has been closed on you, or has failed in some way.
I think the problem here is that you are polling for updates.
Python's serial.read() function actually blocks the current thread until something becomes readable on the thread, until timeout. What you should do, therefore, is break out another thread to handle serial IO. Have it loop indefinitely, checking a condition (the master thread wants you to stay listening and the serial port is still available). This thread will do something like:
while ser.isOpen() && thisthread_should_keep_doing_this:
response = ser.read(ser.inWaiting())
if len(response):
write_log(response)
print response
Then, when you want it to exit, your master thread sets thisthread_should_keep_doing_this=False and when your slave thread has finished reading, it kills itself.
Two things:
Do have the read timeout relatively frequently.
Do not "remote kill" the thread. Pass it a message and have it kill itself. Killing threads remotely creates an awful mess.
See http://pyserial.sourceforge.net/examples.html#miniterm

Categories