udev rules for multiply Teensy 4.1 devices

My latest robot, Raven, now has two custom PC boards in it, each containing a Teensy 4.1 processor. One board handles all the proximity sensors and does a few other chores, the other manages the touch screen and senses a few other values, such as the battery voltage, and does some other control functions. As with any USB device plugged into Linux, however, the name that gets assigned to the device under the /dev directory can change every time you power up the Linux computer or merely unplug and then plug the USB device back in. Fortunately, the manufacture of the Teensy 4.1 devices supplies a unique serial number to each device, and I use that attribute value to create custom udev rules that create new symbolic names under the /dev directory that are constant. One name, /dev/teensy_sensor will always point to the correct /dev/ttyUSBX device (where X is some digit), no matter how Linux renames it when it discovers the device. And /dev/teensy_screen will always point to the other device. Any script or code I create that wants a device name for communicating with the Teensy 4.1 device can use the appropriate name I created via a custom udev rule.

The default udev rule file supplied by the manufacture of the Teensy 4.1 device does not provide any static /dev name if you plug in multiple devices. The default usb device names of, e.g., /dev/ttyACM0, /dev/ttyACM1, etc. will point to different Teensy devices every time you power up or unplug then plug in your Teensy device. What you want is a symlink providing a name of your choosing that reliably points to the correct Teensy device.

If you use, e.g., the lsusb command, you might see something like the following, where lines that don’t refer to the Teensy devices are replaced with ellipses:

...
Bus 001 Device 108: ID 16c0:0483 Van Ooijen Technische Informatica Teensyduino Serial
,,,
Bus 001 Device 111: ID 16c0:0483 Van Ooijen Technische Informatica Teensyduino Serial
...

You could then look at more details of a specific device with a command such as “lsusb -v -s 001:108“, where “-v” asks for verbose output and the pair of colon-separated numbers after “-s” specify the bus and device of the specific USB device you want to inspect. The beginning of the resulting output might look something like:

Bus 001 Device 108: ID 16c0:0483 Van Ooijen Technische Informatica Teensyduino Serial
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass          239 Miscellaneous Device
  bDeviceSubClass         2 
  bDeviceProtocol         1 Interface Association
  bMaxPacketSize0        64
  idVendor           0x16c0 Van Ooijen Technische Informatica
  idProduct          0x0483 Teensyduino Serial
  bcdDevice            2.80
  iManufacturer           1 Teensyduino
  iProduct                2 USB Serial
  iSerial                 3 8771250
...

Where the trailing lines are replaced in the above with an ellipsis.

To create a udev rule specific one Teensy 4.1 device, you need to add a specifier to the rule that qualifies in the iSerial value. In the default udev file provided by the manufacture, namely the file at /etc/udev/rules.d/00-teensy.rules, you will see a line like:

ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789a]*", ENV{MTP_NO_PROBE}="1"

Replace that line with multiple lines that include a qualifier on the serial number, and adding in a SYMLINK action. Also add the KERNEL qualifier at the beginning of the rule, else when you reprogram the Teensy, it will link to a different class of device. For example:

KERNEL=="ttyACM*", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789a]*", ATTRS{serial}=="8771250", ENV{MTP_NO_PROBE}="1",  SYMLINK+="teensy_sensor"
KERNEL=="ttyACM*", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789a]*", ATTRS{serial}=="12135850", ENV{MTP_NO_PROBE}="1",  SYMLINK+="teensy_screen"

With the above, my Teensy 4.1 device with an iSerial value of 8771250 will have a symlink named /dev/teensy_sensor that points to the correct /dev/ttyACMX device (where X is a digit), no matter which X value Linux assigns to the device when it is plugged in. Likewise, my other Teensy 4.1 device, with a different serial number, will always get a symlink named /dev/teensy_screen that I can use as a device name in any code or script that would normally want a device name like, e.g., /dev/ttyACM0.

Note that even though the attribute is named iSerial when shown by lsusb, the qualifier name in the udev rule must use serial as the name.

Leave a comment

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