Python – How to make a udev rule using a “sibling” device’s serial number (for USB devices with no unique serials)

arduinopythonudevusbusb device

I have testing equipment that is made-up of the following:

  • a USB Hub connected to the master PC, the hub is embedded in the test jig and is used so that we have only 1 USB cable running from the jig to the PC.
  • an Arduino UNO, connected by USB to the Hub
  • a measurement instrument (powermeter) connected to the USB hub through USB
  • another measurement device (thermometer) connected to the USB hub by USB
  • a Python script that runs the testing procedure from the master PC and communicates to Arduino and both instruments.

All this works well with my first jig but I need now to duplicate the testing setup (3 testing jigs on one PC). I want to assign udev persistent rules for the devices so the testers later just need to select 1, 2 or 3 depending on which jig they use, so they don't need to fiddle with the port numbers.

Is there a way to do a rule that would basically say:
assign symlink /dev/powermeter01 to the powermeter that is on the same USB hub as the Arduino with the serial xxxxxxx ?

For the Arduino, that is easy as there are proper serial numbers in the udevadm info, but for the powermeter, the serial number is always the same and for the thermometer, there is simply no serial number (thank you cheap suppliers!!!).

The USB hub also has apparently no serial.

Best Answer

Well, this is not an answer to the question but brings me a solution. so here it is.

I fiddle quite a bit with Udev rules, could not get anything better than my Arduino persistently appear on /dev/arduino01 (will use /dev/arduino02, /dev/arduino03,... for other jigs)

My testing script is in Python, I just found out that there is a nice library called pyudev so I decided to have a look at this route.

After just few minutes, I ended up with

from pyudev import *
context = Context()
Arduino = Device.from_device_file(context, '/dev/arduino01') 
Hub = Arduino.find_parent("usb","usb_device").find_parent("usb") #first find_parent brings me up to the USB device Arduino, another find_parent brings me to the Hub

Fixture = Enumerator(context)
for dev in Fixture.match_parent(Hub).match_subsystem('tty'):
    if (dev.get('ID_VENDOR_ID')=="10c4" and dev.get('ID_MODEL_ID')=="ea60"): #I got those ID through udevadm.
        powermeter=dev
    if (dev.get('ID_VENDOR_ID')=="067b" and dev.get('ID_MODEL_ID')=="2303"): #I got those ID through udevadm.
        temprecorder=dev

print('Arduino in on ' + str(Arduino.device_node)) #prints : Arduino is on /dev/ttyACM0
print('Powermeter in on ' + str(powermeter.device_node)) #prints : Powermeter is on /dev/ttyUSB1
print('Thermometer in on ' + str(temprecorder.device_node)) #prints : Thermometer is on /dev/ttyUSB0

This gives me the USB ports of my 3 devices of the test jig, I can now feed it to my testing routine script.

The more savvy between us will then have seen that my temprecorder is through a pl2303 USB-serial and my powermeter through a CP2102 USB-serial.