How can I discover the actual device on a virtual COM port on Linux?

Hi, I have a hobby electronics workshop with many virtual USB COM port devices such as multimeters, power supplies and more. They all have some kind of USB to serial converter.

My problem is that I can't rely on /dev/ttyUSB* because the number can change. Also, as far as I know, I can't use symlinks that I created with udev because they are ignored. serialport::available_ports() only lists standard ports, but not symlinks.

I also don't want to send a command like "*IDN?" to every available port to identify it, as some devices may go into an undefined state.

I can identify some devices by vendor and product ID, but some devices have identical USB to serial converters with the same numbers. These do not have a unique serial number. I have also tried to use it to find a device, but I get a path such as /dev/bus/usb/001/013. Unfortunately, I can't get any further with that either.

I have already read through various pages and asked ChatGPT, but have not yet found a solution.

How can I ( on Linux) identify and communicate with a specific device regardless of whether it is connected to /dev/ttyUSB0 or /dev/ttyUSB4?

The answer is obvious and you have already discovered it: you can't. Neither on Linux nor on MacOS or Windows.

Serial ports just don't have any PnP in them. Technology predates the idea by several years. It comes from an era where manual dip-switches ruled the world.

That's why all apps that work with devices over serial ports offer you configuration options.

So you are basically limited to options you have already discovered. What's worse: USB doesn't give you any options to distinguish identical devices either.

Your best bet is to test if paths such as /dev/bus/usb/001/013 remain stable between reboots (they may or they may not depending on the hardware).

It's because there are no [generic and reliable] solution. Different people invent different ad-hoc kludges for their systems (e.g. you may power on these USB devices in staggered fashion to, hopefully, give them stable numbers).

In the past I have managed to stabilise the name in /dev that a particular USB serial/adapter is assigned by specifying udev rules in Linux. At least with that if every one of you USB adapters can be uniquely identified by it's USB vendor and product ids they can be assigned unique names.

See: How udev rules can help us to recognize a usb-to-serial device over /dev/tty interface - DEV Community

That still requires one to dedicate particular USB/Serial adapters to particular external devices. But at leas the names won't jump around depending on how they are randomly plugged in and out.

1 Like

Maybe I found a solution, that may work at least in most cases.

For most devices I can create a symlink with udev rules based on vendor and product ID and I try to find a unique identifier. For my two CH341 I found different Revision Numbers, one of my FTDI has a serial number and so on.

Then I can use use fs::read_link to follow the symlink to it's origin

use std::fs;

fn main() {
    let symlink_path = "/dev/ttyMoxa"; // Beispiel-Symlink-Pfad

    match fs::read_link(symlink_path) {
        Ok(target) => {
            if let Some(target_path) = target.to_str() {
                println!(
                    "Der Ziel-Pfad des Symlinks {} ist: /dev/{}",
                    symlink_path, target_path
                );
            } else {
                println!(
                    "Ungültiger UTF-8-Zeichenfolgenfehler im Ziel-Pfad des Symlinks {}",
                    symlink_path
                );
            }
        }
        Err(e) => eprintln!(
            "Fehler beim Lesen des Symlink-Ziel-Pfads {}: {}",
            symlink_path, e
        ),
    }
}

This might not work for 100% of possible devices, but maybe for enough of the devices I use.

Edit: maybe I don't even need this. I tried to use the symlinks instead of /dev/ttyUSB* again and surprisingly it works.

You can use symlinks with serialport::new(), it's just that serialport::available_ports() won't find them. I use this with udev rules that create symlinks with useful names for my devices, so that I can rely on symlinks on "real" systems, and create symlinks manually for test systems.

Thanks for confirmation. To detect available symlinks I can just use normal file system operations.

Now I just have one problem: how can I create symlinks for a specific device if I don't find any unique identifier?

That now comes down to the specifics of the device; how do you do that manually, instead of programmatically? Once you've worked out how you'd do it by hand, then it becomes possible to work out how to automate it (or it becomes clear that this is not a solvable problem).

The only way (that I know of) to do this manually is to connect the device and look in /dev to see which new device has been added.

So all I can do programmatically is to set the program into some kind of "listen" mode and wait until the device is connected and store this info until the next start.

Edit: I created udev rules for most of my devices and it works so far. I can use them within my programs, but is still useless for terminal programs like CuteCom or Moserial. They ignore symlinks and I have to guess which ttyUSB* I am looking for.

Anyway, thanks everyone for help

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.