Assigning fixed names for USB devices in Raspberry Pi

You can find the USB devices by ls /dev/*USB*

pi@raspberrypi:~ $ ls /dev/*USB*
/dev/ttyUSB0  /dev/ttyUSB1
pi@raspberrypi:~ $

and more information by lsusb or dmesg | grep ttyUSB.

pi@raspberrypi:~ $ lsusb
Bus 002 Device 003: ID 05e3:0626 Genesys Logic, Inc.
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 006: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
Bus 001 Device 005: ID 046d:c534 Logitech, Inc. Unifying Receiver
Bus 001 Device 004: ID 05e3:0610 Genesys Logic, Inc. 4-port hub
Bus 001 Device 003: ID 1a86:7523 QinHeng Electronics HL-340 USB-Serial adapter
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
pi@raspberrypi:~ $
pi@raspberrypi:~ $
pi@raspberrypi:~ $ dmesg | grep ttyUSB
[    5.284638] usb 1-1.3: ch341-uart converter now attached to ttyUSB0
[    5.312424] usb 1-1.4: FTDI USB Serial Device converter now attached to ttyUSB1

But sometimes, you may want to use the /dev/ path to descript your USB, such as selecting the serial port in NodeRed.

Procedure of assigning fixed name

  1. Finding unique attributes for your USB devices
  2. Creating udev rules for your applications
  3. Creating match keys and assignment keys to bind the USB devices to your desired name.
  4. Applying rules

Finding unique attributes of USB devices

Using udevadm info [options] [devpath|file|unit…]to check the details of the USB devices. -a option stands for --attribute-walk.

pi@raspberrypi:/etc/udev/rules.d $ udevadm info -a /dev/ttyUSB0

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.3/1-1.3:1.0/ttyUSB0/tty/ttyUSB0':
    KERNEL=="ttyUSB0"
    SUBSYSTEM=="tty"
    DRIVER==""

  looking at parent device '/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.3/1-1.3:1.0/ttyUSB0':
    KERNELS=="ttyUSB0"
    SUBSYSTEMS=="usb-serial"
    DRIVERS=="ch341-uart"
    ATTRS{port_number}=="0"

  looking at parent device '/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.3/1-1.3:1.0':
    KERNELS=="1-1.3:1.0"
    SUBSYSTEMS=="usb"
    DRIVERS=="ch341"
    ATTRS{bNumEndpoints}=="03"
    ATTRS{bInterfaceProtocol}=="02"
    ATTRS{bInterfaceSubClass}=="01"
    ATTRS{bInterfaceClass}=="ff"
    ATTRS{bAlternateSetting}==" 0"
    ATTRS{bInterfaceNumber}=="00"
    ATTRS{supports_autosuspend}=="1"
    ATTRS{authorized}=="1"

  looking at parent device '/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.3':
    KERNELS=="1-1.3"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{bcdDevice}=="0264"
    ATTRS{busnum}=="1"
    ATTRS{authorized}=="1"
    ATTRS{bDeviceProtocol}=="00"
    ATTRS{urbnum}=="31"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bDeviceClass}=="ff"
    ATTRS{devspec}=="(null)"
    ATTRS{bMaxPower}=="98mA"
    ATTRS{idProduct}=="7523"
    ATTRS{rx_lanes}=="1"
    ATTRS{tx_lanes}=="1"
    ATTRS{product}=="USB Serial"
    ATTRS{bNumInterfaces}==" 1"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{speed}=="12"
    ATTRS{removable}=="unknown"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{configuration}==""
    ATTRS{devpath}=="1.3"
    ATTRS{quirks}=="0x0"
    ATTRS{bMaxPacketSize0}=="8"
    ATTRS{bmAttributes}=="80"
    ATTRS{ltm_capable}=="no"
    ATTRS{version}==" 1.10"
    ATTRS{devnum}=="65"
    ATTRS{maxchild}=="0"
    ATTRS{idVendor}=="1a86"

...
...
...

Some unique attributes are ” idProduct”, “idVendor”. Make sure you are searching these attributes under the corresponding Port, in my case, usb1/1-1/1-1.3. Repeat the udevadm info command and search for attributes for the other devices. Remember to change the /dev/ttyUSB0 to /dev/ttyUSB1 or else only plug a single device at a time.

To understand more about USB ports, please visit Extra readings down below.

Creating udev rules

Linux stores file-like device nodes in /dev directory. Each node points to a part of a system or device, no matter it exists or not. Because the /dev directories contain every device that may exits in the system, it may be very large and become difficult to manage.

udev plays an important role to manage /dev directories by providing a path forward, by matching information provided by sysfs and rules provided by users.

udev files should be kept in /etc/udev/rules.d directories and with a .rulessuffix. Files in /etc/udev/rules.d are parsed in lexical order. In general, you want your rule file to be parsed first, so it is suggested that /etc/udev/rules.d/10-local.rulesis a good choice.

sudo touch /etc/udev/rules.d/10-local.rules to create a rule file.

In a rules file, lines starting with “#” are treated as comments. Every other non-blank line is a rule. Rules cannot span multiple lines.

To learn more about udev, you may man udev man udevadmor visit http://www.reactivated.net/writing_udev_rules.html#udevinfo

Writing rules

Each line of rule should contain at least one match key and at least one assignment to construct a key-value pair. When all match keys are fulfilled, the rule will apply and the action of assignment will be performed.

Do not insert any line breaks in the rules, udev will see your one rule as multiple rules. And here are my rules to assign a new name for the 2 USB devices. Just add the following lines in your 10-local.rules file.

SUBSYSTEM=="tty", ATTRS{idProduct}=="7523", ATTRS{idVendor}=="1a86", SYMLINK+="ttyUSB_HL-340_device"
SUBSYSTEM=="tty", ATTRS{idProduct}=="6001", ATTRS{idVendor}=="0403", SYMLINK+="ttyUSB_FT232_device"

SUBSYSTEM will match against the subsystem of the device.
SYMLINK is the alternative name(s) you want to assign to the USB device. This will not change or hide the original name but only provide an alternative name to link to the device.

Besides an exact matching of strings, you could also use shell-style pattern matching, such as *, ? and [].

Applying rules

Run udevadm trigger to apply the new rules.

sudo udevadm trigger

To check the result.


pi@raspberrypi:/etc/udev/rules.d $ ls -l /dev/ttyUSB*
crw-rw---- 1 root dialout 188, 0 Dec  3 12:05 /dev/ttyUSB0
crw-rw---- 1 root dialout 188, 1 Dec  3 12:05 /dev/ttyUSB1
lrwxrwxrwx 1 root root         7 Dec  3 12:05 /dev/ttyUSB_FT232_device -> ttyUSB1
lrwxrwxrwx 1 root root         7 Dec  3 12:05 /dev/ttyUSB_HL-340_device -> ttyUSB0

If you know the top-level device path, you can use udevadm test to show the action. Or else, just use

pi@raspberrypi:/etc/udev/rules.d $ udevadm test -a -p  $(udevadm info -q path -n /dev/ttyUSB_FT232_device)

Serial port name in Node-red

Now you can specify the USB you are pointing to in Node-red rather than guessing which is ttyUSB0 and ttyUSB1.


———————-This is separator———————–

Extra readings

You may use lsusb -t to explore what devices are connected to your pi. The following corresponds to no USB device connected.

pi@raspberrypi:~ $ lsusb -t
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 5000M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M

And the following corresponds to 4 USB devices connected. Dev 31 is a wireless mouse and keyboard. All devices are via USB2.0.

pi@raspberrypi:~ $ lsusb -t
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 5000M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M
        |__ Port 1: Dev 27, If 0, Class=Vendor Specific Class, Driver=ch341, 12M
        |__ Port 2: Dev 31, If 0, Class=Human Interface Device, Driver=usbhid, 12M
        |__ Port 2: Dev 31, If 1, Class=Human Interface Device, Driver=usbhid, 12M
        |__ Port 3: Dev 26, If 0, Class=Vendor Specific Class, Driver=ftdi_sio, 12M
        |__ Port 4: Dev 30, If 0, Class=Hub, Driver=hub/4p, 480M

If you have a USB hub, then those connected devices will be under the hub port. In my case, the hub is connected to Port 3.


pi@raspberrypi:~ $ lsusb -t
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 5000M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M
        |__ Port 3: Dev 36, If 0, Class=Hub, Driver=hub/4p, 480M
            |__ Port 1: Dev 37, If 0, Class=Human Interface Device, Driver=usbhid, 12M
            |__ Port 1: Dev 37, If 1, Class=Human Interface Device, Driver=usbhid, 12M
            |__ Port 2: Dev 41, If 0, Class=Vendor Specific Class, Driver=ftdi_sio, 12M
            |__ Port 3: Dev 42, If 0, Class=Vendor Specific Class, Driver=ch341, 12M

If you have USB 3.0 devices, they will be using Bus 02. In my case, both the USB hub and USB flash drive support USB 3.0.

pi@raspberrypi:~ $ lsusb -t
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 5000M
    |__ Port 1: Dev 13, If 0, Class=Hub, Driver=hub/4p, 5000M
        |__ Port 4: Dev 14, If 0, Class=Mass Storage, Driver=usb-storage, 5000M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M
        |__ Port 1: Dev 56, If 0, Class=Hub, Driver=hub/4p, 480M

The number after the word ‘usb’ in dmesg is actually the USB Bus and Port. In my case, the ch341-uart converter is under bus 1, port 1.3. In similar manner, usb 3.0 devices will be usb 2-X.X.

[161104.469392] usb 1-1.3: ch341-uart converter now attached to ttyUSB1

Leave a Reply

Your email address will not be published. Required fields are marked *