RaspberryPi[1] USBポートでデバイスを固定
要旨
Raspberry Pi には4つのUSBポートが付いています。これに複数のUSBが付いたときに、どこに何を刺すかによってポート名を固定したほうがプログラムが書きやすいので調べてみました。
Raspberry Pi & Python 開発ブログ ☆彡を参考にさせていただきました。詳細はこちらの方がいいと思います。
Rasperry Pi 4の場合は以下のように割り付けてあります。ちなみに確認はUSBにさして、順番にポートを変えていくとわかります。その時のコマンドは、
udevadm info -a -n /dev/ttyACM0 | grep KERNELSを実行すると、デバイス名は指したデバイス名です。
KERNELS=="1-1.3"
KERNELS=="1-1.4"とか出てきます。
cd /etc/udev/rules.dの下にデフォルトで 99-com.rules というファイルが入っています。これが、ポート名を作るルールになっているようなので、ここに新規にルールを書いていけばよさそうです。しかし、このオリジナルファイルを修正するのではなく、違うファイル名で作ることも可能になっているようなので、新規に作ります。参考のページでは 90-usb.rules としているので同じ名称にしておきます。
ルールの書き方
# /etc/udev/rules.d/90-usb-rtk.rules
SUBSYSTEM=="tty", KERNELS=="1-1.3", SYMLINK+="USB_RTK"
SUBSYSTEM=="tty", KERNELS=="1-1.4", SYMLINK+="USB_CLAS"反映
sudo udevadm control --reload-rules
sudo udevadm triggerUSBの抜き差しまたは再起動で
/dev/USB_RTK
/dev/USB_CLASと出てくれば成功
この何がメリットかというと、ポートに名前が付けられるので、アプリでその名称でオープンするときに、そのポートに刺さっているものはその名称であると固定できるということです。間違ったものを指すと、それは異なるデバイスをオープンすることになります。そのポートに何も刺さっていないのならば、オープンでエラーがでます。どこにさしてもそのデバイスを正しく認識してほしいときは、下記の「デバイスでポートを指定する」を用いると便利です。ただこれもデバイスが固定されている場合ですので、異なるデバイスが接続させる可能性がある場合は、下記の方法は使えません。
デバイスでポートを指定する。
USBデバイスを複数さすときに、USB0になったりUSB1になったりする場合、順番に番号が振られるけど、両方を刺したまま再起動すると、どっちにUSB0が振られるかはどうも時の運のようです。しかし、プログラムでデバイスを指定するためには変わっては欲しくない。こんなことがよくありますが、デバイス名にシンボリックリンクを張ることができるようです。その識別子としてプロダクトIDとかモデルIDとかを使えるようです。USB一つにデバイスを刺して下記コマンドを打つと
udevadm info -q all -n /dev/ttyUSB0
P: /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.1/1-1.1:1.0/ttyUSB0/tty/ttyUSB0
N: ttyUSB0
L: 0
S: serial/by-id/usb-Silicon_Labs_CP2102N_USB_to_UART_Bridge_Controller_1a949dbce7e6ea11a474f17a994a5d01-if00-port0
S: serial/by-path/platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.1:1.0-port0
S: wind
E: DEVPATH=/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.1/1-1.1:1.0/ttyUSB0/tty/ttyUSB0
E: DEVNAME=/dev/ttyUSB0
E: MAJOR=188
E: MINOR=0
E: SUBSYSTEM=tty
E: USEC_INITIALIZED=195369592
E: ID_BUS=usb
E: ID_VENDOR_ID=10c4
E: ID_MODEL_ID=ea60
E: ID_PCI_CLASS_FROM_DATABASE=Serial bus controller
E: ID_PCI_SUBCLASS_FROM_DATABASE=USB controller
E: ID_PCI_INTERFACE_FROM_DATABASE=XHCI
E: ID_VENDOR_FROM_DATABASE=VIA Technologies, Inc.
E: ID_MODEL_FROM_DATABASE=VL805 USB 3.0 Host Controller
E: ID_PATH=platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.1:1.0
E: ID_PATH_TAG=platform-fd500000_pcie-pci-0000_01_00_0-usb-0_1_1_1_0
E: ID_VENDOR=Silicon_Labs
E: ID_VENDOR_ENC=Silicon\x20Labs
E: ID_MODEL=CP2102N_USB_to_UART_Bridge_Controller
E: ID_MODEL_ENC=CP2102N\x20USB\x20to\x20UART\x20Bridge\x20Controller
E: ID_REVISION=0100
E: ID_SERIAL=Silicon_Labs_CP2102N_USB_to_UART_Bridge_Controller_1a949dbce7e6ea11a474f17a994a5d01
E: ID_SERIAL_SHORT=1a949dbce7e6ea11a474f17a994a5d01
E: ID_TYPE=generic
E: ID_USB_INTERFACES=:ff0000:
E: ID_USB_INTERFACE_NUM=00
E: ID_USB_DRIVER=cp210x
E: ID_MM_CANDIDATE=1
E: DEVLINKS=/dev/serial/by-id/usb-Silicon_Labs_CP2102N_USB_to_UART_Bridge_Controller_1a949dbce7e6ea11a474f17a994a5d01-if00-port0 /dev/serial/by-path/platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.1:1.0-port0 /dev/wind
E: TAGS=:systemd:
E: CURRENT_TAGS=:systemd:
と表示されます。ほとんど場合は、ID_VENDOR で区別できると思うので、これをキーワードとします。もし、同じベンダーで識別したいというなら異なるものをキーワードにすればできると思います。
上記に書いた99-com.rulesの中身を以下ににします。
KERNEL=="ttyUSB*",ATTRS{idVendor}=="067b",SYMLINK+="via_gps",MODE="0666"
KERNEL=="ttyUSB*",ATTRS{idVendor}=="10c4",SYMLINK+="wind",MODE="0666"これで再起動をかけて /dev/を見ると以下のように見えます。したがってプログラムでは、/dev/via_gps としてopen してあげれば、その実態がttyUSB0だろうがttyUSB1だろうが関係なくなります。ちなみに、上記の、idVendor をID_VENDOR_IDとすると機能しません。
$ ls -al /dev/ | grep USB
crw-rw-rw- 1 root dialout 188, 0 1月 30 09:46 ttyUSB0
crw-rw-rw- 1 root dialout 188, 1 1月 30 09:51 ttyUSB1
lrwxrwxrwx 1 root root 7 1月 30 09:46 via_gps -> ttyUSB0
lrwxrwxrwx 1 root root 7 1月 30 09:51 wind -> ttyUSB1
とはいえ、同じシリアルーUSB変換を使ったりすると、VENDER IDもMODEL IDも同じになります。そんな時はID_USB_SERIAL_SHORTを使います。ID_USB_SERIAL_SHORT は udev のデバイスプロパティの一つで、USBデバイスのシリアル番号(短縮版)に対応します。udev ルールでこれを使用するには、ATTRS ではなく、ENV{} を使って参照します。
KERNEL=="ttyUSB*", ENV{ID_USB_SERIAL_SHORT}=="AXBCj137C01", SYMLINK+="ttyTMIU", MODE="0666"
KERNEL=="ttyUSB*", ENV{ID_USB_SERIAL_SHORT}=="b23048f22ce7ea11baa0f17a994a5d01", SYMLINK+="ttyWIND1", MODE="0666"
KERNEL=="ttyUSB*", ENV{ID_USB_SERIAL_SHORT}=="1a949dbce7e6ea11a474f17a994a5d01", SYMLINK+="ttyWIND2", MODE="0666"
その後、
sudo udevadm control --reload
sudo udevadm triggerとして、
udevadm info --query=all --name=/dev/ttyTMIU
ls -al /dev/ttyTMIUで確認します。


