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.

Teensy Monitor Board

Teensy Monitor Board and 8-Channel Relay Board

Overview of the Teensy Monitor Board

A custom PC board was built to use a Teensy 4.1 Development Board as a base and control a lot of sensors and otherwise monitor the health of the robot. I call this board the Teensy Monitor Board.

It controls eight VL53L0X time of flight sensors used as proximity sensors, four HC-SR04 sonar sensors used as proximity sensors, a pair of I2C-bus motor current sensors, a color touchscreen, two analog motor temperature sensors, an 8-channel relay board, an Ethernet connector, and a serial UART connection to the RoboClaw motor controller. Additionally, there is a spare port for either another analog sensor or another serial UART connection, and a pair of SPI-bus ports that were originally intended to be used for motor current sensors.

The monitor board collects all of the sensor information and publishes it as a JSON message via the Ethernet connection. It also reads a status message from the primary computer. If the monitor detects an unexpected situation, it is able to shutdown the motors.

A block diagram overview of the Teensy Monitor Board

The monitor also reads the status of the RoboClaw motor controller and compares the status of the motors to the expected status as reported by the main processor.

The touchscreen display shows the status of all of the sensors on one panel, and the other panel provides a way to power off and on the primary and secondary computers or to reset either of those computers. It can also power off and on the RoboClaw motor controller.

An unexpected situation, which might cause the monitor to shut down the motors, occurs if any of the following is detected:

  • The current in either motor is out of range. Currents are read via the connection to the RoboClaw motor controller and, redundantly, by current sensors inline with the motor power. If any motor should stall or even be slowed down by an external force, the motor current spikes and could result in a meltdown of the winding.
  • The speed of either motor is significantly different than that commanded by the computers via the navigational software, an external game controller or a virtual keyboard teleoperation device.
  • The temperature of either motor is out of range. Even if the current to the motors stays within a safe range, over time the motors could gradually heat up to an unsafe temperature where the windings could short of melt.
  • The time of flight or sonar sensors detect that the robot is too near an object. The navigational software should have prevented the robot from getting too close.
  • The heartbeat timer or other status from the computers is out of range. This can occur especially if software crashes on either of the two computers.

The Touchscreen Display

The sensor status panel of the touchscreen display

The touchscreen display shows one of two different panels, selected by touching either the “POWER” or “SENSE” boxes in the upper right of the display.

The SENSE panel displays:

  • The eight time of flight sensor values. The sensors are mounted in pairs on each corner of the robot. The display shows the value of each sensor corresponding to the physical position of the sensor. For instance, the red value “0.049” above is showing the sensor distance, in meters, for the sensor that is mounted at the front left corner of the robot, facing forward. The value “0.255” above is showing the sensor distance for the sensor that is mounted in the back right corner of the robot, facing to the right. As a result, the eight sensors provide two forward looking, two backward looking, two left looking and two right lookng sensors.

    If the display value is white, the sensed distance is considered within the range that the navigational software should be able to deal with. If the value is red, an object or surface has gotten too close to one of the corners of the robot. If “rng” is shown as the value, then the range is far enough out to not be of interest to the monitor board.
  • The four sonar values. The sensors are mounted in the middle of the front, back, left and right sides of the robot. The same white/red rules apply as for the time of flight sensors. The LASER powered time of flight sensors have their pluses and minuses as to what kinds of objects they can detect, and the ultrasonic sonar sensors have a different set of pluses and minus. To provide extra projection, these two kinds of sensors somewhat overlap in detecting that the robot has gotten somewhere it shouldn’t have.
  • The two motor currents. The RobotClaw motor controller provides a readout of the motor currents, but it doesn’t always agree with independent observation. An extra pair of motor current sensors is provided as a redundant set of eyes. If the motor is pushing on something so the wheels are laboring or, worse, if the wheels stop turning altogether, the current in the motors can spike high enough to melt the wire windings.
  • The two motor temperatures. The motors can overheat either through gradual means from normal operation at high but expected current, from external causes such as radiation from the sun or from driving over a hot surface. Regardless of the cause, if the motor windings get too hot, they can short out or melt.
The power control panel of the touchscreen display

When the POWER panel is selected, you can turn on or off the power to the primary computer, the secondary computer or the RoboClaw motor controller. You can also reset the primary or secondary computer

The schematics of the Teensy Monitor Board can be seen here.