pythonserial-portpyserial

Strange behaviour of PySerial after unplugging the serial device


I have the following simple setup:

Simple code to read the serial device data:

import time
import os
import sys
import serial 

    ser = serial.Serial("COM10", 9600, timeout=0.5)
    while(1):
         if ser.in_waiting:
              serial_data = str(ser.readline().decode('ascii')) #Read (receive) data
              print("Received: " + serial_data) #Print data
         time.sleep(0.01)  #Slight delay just in case.

The code works fine. However, when the Serial device is unplugged (via USB-to-Serial plug pull) Python crashes with the following exception:

File "D:\Program Files\Python37\lib\serial\serialwin32.py", line 259, in in_waiting
    raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError()))
serial.serialutil.SerialException: ClearCommError failed (OSError(22, 'The I/O operation has been aborted because of either a thread exit or an application request.', None, 995))

Now, warnings/exceptions whenever the serial device is physically unplugged are nothing new. PuTTy does that, and so do a number of other terminal emulators and logically so should do PySerial.

HOWEVER, I remember clear as day that at some point in development unplugging the serial device incited no reaction from PySerial. I could even "hot-swap" - unplug one serial device, plug in another and the script would still work and print the received data.

Is this "hot-swapping" possible or did I horribly misremember all of it?


Solution

  • You can use a try-except block to make it hot swappable. This works for me (using an Arduino as the serial device).

    import time
    import serial
    
    ser = serial.Serial("COM9", 9600, timeout=0.5)
    while True:
        try:
            if ser.in_waiting:
                serial_data = str(ser.readline().decode('ascii'))
                print("Received: " + serial_data)
            time.sleep(0.01)
        except:
            try:
                ser = serial.Serial("COM9", 9600, timeout=0.5)
            except:
                pass
    

    Currently the device must be plugged in when you start running the code. But you can put the initial serial.Serial call inside its own try-except block to avoid this. Or you could simply leave this line out completely, and it will still work fine.