Search code examples
pythonpython-3.xraspberry-pi3python-multithreadingpyserial

pyserial thread hangs on serial.in_waiting() statement


I have a pyserial thread that hangs in an unexpected place; checking the serial port for queued bytes. All serial input is done in this thread; main is primarily the display manager. The init executes fine, once in the run subroutine it hangs at the statement "Trace('In_waiting={}'.format(self.serial.in_waiting()))". main is not a cpu hog - it updates the display then waits for 1.0 seconds - that code is well debugged (says the naive programmer ;-)). There are no other threads although there are callbacks for GPIO pins. The input comes from the RasPi UART pins.

Having the code hang at this location makes no sense to me. I have tried it with various timeout values, including None and 0. The documentation seems to clearly indicate a return of 0 or n are the only options. I'm probably missing something obvious, but it eludes me.

class Monitor_PE_Devices_Thread(threading.Thread):
# Thread to monitor all of the PE devices.  All updating of the device_list items occurs
# here.
    def __init__(self):
        global device_list
        threading.Thread.__init__(self)
        Log.Debug('PE Monitoring thread initialized')
        self.buffer = ''
        self.port = '/dev/ttyAMA0'
        self.speed = 9600
        self.serial = serial.Serial(port=self.port, baudrate=self.speed, timeout=0.5)
        if self.serial.is_open: Trace('Serial port {} opened'.format(self.serial.name))
        Log.Debug('Monitoring the following devices:')
        for d in device_list:
            Log.Debug('   DevID[{}]: Name={}'.format(d, device_list[d][0]))

    def process_PE_response(self, devid, data): <block compressed>

    def read_line(self):  <block compressed>

    def run(self):
    # Main loop for PE device monitoring
        global kill_threads, device_list

        Log.Debug('PE Monitoring thread started')
        while not kill_threads:
            Trace('Top of read serial loop')
            Trace('Port status={}'.format(self.serial.is_open))
            Trace('In_waiting={}'.format(self.serial.in_waiting()))
            if self.serial.in_waiting() > 0:
                line = self.read_line()
                if len(line) > 0 and line[0] == 'a':
                    devid = line[1:3]
                    data = line[3:]
                    Trace('Data recieved for dev[{}]: {}'.format(devid, data))
                    dev = device_list.get(devid, None)
                    if dev != None:
                        device_list[devid][3] = utcnow()
                        self.process_PE_response(devid, data)
                    else:
                        Log.Debug('Unknown device id {} - ignored'.format(devid))
                else:
                    Log.Info('Invalid serial data: {}'.format(line))

Environment: python 3, pyserial 3, Raspberry Pi 3, Buster Lite, pygame


Solution

  • In pySerial 3.0, in_waiting is a property, not a method.

    So when you use self.serial.in_waiting(), Python should raise a TypeError, because the value of in_waiting in an int, which is not a callable.

    A small example:

    In [5]: class Test: 
       ...:     def __init__(self, a): 
       ...:         self._a = a 
       ...:          
       ...:     @property 
       ...:     def a(self): 
       ...:         return self._a 
       ...:                                                                                                  
    
    In [6]: t = Test(2)                                                                                      
    Out[6]: <__main__.Test at 0x8042660d0>
    
    In [7]: t.a                                                                                              
    Out[7]: 2
    
    In [8]: t.a()                                                                                            
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-8-47b4c8d89978> in <module>
    ----> 1 t.a()
    
    TypeError: 'int' object is not callable
    

    It could be that your program seems to hang because this happens in another thread.

    An unhandled exception should terminate Monitor_PE_Devices_Thread.run().

    In your main thread that updates the display test if the Monitor_PE_Devices_Thread is still running by using its is_alive() method.