blocking read(1) with timeout in pyserial

6.8k views Asked by At

I use the following piece of code to read the serial port until i get a terminating character.

"""Read until you see a terminating character with a timeout"""
    response=[]
    byte_read=''
    break_yes=0
    time_now = time.clock()
    while ((not (byte_read=='\r') ) and (break_yes==0)):
        byte_read = self.ser.read(1)
        if (not(len(byte_read)== 0) and (not (byte_read =='\r'))):
            response.append(byte_read)
        if ( time.clock() - time_now > 1 ):
            if self.DEBUG_FLAG: 
                print "[animatics Motor class] time out occured. check code"
            break_yes=1
    if break_yes==0:
        return ''.join(response)
    else:
        return 'FAIL'

This works well but because of the while loop, the cpu resources are taken up.

I think that having a blocking read(1) with a timeout will save some of the cpu. The flag i am looking for C is "MIN == 0, TIME > 0 (read with timeout)" in termios

i am looking for a similar flag in Python.

I could also use the io.readline to read till i get '\r', but i want to stick to pyserial as much as possible without any other dependency.

Would greatly appreciate advice. Do let me know if i should do it in a completely different way either too.

Thanks,

2

There are 2 answers

3
deets On

You should read the documentation of Pyserial: it clearly states that a timeout of 0 as you pass it to the constructor will turn on non-blocking behaviour:

http://pyserial.sourceforge.net/pyserial_api.html#classes

Just get rid of the timeout parameter, and you should be set.

0
vamshi konduri On

Aight, so I found out a way. Instead of polling with the no timeout, I use the select module in python, which is similar to the one in C.

It returns if any data is available immediately, or waits for the timeout period and exits, which is precisely what i wanted. I took deets comments for cleaning up the code and it looks like so now.

def readOnly(self):
"""Read until you see a terminating character with a timeout"""
    response=[]
    byte_read=''
    while (not (byte_read=='\r')):
        reading,_,_ = select.select([self.ser], [], [], 1) #returns immediately if there is data on serial port. waits 1 second to timeout if not.
        if reading !=[]:                                   #something is to be read on the file descriptor
            byte_read = self.ser.read(1)
            if (byte_read !='\r'):
                response.append(byte_read)
            else:                                          #'\r' received
                return ''.join(response)
                break
        else:
            if self.DEBUG_FLAG: 
                print "[Motor class] time out occured. check code"
            return 'FAIL'
            break

`

This decreased the cpu usage from 50% to 5% so life is better now.

Thanks,