はじめに
この記事は、CPS Lab Advent Calendar 2018の13日目の記事です。
12日目の記事は【Web Speech API】Speech Recognition ホームページでブラウザの音声認識を使う(無料)
14日目の記事はLabmartの顔認証を実装してみる
背景
画像のように同種のドライバーのUSB機器を接続したとき、それぞれ挿した順番に`/dev`に認識されてしまいます。 例えば画像のようにArduinoを2つ接続すると両方ともACMのドライバーなので以下のようになります。pi@raspberrypi:~ $ ls /dev | grep ttyACM*
ttyACM0
ttyACM1
これだとどっちがArduinoMEGAでどっちがArduinoUNOなのかわかりません。
USBカメラを複数個接続したときも/dev/video*
で認識されるのでどれがどのカメラかわからなくなります。
そこで今USBポートに接続されている機器を識別する方法をまとめてみました。
環境
RaspberryPi2(RASBIAN JESSIE)
どう認識されてるのかみてみる
まずUSBに接続されている機器を確認するコマンドとしてlsusb
コマンドがあります。
先程の画像の状態でコマンドを叩くと
pi@raspberrypi:~ $ lsusb
Bus 001 Device 006: ID 2019:ab2a PLANEX GW-USNano2 802.11n Wireless Adapter [Realtek RTL8188CUS]
Bus 001 Device 005: ID 2341:0043 Arduino SA Uno R3 (CDC ACM)
Bus 001 Device 004: ID 2341:0042 Arduino SA Mega 2560 R3 (CDC ACM)
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
こんな感じでUNO R3
とMEGA 2560 R3
が別のものとしてちゃんと認識されていることがわかります。
よく見るとUNO R3
がID 2341:0043
、MEGA 2560 R3、
がID 2341:0042
で認識されているのでここで条件分けしてあげれば識別できそうです。
2341
のほうがidVendor
その右がidProduct
です。
識別するその1
USB機器が接続されたときにどのようにマウントするかを決めたルールが/etc/udev/rules.d/99-com.rules
に記述されています。
今回はここの末尾にUNO R3
とMEGA 2560 R3
を識別するルールをidVendor
とidProduct
をもとに追加します。
# for UNO
KERNEL=="ttyACM*", ATTRS{idVendor}=="2341", ATTRS{idProduct}=="0043", SYMLINK+="ttyUSB_UNO"
# for MEGA
KERNEL=="ttyACM*", ATTRS{idVendor}=="2341", ATTRS{idProduct}=="0042", SYMLINK+="ttyUSB_MEGA"
このような記述を末尾に追加してリブートすると以下のように別の名前で識別されて、それぞれが対応したttyACM*
にリンクされている事がわかります。
pi@raspberrypi:~ $ ls /dev |grep USB
ttyUSB_MEGA
ttyUSB_UNO
pi@raspberrypi:~ $ ls -l /dev |grep USB
lrwxrwxrwx 1 root root 7 Dec 13 19:12 ttyUSB_MEGA -> ttyACM0
lrwxrwxrwx 1 root root 7 Dec 13 19:12 ttyUSB_UNO -> ttyACM1
識別するその2
lsusbで観察
写真のようにesp8266とesp32の開発ボードを接続した場合これらは同じIDのFTDIチップとして認識されてしまい`lsusb`では区別できません。pi@raspberrypi:~ $ ls /dev | grep ttyUSB*
ttyUSB0
ttyUSB1
pi@raspberrypi:~ $ lsusb
Bus 001 Device 006: ID 2019:ab2a PLANEX GW-USNano2 802.11n Wireless Adapter [Realtek RTL8188CUS]
Bus 001 Device 008: ID 0403:6015 Future Technology Devices International, Ltd Bridge(I2C/SPI/UART/FIFO)
Bus 001 Device 007: ID 0403:6015 Future Technology Devices International, Ltd Bridge(I2C/SPI/UART/FIFO)
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
lsusb -v
でオプションを付けて観察してみます
Bus 001 Device 008: ID 0403:6015 Future Technology Devices International, Ltd Bridge(I2C/SPI/UART/FIFO)
Couldn't open device, some information will be missing
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 0 (Defined at Interface level)
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 8
idVendor 0x0403 Future Technology Devices International, Ltd
idProduct 0x6015 Bridge(I2C/SPI/UART/FIFO)
bcdDevice 10.00
iManufacturer 1
iProduct 2
iSerial 3
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 32
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0xa0
(Bus Powered)
Remote Wakeup
MaxPower 90mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 255 Vendor Specific Subclass
bInterfaceProtocol 255 Vendor Specific Protocol
iInterface 2
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
Bus 001 Device 007: ID 0403:6015 Future Technology Devices International, Ltd Bridge(I2C/SPI/UART/FIFO)
Couldn't open device, some information will be missing
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 0 (Defined at Interface level)
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 8
idVendor 0x0403 Future Technology Devices International, Ltd
idProduct 0x6015 Bridge(I2C/SPI/UART/FIFO)
bcdDevice 10.00
iManufacturer 1
iProduct 2
iSerial 3
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 32
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0xa0
(Bus Powered)
Remote Wakeup
MaxPower 90mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 255 Vendor Specific Subclass
bInterfaceProtocol 255 Vendor Specific Protocol
iInterface 2
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
この情報の範囲だとユニークな属性は見当たらなさそうです。
そこでudevadm
コマンドを使います。
udevadmで観察
udevadm
では以下のように観察したいデバイスのパスを渡してあげます
pi@raspberrypi:~ $ udevadm info -a -p $(udevadm info -q path -n /dev/ttyUSB0)
Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.
looking at device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.2/1-1.2:1.0/ttyUSB0/tty/ttyUSB0':
KERNEL=="ttyUSB0"
SUBSYSTEM=="tty"
DRIVER==""
looking at parent device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.2/1-1.2:1.0/ttyUSB0':
KERNELS=="ttyUSB0"
SUBSYSTEMS=="usb-serial"
DRIVERS=="ftdi_sio"
ATTRS{port_number}=="0"
ATTRS{latency_timer}=="1"
looking at parent device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.2/1-1.2:1.0':
KERNELS=="1-1.2:1.0"
SUBSYSTEMS=="usb"
DRIVERS=="ftdi_sio"
ATTRS{bInterfaceClass}=="ff"
ATTRS{bInterfaceSubClass}=="ff"
ATTRS{bInterfaceProtocol}=="ff"
ATTRS{bNumEndpoints}=="02"
ATTRS{authorized}=="1"
ATTRS{supports_autosuspend}=="1"
ATTRS{bAlternateSetting}==" 0"
ATTRS{bInterfaceNumber}=="00"
ATTRS{interface}=="FT231X USB UART"
looking at parent device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.2':
KERNELS=="1-1.2"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bDeviceProtocol}=="00"
ATTRS{devpath}=="1.2"
ATTRS{idVendor}=="0403"
ATTRS{speed}=="12"
ATTRS{bNumInterfaces}==" 1"
ATTRS{bConfigurationValue}=="1"
ATTRS{bMaxPacketSize0}=="8"
ATTRS{busnum}=="1"
ATTRS{devnum}=="7"
ATTRS{configuration}==""
ATTRS{bMaxPower}=="90mA"
ATTRS{authorized}=="1"
ATTRS{bmAttributes}=="a0"
ATTRS{bNumConfigurations}=="1"
ATTRS{maxchild}=="0"
ATTRS{bcdDevice}=="1000"
ATTRS{avoid_reset_quirk}=="0"
ATTRS{quirks}=="0x0"
ATTRS{serial}=="DN02BCJT"
ATTRS{version}==" 2.00"
ATTRS{urbnum}=="16"
ATTRS{ltm_capable}=="no"
ATTRS{manufacturer}=="FTDI"
ATTRS{removable}=="removable"
ATTRS{idProduct}=="6015"
ATTRS{bDeviceClass}=="00"
ATTRS{product}=="FT231X USB UART"
looking at parent device '/devices/platform/soc/3f980000.usb/usb1/1-1':
KERNELS=="1-1"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bDeviceProtocol}=="02"
ATTRS{devpath}=="1"
ATTRS{idVendor}=="0424"
ATTRS{speed}=="480"
ATTRS{bNumInterfaces}==" 1"
ATTRS{bConfigurationValue}=="1"
ATTRS{bMaxPacketSize0}=="64"
ATTRS{busnum}=="1"
ATTRS{devnum}=="2"
ATTRS{configuration}==""
ATTRS{bMaxPower}=="2mA"
ATTRS{authorized}=="1"
ATTRS{bmAttributes}=="e0"
ATTRS{bNumConfigurations}=="1"
ATTRS{maxchild}=="5"
ATTRS{bcdDevice}=="0200"
ATTRS{avoid_reset_quirk}=="0"
ATTRS{quirks}=="0x0"
ATTRS{version}==" 2.00"
ATTRS{urbnum}=="101"
ATTRS{ltm_capable}=="no"
ATTRS{removable}=="unknown"
ATTRS{idProduct}=="9514"
ATTRS{bDeviceClass}=="09"
looking at parent device '/devices/platform/soc/3f980000.usb/usb1':
KERNELS=="usb1"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bDeviceProtocol}=="01"
ATTRS{devpath}=="0"
ATTRS{idVendor}=="1d6b"
ATTRS{speed}=="480"
ATTRS{bNumInterfaces}==" 1"
ATTRS{bConfigurationValue}=="1"
ATTRS{bMaxPacketSize0}=="64"
ATTRS{authorized_default}=="1"
ATTRS{busnum}=="1"
ATTRS{devnum}=="1"
ATTRS{configuration}==""
ATTRS{bMaxPower}=="0mA"
ATTRS{authorized}=="1"
ATTRS{bmAttributes}=="e0"
ATTRS{bNumConfigurations}=="1"
ATTRS{maxchild}=="1"
ATTRS{interface_authorized_default}=="1"
ATTRS{bcdDevice}=="0404"
ATTRS{avoid_reset_quirk}=="0"
ATTRS{quirks}=="0x0"
ATTRS{serial}=="3f980000.usb"
ATTRS{version}==" 2.00"
ATTRS{urbnum}=="25"
ATTRS{ltm_capable}=="no"
ATTRS{manufacturer}=="Linux 4.4.38-v7+ dwc_otg_hcd"
ATTRS{removable}=="unknown"
ATTRS{idProduct}=="0002"
ATTRS{bDeviceClass}=="09"
ATTRS{product}=="DWC OTG Controller"
looking at parent device '/devices/platform/soc/3f980000.usb':
KERNELS=="3f980000.usb"
SUBSYSTEMS=="platform"
DRIVERS=="dwc_otg"
ATTRS{hnp}=="HstNegScs = 0x0"
ATTRS{srp}=="SesReqScs = 0x1"
ATTRS{regvalue}=="invalid offset"
ATTRS{hsic_connect}=="HSIC Connect = 0x1"
ATTRS{guid}=="GUID = 0x2708a000"
ATTRS{mode}=="Mode = 0x1"
ATTRS{srpcapable}=="SRPCapable = 0x1"
ATTRS{regdump}=="Register Dump"
ATTRS{gpvndctl}=="GPVNDCTL = 0x00000000"
ATTRS{ggpio}=="GGPIO = 0x00000000"
ATTRS{hprt0}=="HPRT0 = 0x00001005"
ATTRS{wr_reg_test}=="Time to write GNPTXFSIZ reg 10000000 times: 520 msecs (52 jiffies)"
ATTRS{driver_override}=="(null)"
ATTRS{hcd_frrem}=="HCD Dump Frame Remaining"
ATTRS{mode_ch_tim_en}=="Mode Change Ready Timer Enable = 0x0"
ATTRS{gnptxfsiz}=="GNPTXFSIZ = 0x01000306"
ATTRS{remote_wakeup}=="Remote Wakeup Sig = 0 Enabled = 0 LPM Remote Wakeup = 0"
ATTRS{busconnected}=="Bus Connected = 0x1"
ATTRS{hcddump}=="HCD Dump"
ATTRS{gotgctl}=="GOTGCTL = 0x001c0001"
ATTRS{spramdump}=="SPRAM Dump"
ATTRS{grxfsiz}=="GRXFSIZ = 0x00000306"
ATTRS{gsnpsid}=="GSNPSID = 0x4f54280a"
ATTRS{gusbcfg}=="GUSBCFG = 0x20001700"
ATTRS{hptxfsiz}=="HPTXFSIZ = 0x02000406"
ATTRS{devspeed}=="Device Speed = 0x0"
ATTRS{fr_interval}=="Frame Interval = 0x1d4c"
ATTRS{rem_wakeup_pwrdn}==""
ATTRS{bussuspend}=="Bus Suspend = 0x0"
ATTRS{buspower}=="Bus Power = 0x1"
ATTRS{hnpcapable}=="HNPCapable = 0x1"
ATTRS{rd_reg_test}=="Time to read GNPTXFSIZ reg 10000000 times: 1410 msecs (141 jiffies)"
ATTRS{enumspeed}=="Device Enumeration Speed = 0x1"
ATTRS{inv_sel_hsic}=="Invert Select HSIC = 0x0"
ATTRS{regoffset}=="0xffffffff"
looking at parent device '/devices/platform/soc':
KERNELS=="soc"
SUBSYSTEMS=="platform"
DRIVERS==""
ATTRS{driver_override}=="(null)"
looking at parent device '/devices/platform':
KERNELS=="platform"
SUBSYSTEMS==""
DRIVERS==""
2つ並べると長いので割愛しますがこの中にあるユニークな属性を使って識別ルールを追加してあげます。
今回はesp8266側ATTRS{serial}=="DN02BCJT"
とesp32側ATTRS{serial}=="DN02TAFR"
を使います。
# for esp8266
KERNEL=="ttyUSB*", ATTRS{serial}=="DN02BCJT", SYMLINK+="ttyUSB_esp8266"
# for esp8266
KERNEL=="ttyUSB*", ATTRS{serial}=="DN02TAFR", SYMLINK+="ttyUSB_32"
pi@raspberrypi:~ $ ls -l /dev |grep USB
crw-rw---- 1 root dialout 188, 0 Dec 13 19:50 ttyUSB0
crw-rw---- 1 root dialout 188, 1 Dec 13 19:50 ttyUSB1
lrwxrwxrwx 1 root root 7 Dec 13 19:50 ttyUSB_32 -> ttyUSB1
lrwxrwxrwx 1 root root 7 Dec 13 19:50 ttyUSB_esp8266 -> ttyUSB0
このようにして識別が可能です。
識別するその3
USBを挿した位置による識別
udevadm
した結果をみてみるとKERNELS=="1-1.2"
のような箇所が見つかると思います。実はここの値で4つあるUSBポートそれぞれで物理的にどこにささっているものか識別することができます。RaspberryPi2の場合、対応は画像のようになっているようです。
参考
こちらを参考にさせてもらいました。ありがとうございます。
https://www.irori.org/doc/usb-console.html
https://askubuntu.com/questions/49910/how-to-distinguish-between-identical-usb-to-serial-adapters