pyserial icon indicating copy to clipboard operation
pyserial copied to clipboard

How to detect if a serial port is being used by another application?

Open sunbearc22 opened this issue 6 years ago • 7 comments

How do I detect if a serial port is being used by another application?

E.g. on a terminal, I have picocom /dev/ttyUSB0 -b 115200 running. Separately, I have an application running this script.

port='/dev/ttyUSB0'
try:
    serial_port = serial.Serial(port=port, baudrate=115200, timeout=1)
except serial.SerialException as e:
    print("could not open serial port '{}': {}".format(port, e))
else:
    print("serial port is not busy.")
    serial_port.close()

Yet this script returns serial port not busy..

sunbearc22 avatar Feb 15 '19 06:02 sunbearc22

According to serialposix.py#L523, the serial.Serial.read() method will throw a SerialException relating to device disconnected or multiple access on port?. I tried it but could not get the desired response. What am I doing wrong here? I think I have corrected my except expression here.

import serial

port='/dev/ttyUSB0'
try:
    serial_port = serial.Serial(port=port, baudrate=115200, timeout=5 )
    read_data = serial_port.read()
except serial.serialutil.SerialException as err:
    raise err
else:
    print('serial_port = ', serial_port.__dict__)
    print('read_data = ', read_data)
    print("0 serial port is not busy.")
    serial_port.close()

if not read_data:
    print("1 serial port is busy.")

Output:

serial_port =  {'is_open': True, 'portstr': '/dev/ttyUSB0', 'name': '/dev/ttyUSB0', '_port': '/dev/ttyUSB0', '_baudrate': 115200, '_bytesize': 8, '_parity': 'N', '_stopbits': 1, '_timeout': 5, '_write_timeout': None, '_xonxoff': False, '_rtscts': False, '_dsrdtr': False, '_inter_byte_timeout': None, '_rs485_mode': None, '_rts_state': True, '_dtr_state': True, '_break_state': False, '_exclusive': None, 'fd': 6, 'pipe_abort_read_r': 7, 'pipe_abort_read_w': 8, 'pipe_abort_write_r': 9, 'pipe_abort_write_w': 10}
read_data =  b''
0 serial port is not busy.
1 serial port is busy.

sunbearc22 avatar Feb 15 '19 15:02 sunbearc22

Trying the same code. Getting the expected results.

import serial 
def check_port_status(port_name: str, baudrate: int = 115200):

    if port_name is  None: 
        return 
    if baudrate is None:
        return 

    ## config 
    # baudrate = 115200 
    # port_name = "/dev/tty.SLAB_USBtoUART"

    try:
        # print("port name: ", port_name)
        # print("baudrate: ", baudrate)
        serial_port = serial.Serial(port=port_name, baudrate=baudrate, timeout=5)
        read_data = serial_port.read()
    except serial.serialutil.SerialException as err:
        ## get the error arguments and print 
        code, context = err.args # tuple 
        if(code == 16): 
            print(f"port {port_name} is busy, please close and try again.")
            pass 
        elif(code == 2): 
            print(f"port name: {port_name} is not valid.")
            pass 
        
        return (False, code) 

    else:
        print('serial_port = ', serial_port.__dict__)
        print('read_data = ', read_data)
        print("serial port is not busy.")
        serial_port.close()
        return (True, 1) 


if __name__ == "__main__":
    # main() 
    port_name = '/dev/tty.SLAB_USBtoUART'
    baudrate = 115200 
    check_port_status(port_name=port_name, baudrate=baudrate)

Output Instance 1 (Port is not busy):

serial_port =  {'is_open': True, 'portstr': '/dev/tty.SLAB_USBtoUART', 'name': '/dev/tty.SLAB_USBtoUART', '_port': '/dev/tty.SLAB_USBtoUART', '_baudrate': 115200, '_bytesize': 8, '_parity': 'N', '_stopbits': 1, '_timeout': 5, '_write_timeout': None, '_xonxoff': False, '_rtscts': False, '_dsrdtr': False, '_inter_byte_timeout': None, '_rs485_mode': None, '_rts_state': True, '_dtr_state': True, '_break_state': False, '_exclusive': None, 'fd': 3, 'pipe_abort_read_r': 4, 'pipe_abort_read_w': 5, 'pipe_abort_write_r': 6, 'pipe_abort_write_w': 7}
read_data =  b'x'
serial port is not busy.

Output Instance 2 (Port is busy): port /dev/tty.SLAB_USBtoUART is busy, please close and try again.

Output Instance 3 (Port is not active): port name: /dev/tty.SLAB_USBtoUART is not valid.

To reproduce: run: python3 <file_name.py>

deepsatflow avatar Oct 04 '22 18:10 deepsatflow

How do I detect if a serial port is being used by another application?

E.g. on a terminal, I have picocom /dev/ttyUSB0 -b 115200 running. Separately, I have an application running this script.

port='/dev/ttyUSB0'
try:
    serial_port = serial.Serial(port=port, baudrate=115200, timeout=1)
except serial.SerialException as e:
    print("could not open serial port '{}': {}".format(port, e))
else:
    print("serial port is not busy.")
    serial_port.close()

Yet this script returns serial port not busy..

Is this issue resolved?

praveena2s avatar Apr 18 '23 22:04 praveena2s

Trying the same code. Getting the expected results.

import serial 
def check_port_status(port_name: str, baudrate: int = 115200):

    if port_name is  None: 
        return 
    if baudrate is None:
        return 

    ## config 
    # baudrate = 115200 
    # port_name = "/dev/tty.SLAB_USBtoUART"

    try:
        # print("port name: ", port_name)
        # print("baudrate: ", baudrate)
        serial_port = serial.Serial(port=port_name, baudrate=baudrate, timeout=5)
        read_data = serial_port.read()
    except serial.serialutil.SerialException as err:
        ## get the error arguments and print 
        code, context = err.args # tuple 
        if(code == 16): 
            print(f"port {port_name} is busy, please close and try again.")
            pass 
        elif(code == 2): 
            print(f"port name: {port_name} is not valid.")
            pass 
        
        return (False, code) 

    else:
        print('serial_port = ', serial_port.__dict__)
        print('read_data = ', read_data)
        print("serial port is not busy.")
        serial_port.close()
        return (True, 1) 


if __name__ == "__main__":
    # main() 
    port_name = '/dev/tty.SLAB_USBtoUART'
    baudrate = 115200 
    check_port_status(port_name=port_name, baudrate=baudrate)

Output Instance 1 (Port is not busy):

serial_port =  {'is_open': True, 'portstr': '/dev/tty.SLAB_USBtoUART', 'name': '/dev/tty.SLAB_USBtoUART', '_port': '/dev/tty.SLAB_USBtoUART', '_baudrate': 115200, '_bytesize': 8, '_parity': 'N', '_stopbits': 1, '_timeout': 5, '_write_timeout': None, '_xonxoff': False, '_rtscts': False, '_dsrdtr': False, '_inter_byte_timeout': None, '_rs485_mode': None, '_rts_state': True, '_dtr_state': True, '_break_state': False, '_exclusive': None, 'fd': 3, 'pipe_abort_read_r': 4, 'pipe_abort_read_w': 5, 'pipe_abort_write_r': 6, 'pipe_abort_write_w': 7}
read_data =  b'x'
serial port is not busy.

Output Instance 2 (Port is busy): port /dev/tty.SLAB_USBtoUART is busy, please close and try again.

Output Instance 3 (Port is not active): port name: /dev/tty.SLAB_USBtoUART is not valid.

To reproduce: run: python3 <file_name.py>

Hi Satish, Thanks for the write up. (Did you validate your code when the port was occupied by picocom application?) I was also facing the same issue as @sunbearc22 was facing. I have installed latest pyserial v3.5 on Ubuntu v22.04.

I have re-used your code and tried against 3 different scenarios

Scen-1. Port /dev/ttyACM4 was fee (i.e., port is available/ready to use) Verification-1: Cross verification using lsof command which gives no output $ lsof /dev/ttyACM4

Verification-2: Using your code,

$ python3 serial_port_busy_check.py
serial_port =  {'is_open': True, 'portstr': '/dev/ttyACM4', 'name': '/dev/ttyACM4', '_port': '/dev/ttyACM4', '_baudrate': 115200, '_bytesize': 8, '_parity': 'N', '_stopbits': 1, '_timeout': 5, '_write_timeout': None, '_xonxoff': False, '_rtscts': False, '_dsrdtr': False, '_inter_byte_timeout': None, '_rs485_mode': None, '_rts_state': True, '_dtr_state': True, '_break_state': False, '_exclusive': None, 'fd': 3, 'pipe_abort_read_r': 4, 'pipe_abort_read_w': 5, 'pipe_abort_write_r': 6, 'pipe_abort_write_w': 7}
read_data =  b'E'
serial port is not busy.

Scen-2. Port /dev/ttyACM4 not present in the system (i.e., hardware is not connected to the system) Verification-1: Cross verification using lsof command which gives error

$ lsof /dev/ttyACM4
lsof: status error on /dev/ttyACM4: No such file or directory
;
;

Verification-2: Using your code,

$ python3 serial_port_busy_check.py
port name: /dev/ttyACM4 is not valid.

Scen-3. Port /dev/ttyACM4 was used by picocom application (i.e., port is already busy with other process) Verification-1: Cross verification using lsof command which gives port details in the output

$ lsof /dev/ttyACM4
COMMAND    PID     USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
picocom 383023 xyz_user    3uW  CHR  166,4      0t0 1398 /dev/ttyACM4

Verification-2: Using your code,

$ python3 serial_port_busy_check.py
serial_port =  {'is_open': True, 'portstr': '/dev/ttyACM4', 'name': '/dev/ttyACM4', '_port': '/dev/ttyACM4', '_baudrate': 115200, '_bytesize': 8, '_parity': 'N', '_stopbits': 1, '_timeout': 5, '_write_timeout': None, '_xonxoff': False, '_rtscts': False, '_dsrdtr': False, '_inter_byte_timeout': None, '_rs485_mode': None, '_rts_state': True, '_dtr_state': True, '_break_state': False, '_exclusive': None, 'fd': 3, 'pipe_abort_read_r': 4, 'pipe_abort_read_w': 5, 'pipe_abort_write_r': 6, 'pipe_abort_write_w': 7}
read_data =  b''
serial port is not busy.

my concern is Scen-3 where code logic shows 'serial port is not busy' even the port was occupied by other application (in my case picocom application.)

Wondering why read_data = serial_port.read() instruction is not detecting the busy port, where lsof command is able to....

If anybody has resolved this issue, feel free to add the comments.

Thanks

praveena2s avatar Apr 18 '23 22:04 praveena2s

i'm using this method to fill tkinter combobox.

def serial_ports():
    """ Lists serial port names

        :raises EnvironmentError:
            On unsupported or unknown platforms
        :returns:
            A list of the serial ports available on the system
    """
    if sys.platform.startswith('win'):
        ports = ['COM%s' % (i + 1) for i in range(256)]
    elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
        # this excludes your current terminal "/dev/tty"
        ports = glob.glob('/dev/tty[A-Za-z]*')
    elif sys.platform.startswith('darwin'):
        ports = glob.glob('/dev/tty.*')
    else:
        raise EnvironmentError('Unsupported platform')

    result = []
    for port in ports:
        try:
            s = serial.Serial(port)
            s.close()
            result.append(port)
        except (OSError, serial.SerialException):
            pass
    return result

harryberlin avatar Apr 19 '23 20:04 harryberlin

@harryberlin This has nothing to do with the issue, and is also pretty bad to return 256 COM ports on Windows. You can just use "import serial.tools.list_ports", and then use this:

        ports = [port.device for port in serial.tools.list_ports.comports()]

to get only the usable serial ports. This should work on all platforms (tested on Linux).

Frank-Buss avatar Jul 10 '24 10:07 Frank-Buss

ok, did add extra sorted() ports = [port.device for port in sorted(serial.tools.list_ports.comports())]

harryberlin avatar Jul 10 '24 19:07 harryberlin