16

If I have multiple Arduinos connected over USB to a Linux computer, and they show up as

  • /tty/ACM0
  • /tty/ACM1
  • /tty/ACM2

How can I identify which Arduino is which without connecting to them via serial connection? Is there a serial number or a unique id on the Arduino?

Thank you for your time.

Situation: Uno R3, Mega, Leonardo with /ttyACM[1,2,3]

lsusb output with devices in the order mentioned above:

...
Bus 001 Device 011: ID 2341:0043
Bus 001 Device 013: ID 2341:8036
...
Bus 001 Device 014: ID 2341:0042

lsusb -d vendor:device -vvv shows for each one

Uno

Bus 001 Device 014: ID 2341:0042
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            2 Communications
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0         8
  idVendor           0x2341
  idProduct          0x0042
  bcdDevice            0.01
  iManufacturer           1 Arduino (www.arduino.cc)
  iProduct                2
  iSerial               220 55330313735351910141
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           62
    bNumInterfaces          2
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0xc0
      Self Powered
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         2 Communications
      bInterfaceSubClass      2 Abstract (modem)
      bInterfaceProtocol      1 AT-commands (v.25ter)
      iInterface              0
      CDC Header:
        bcdCDC               10.01
      CDC ACM:
        bmCapabilities       0x06
          sends break
          line coding and serial state
      CDC Union:
        bMasterInterface        0
        bSlaveInterface         1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval             255
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass        10 CDC Data
      bInterfaceSubClass      0 Unused
      bInterfaceProtocol      0
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x04  EP 4 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
Device Status:     0x0000
  (Bus Powered)

Leonardo:

Bus 001 Device 013: ID 2341:8036
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0        64
  idVendor           0x2341
  idProduct          0x8036
  bcdDevice            1.00
  iManufacturer           1 Arduino LLC
  iProduct                2 Arduino Leonardo
  iSerial                 0
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength          100
    bNumInterfaces          3
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              500mA
    Interface Association:
      bLength                 8
      bDescriptorType        11
      bFirstInterface         0
      bInterfaceCount         2
      bFunctionClass          2 Communications
      bFunctionSubClass       2 Abstract (modem)
      bFunctionProtocol       1 AT-commands (v.25ter)
      iFunction               0
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         2 Communications
      bInterfaceSubClass      2 Abstract (modem)
      bInterfaceProtocol      0 None
      iInterface              0
      CDC Header:
        bcdCDC               1.10
      CDC Call Management:
        bmCapabilities       0x01
          call management
        bDataInterface          1
      CDC ACM:
        bmCapabilities       0x06
          sends break
          line coding and serial state
      CDC Union:
        bMasterInterface        0
        bSlaveInterface         1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        2
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      0 None
      iInterface              0
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.01
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength     101
          Report Descriptor: (length is 101)
            Item(Global): Usage Page, data= [ 0x01 ] 1
                            Generic Desktop Controls
            Item(Local ): Usage, data= [ 0x02 ] 2
                            Mouse
            Item(Main  ): Collection, data= [ 0x01 ] 1
                            Application
            Item(Local ): Usage, data= [ 0x01 ] 1
                            Pointer
            Item(Main  ): Collection, data= [ 0x00 ] 0
                            Physical
            Item(Global): Report ID, data= [ 0x01 ] 1
            Item(Global): Usage Page, data= [ 0x09 ] 9
                            Buttons
            Item(Local ): Usage Minimum, data= [ 0x01 ] 1
                            Button 1 (Primary)
            Item(Local ): Usage Maximum, data= [ 0x03 ] 3
                            Button 3 (Tertiary)
            Item(Global): Logical Minimum, data= [ 0x00 ] 0
            Item(Global): Logical Maximum, data= [ 0x01 ] 1
            Item(Global): Report Count, data= [ 0x03 ] 3
            Item(Global): Report Size, data= [ 0x01 ] 1
            Item(Main  ): Input, data= [ 0x02 ] 2
                            Data Variable Absolute No_Wrap Linear
                            Preferred_State No_Null_Position Non_Volatile Bitfield
            Item(Global): Report Count, data= [ 0x01 ] 1
            Item(Global): Report Size, data= [ 0x05 ] 5
            Item(Main  ): Input, data= [ 0x03 ] 3
                            Constant Variable Absolute No_Wrap Linear
                            Preferred_State No_Null_Position Non_Volatile Bitfield
            Item(Global): Usage Page, data= [ 0x01 ] 1
                            Generic Desktop Controls
            Item(Local ): Usage, data= [ 0x30 ] 48
                            Direction-X
            Item(Local ): Usage, data= [ 0x31 ] 49
                            Direction-Y
            Item(Local ): Usage, data= [ 0x38 ] 56
                            Wheel
            Item(Global): Logical Minimum, data= [ 0x81 ] 129
            Item(Global): Logical Maximum, data= [ 0x7f ] 127
            Item(Global): Report Size, data= [ 0x08 ] 8
            Item(Global): Report Count, data= [ 0x03 ] 3
            Item(Main  ): Input, data= [ 0x06 ] 6
                            Data Variable Relative No_Wrap Linear
                            Preferred_State No_Null_Position Non_Volatile Bitfield
            Item(Main  ): End Collection, data=none
            Item(Main  ): End Collection, data=none

            ......

      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x84  EP 4 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
Device Status:     0x0000
  (Bus Powered)

And Mega256:

Bus 001 Device 014: ID 2341:0042
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            2 Communications
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0         8
  idVendor           0x2341
  idProduct          0x0042
  bcdDevice            0.01
  iManufacturer           1 Arduino (www.arduino.cc)
  iProduct                2
  iSerial               220 55330313735351910141
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           62
    bNumInterfaces          2
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0xc0
      Self Powered
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         2 Communications
      bInterfaceSubClass      2 Abstract (modem)
      bInterfaceProtocol      1 AT-commands (v.25ter)
      iInterface              0
      CDC Header:
        bcdCDC               10.01
      CDC ACM:
        bmCapabilities       0x06
          sends break
          line coding and serial state
      CDC Union:
        bMasterInterface        0
        bSlaveInterface         1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval             255
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass        10 CDC Data
      bInterfaceSubClass      0 Unused
      bInterfaceProtocol      0
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x04  EP 4 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
Device Status:     0x0000
  (Bus Powered)
  • lsusb -vvv says? – Ignacio Vazquez-Abrams Aug 7 '14 at 20:31
  • Can you unplug one and see which one disappears? – Anonymous Penguin Aug 7 '14 at 20:35
  • 1
    Not really, i want to do this for a remote solar-powered sensor array that will be very far away for someone to do that when it reboots. – vlad b. Aug 7 '14 at 20:46
  • Ignacio Vasquez-Abrams - how can I map the information from lsusb -vvv to /dev/ttyACM<x> ? IS there something in the lsusb output i can parse? Thank you for the idea! – vlad b. Aug 7 '14 at 20:48
  • Do you have another Arduino you can compare output with? – Ignacio Vazquez-Abrams Aug 7 '14 at 23:17
6

Assuming your distro is using udev:

udevadm info --query=all --name=/dev/ttyACM0

You might need root privileges to run this(sudo / su). It will output a list of information like this:

P: /devices/pci0000:00/0000:00:1d.2/usb4/4-2/4-2:1.0/tty/ttyACM0
N: ttyACM0
S: serial/by-id/usb-Dean_Camera_LUFA_USB-RS232_Adapter_74133353537351403012-if00
S: serial/by-path/pci-0000:00:1d.2-usb-0:2:1.0
E: DEVLINKS=/dev/serial/by-id/usb-Dean_Camera_LUFA_USB-RS232_Adapter_74133353537351403012-if00 /dev/serial/by-path/pci-0000:00:1d.2-usb-0:2:1.0
E: DEVNAME=/dev/ttyACM0
E: DEVPATH=/devices/pci0000:00/0000:00:1d.2/usb4/4-2/4-2:1.0/tty/ttyACM0
E: ID_BUS=usb
E: ID_MM_CANDIDATE=1
E: ID_MODEL=LUFA_USB-RS232_Adapter
E: ID_MODEL_ENC=LUFA\x20USB-RS232\x20Adapter
E: ID_MODEL_FROM_DATABASE=Uno R3 (CDC ACM)
E: ID_MODEL_ID=0043
E: ID_PATH=pci-0000:00:1d.2-usb-0:2:1.0
E: ID_PATH_TAG=pci-0000_00_1d_2-usb-0_2_1_0
E: ID_REVISION=0001
E: ID_SERIAL=Dean_Camera_LUFA_USB-RS232_Adapter_74133353537351403012
E: ID_SERIAL_SHORT=74133353537351403012
E: ID_TYPE=generic
E: ID_USB_DRIVER=cdc_acm
E: ID_USB_INTERFACES=:020201:0a0000:
E: ID_USB_INTERFACE_NUM=00
E: ID_VENDOR=Dean_Camera
E: ID_VENDOR_ENC=Dean\x20Camera
E: ID_VENDOR_FROM_DATABASE=Arduino SA
E: ID_VENDOR_ID=2341
E: MAJOR=166
E: MINOR=0
E: SUBSYSTEM=tty
E: UDEV_LOG=3
E: USEC_INITIALIZED=751387324986

This is for an Uno with modified firmware on the atmega16u2(usb to serial). The lines of interest are probably ID_MODEL_ID and ID_MODEL_FROM_DATABASE.

  • Thank you. I am using OpenWrt in this case with hotplug2 by default but I will try to switch to udev and test this. – vlad b. Aug 8 '14 at 9:27
5

You can add an alias for each. Then you know which is which. Here is a nice tutorial on how to set this up.

Here is a snippet that I wrote based on the tutorial. In the below example, I am using a FTDI RS232RL USB to Serial adapter, which I believe is what the Arduino uses too.

  1. In order to assign an alias to a USB-Serial device, we need to find some info on the device
  2. Plug in the usb. This assumes you have already install the drivers for this use be device and the device is visible when typing:

lsusb

  1. We are going to need the following a. The vendor id b. The product id c. The device serial number
  2. To do this, it takes a little bit of hunting. All your devices log entries in '/var/log/messages'.

Therefore we can read this file and find the correct USB:

grep "ftdi" /var/log/messages

You can also use "usb"

  1. This is find all messages tagged with ftdi:

  2. Next to ftdi_sio, there is a number like 1-1.2. This is the USB device

grep "usb 1-1.2" /var/log/messages

Or you can use:

dmesg | grep "usb 1-1.3"
  1. This gives us all the info we need:

USB Alias

  1. Now with the list of serial numbers in hand let’s create a UDEV ruleset that’ll make a nice symbolic link for each of these devices. UDEV rules are usually scattered into many files in /etc/udev/rules.d. Create a new file called 99-usb-serial.rules and put the following lines in there:

In this example my alias is called 'lcdbox'

SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="A601ERJJ", SYMLINK+="lcdbox"
  1. SYMLINK is the name of your alias. In this case my alias is lcdbox.
  2. Save the file and type

    sudo udevadm control --reload-rules

  3. Type

    ls –l /dev/lcdbox

lrwxrwxrwx 1 root root 7 Jan 1 1970 /dev/lcdbox -> ttyUSB0

  1. This shows that my lcdbox alias is mapped to ttyUSB0
4

This is quite easy! You have to customize the ftdi chips firmware and add an udev rule:

First, get ftdi_eeprom via apt-get or from the sources. Identify your device via lsusb and get the id:

lsusb

Bus 001 Device 005: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
....

Prepare a config and make sure, that vendor_id and product_id match. Customize the strings in the Strings section to get a unique id for your device.

vendor_id=0x0403    # Vendor ID
product_id=0x6001   # Product ID

max_power=50        # Max. power consumption: value * 2 mA. Use 0 if self_powered = true.

###########
# Strings #
########### 
manufacturer="FTDI"     # Manufacturer
product="Arduino Nano"      # Product
serial="arduino1"       # Serial

###########
# Options #
###########
self_powered=false  # Turn this off for bus powered
remote_wakeup=false # Turn this on for remote wakeup feature
use_serial=true     # Use the serial number string

# Normally out don't have to change one of these flags
# BM_type_chip=true         # Newer chips are all BM type
in_is_isochronous=false     # In Endpoint is Isochronous
out_is_isochronous=false    # Out Endpoint is Isochronous
suspend_pull_downs=false    # Enable suspend pull downs for lower power
change_usb_version=false    # Change USB Version
usb_version=0x0200      # Only used when change_usb_version is enabled

########
# Misc #
########

filename="eeprom.old"   # Filename, leave empty to skip file writing
cbus0=RXLED#
cbus1=TXLED#

Dump the current ftdi-firmware:

ftdi_eeprom --read-eprom

This command creates ${pwd}/eeprom.old, which contains the current firmware on the ftdi. Backup this file before continuing, because during flash-eeprom the file gets rewritten. After the backup, flash the ftdi:

ftdi_eeprom --flash-eeprom myconfig.conf

Now, create an udev rule, like so

SUBSYSTEMS=="usb", ATTRS{idProduct}=="6001", ATTRS{idVendor}=="0403", SYMLINK+="$attr{serial}", OWNER="bananapi", GROUP="pi", MODE="0777"

in /etc/udev/rules.d/90-arduino-usb.rules and restart udev.

service udev restart

Unplug and re-plug your device and try

ls -lah /dev/arduino1

Where arduino1 is the string defined under serial in the above .conf.

See also: Risks of ftdi_eeprom? - TX always high after flashing

3

I would have used a script to make an alias under /dev/ and also set group and rights on it with udev like the other answers.

But if I didn't have udev I would pipe lsusb into grep or awk, like lsusb|grep -e "idProduct".

Anyway, with udev rules or with lsusb and grep to identify USB devices, use idVendor, idProduct and iSerial in Device descriptor part of lsusb to make the proper idenification. The idVendor tells you the manufaturer, idProduct should indentify the product from the manufacturer but sometimes they use the same product id for more than one product. Lastly, if needed, the iSerial should be a unique id for each example of that product.

3

On Ubuntu 16.04 (and maybe previous versions or other distribs too), you can do:

> ls /dev/serial/by-id

which displays (on my box where an Arduino UNO is connected):

usb-Arduino__www.arduino.cc__0043_A4139363931351318241-if00

You can easily spot the Device ID 0043 (UNO) here.

This file is actually a link to /dev/ttyACM0 in my box.

2

You can always make some kind of id print via serial in void setup(). After the certain board is connected it will send this id to your USB interface (which you are listening with some kind of daemon app on your linux box). Once you receive id you can map it to it's path 'Arduino1' : '/dev/ttyACM0', 'Arduino2' : '/dev/ttyACM1', 'Arduino3' : '/dev/ttyACM2'...etc

Be aware that when device is disconnected for some reason it can change it's physical path so you might have to remap all of them. For this case it might be good to write separate function ex: get_id() which you can call any time (not only on startup).

1

I'd be mighty tempted to identify some other way, like having the sketch itself respond to a special identify command, just to avoid USB's odd ways of identifying devices.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.