PureBasic SerialPort使用方法
PureBasicでのシリアルポートの使い方についてメモを書いておきます。
Arduinoでループバックアダプタを作る
動作テストのために単純なループバックをするコードを書いておきます。
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) {
digitalWrite(LED_BUILTIN, HIGH);
static uint8_t buf;
buf = Serial.read();
Serial.write(buf);
digitalWrite(LED_BUILTIN, LOW);
}
}
上記内容をArduinoに書き込みすることで、シリアルに送った内容をそのまま返すループバックのアダプタになります。
Windows上でシリアルポートの一覧を出す方法
- How do I get a list of available serial ports in Win32?
- CEnumerateSerial v1.43 A C++ class to enumerate serial ports
- SerialPort list
- Serial Port Search
Mac OS X上でシリアルポートの一覧を出す方法
デバイス名も含めて出したいとなると結構面倒なことがわかりました。
いくつかのアプローチがあるようです。
- ls /dev/
- system_profiler
- ioreg
Mac OS X : ls /dev/ を使用する方法
ターミナルからlsコマンドを使用して調べる方法です。
一般的に使われる手法ですが、デバイス名は判別できません。
あくまで存在するデバイスのポート名のみが確認できます。
% ls /dev/cu.*
/dev/cu.BLTH /dev/cu.usbmodem14301
/dev/cu.Bluetooth-Incoming-Port /dev/cu.usbmodem14401
% ls /dev/tty.*
/dev/tty.BLTH /dev/tty.usbmodem14301
/dev/tty.Bluetooth-Incoming-Port /dev/tty.usbmodem14401
cu.デバイスとtty.デバイスが存在します。
これは、コールアウトとコールインデバイスの違いのようで、ttyだとDCD(Data Carrier Detect)がアサートされないとOpen時にブロックされるとあります。
出力する目的で使用するのであれば、Mac OS X上の場合は、cu.デバイスを使用するのがセオリーなのではないでしょうか。
現に、Mac版のArduino IDEのシリアルポートでリストアップされるデバイスは、cu.デバイスになっています。
情報:
- What's the difference between /dev/tty.* and /dev/cu.* on macOS?
- [Gpsd-dev] Mac OS X & BSD - Should we use /dev/tty.xxx or /dev/cu.xxx for gpsd?
PureBasicのRunProgramから実行する場合は、lsでのワイルドカード指定がglob展開されません。
/cu.*などとワイルドカード指定して一致するものを探す際に、シェル(Bashやzsh)がファイル名を展開しているため、ワイルドカードをRunProgramでlsに渡しても、そのままの意味(ワイルドカード付きのファイル名「cu.*」1つ)を探してしまうことになります。
そのため、lsコマンドではなく、globを内部に実装しているfindコマンドに渡す必要があります。
% find /dev -name 'cu.*' 2>/dev/null
/dev/cu.BLTH
/dev/cu.Bluetooth-Incoming-Port
/dev/cu.usbmodem14301
/dev/cu.usbmodem14401
シェル上でのglob展開を使用しないfindコマンド使用例。
Macの場合「find: /dev/fd/3: Not a directory」というエラーが出るので、標準エラー出力を/dev/nullに送ってます。
Structure SerialDevice
Port.s
EndStructure
Global Dim SerialPortList.SerialDevice(0)
cmdFind = RunProgram("/usr/bin/find", "/dev -name cu.*", "", #PB_Program_Open | #PB_Program_Read)
i.l = 0
If cmdFind
While ProgramRunning(cmdFind)
If AvailableProgramOutput(cmdFind)
ReDim SerialPortList(i)
SerialPortList(i) \ Port = ReadProgramString(cmdFind)
i = i + 1
EndIf
Wend
CloseProgram(cmdFind)
EndIf
For i.l = 0 To ArraySize(SerialPortList())
Debug SerialPortList(i) \ Port
Next
PureBasicで書くとこんな感じでしょうか。
Mac OS X : system_profiler を使用する方法
% /usr/sbin/system_profiler SPUSBDataType
2024-02-22 12:48:45.511 system_profiler[24632:1902355] SPUSBDevice: IOCreatePlugInInterfaceForService failed 0xe00002be
2024-02-22 12:48:45.511 system_profiler[24632:1902355] SPUSBDevice: IOCreatePlugInInterfaceForService failed 0xe00002be
2024-02-22 12:48:45.512 system_profiler[24632:1902355] SPUSBDevice: IOCreatePlugInInterfaceForService failed 0xe00002be
2024-02-22 12:48:45.512 system_profiler[24632:1902355] SPUSBDevice: IOCreatePlugInInterfaceForService failed 0xe00002be
2024-02-22 12:48:45.512 system_profiler[24632:1902355] SPUSBDevice: IOCreatePlugInInterfaceForService failed 0xe00002be
2024-02-22 12:48:45.513 system_profiler[24632:1902355] SPUSBDevice: IOCreatePlugInInterfaceForService failed 0xe00002be
2024-02-22 12:48:45.513 system_profiler[24632:1902355] SPUSBDevice: IOCreatePlugInInterfaceForService failed 0xe00002be
USB:
USB 3.1 Bus:
Host Controller Driver: AppleIntelICLUSBXHCI
PCI Device ID: 0x8a13
PCI Revision ID: 0x0003
PCI Vendor ID: 0x8086
USB 3.1 Bus:
Host Controller Driver: AppleIntelICLUSBXHCI
PCI Device ID: 0x38ed
PCI Revision ID: 0x0010
PCI Vendor ID: 0x8086
Seeed XIAO M0:
Product ID: 0x802f
Vendor ID: 0x2886
Version: 1.04
Serial Number: 7EF958DA5055344A322E3120FF010B15
Speed: Up to 12 Mb/s
Manufacturer: Seeed
Location ID: 0x14400000 / 10
Current Available (mA): 500
Current Required (mA): 100
Extra Operating Current (mA): 0
Arduino MKRZero:
Product ID: 0x804f
Vendor ID: 0x2341
Version: 1.00
Serial Number: 6DD43F3A5050323339202020FF103030
Speed: Up to 12 Mb/s
Manufacturer: Arduino LLC
Location ID: 0x14300000 / 7
Current Available (mA): 500
Current Required (mA): 500
Extra Operating Current (mA): 0
Apple T2 Bus:
Host Controller Driver: AppleUSBVHCIBCE
Touch Bar Backlight:
Product ID: 0x8102
Vendor ID: 0x05ac (Apple Inc.)
Version: 2.01
Serial Number: 0000000000000000
Manufacturer: Apple Inc.
Location ID: 0x80700000
Touch Bar Display:
Product ID: 0x8302
Vendor ID: 0x05ac (Apple Inc.)
Version: 2.01
Serial Number: 0000000000000000
Manufacturer: Apple Inc.
Location ID: 0x80600000
Apple Internal Keyboard / Trackpad:
Product ID: 0x027e
Vendor ID: 0x05ac (Apple Inc.)
Version: 9.33
Serial Number: FM712820080N1R0BR+TNZ
Manufacturer: Apple Inc.
Location ID: 0x80500000
Headset:
Product ID: 0x8103
Vendor ID: 0x05ac (Apple Inc.)
Version: 2.16
Serial Number: 000000000000
Manufacturer: Apple
Location ID: 0x80400000
Ambient Light Sensor:
Product ID: 0x8262
Vendor ID: 0x05ac (Apple Inc.)
Version: 2.01
Serial Number: 000000000000
Manufacturer: Apple Inc.
Location ID: 0x80300000
FaceTime HD Camera (Built-in):
Product ID: 0x8514
Vendor ID: 0x05ac (Apple Inc.)
Version: 2.01
Serial Number: CC212223730J3Y384
Manufacturer: Apple Inc.
Location ID: 0x80200000
Apple T2 Controller:
Product ID: 0x8233
Vendor ID: 0x05ac (Apple Inc.)
Version: 2.01
Serial Number: 0000000000000000
Manufacturer: Apple Inc.
Location ID: 0x80100000
2020年モデルの13インチMacBookPro(4ポートThunderbolt3)上で確認した結果です。
なにやらエラー出力?と思われるものも表示されていますが、USBに接続しているマイコンボード名も取れるようです。
しかし、/dev/cu.*のポート名とのマッピングがいまいち取りづらいですね・・・。
cu.usbmodem****1(Location IDの上部4桁と一致)とマッチングが取れると思われるでしょうが、cu.に続く「usbmodem」の文字列はドライバ依存のため、繋いでいるデバイスによって変わる可能性があります。
となると、cu.*デバイスに対する情報取得の意味では、実質使用できないコマンドと思われます。
ちなみに、man system_profilerで確認してみると、いくつかのオプションが利用可能なようです。
- -xml(XMLで出力)
- -json(JSONで出力)
- -timeout(デフォルトで180秒指定)
情報:
- Any way to get the serial port name from USB info?
- MacでProductIDからシリアルポートを特定する
- serial port names not the same in terminal and in preferences system
Mac OS X : ioreg を使用する方法
これはMac OS Xで使用されているI/O Kitというフレームワークのレジストリを表示するコマンドで、IOに関係する情報を取り出すことができます。
man ioregで確認してみたところ、いくつかのオプションが利用可能なようです。
- -a(XMLで出力)
- -c(オブジェクトのプロパティ)
- -k(キーと一致したオブジェクトを表示、子ノードは機能せず?)
- -l(表示されているすべてのオブジェクトのプロパティを表示)
- -n(名前と一致したオブジェクトを表示、前方一致)
- -r(一致オブジェクトをルートにしたサブツリー表示、-c,-k,-n時有効)
- -x(データと数値を16進数で表示)
シリアルポートを取得する上での基本のコマンドは
ioreg -lr -c IOUSBHostDevice
となります。
これは、-lオプションで、すべてのプロパティを表示し、-rオプションで一致したものだけ表示、-cオプションでIOUSBHostDeviceに限定しています。
この指定を忘れると、テキスト情報で3MB以上もの情報が流れてくるので、実装によっては危険です。
そのため、-c,-rは必ずつけたほうが良く、-lオプションを入れないとポート情報の表示がされませんでしたので、これも必須となります。
% /usr/sbin/ioreg -l -r -c IOUSBHostDevice
+-o Arduino MKRZero@14300000 <class IOUSBHostDevice, id 0x10002b200, registered, matched, active, busy 0 (627 ms), retain 34>
| {
| "kUSBSerialNumberString" = "6DD43F3A5050323339202020FF103030"
| "bDeviceClass" = 239
| "bDeviceSubClass" = 2
| "iSerialNumber" = 3
| "Built-In" = No
| "kUSBFailedRemoteWake" = Yes
| "IOServiceDEXTEntitlements" = (("com.apple.developer.driverkit.transport.usb"))
| "iProduct" = 2
| "USB Serial Number" = "6DD43F3A5050323339202020FF103030"
| "USB Vendor Name" = "Arduino LLC"
| "USBSpeed" = 1
| "IOPowerManagement" = {"PowerOverrideOn"=Yes,"CapabilityFlags"=32768,"MaxPowerState"=2,"DevicePowerState"=2,"DriverPowerState"=0,"ChildrenPowerState"=1,"CurrentPowerState"=2}
| "bNumConfigurations" = 1
| "kUSBProductString" = "Arduino MKRZero"
| "IOServiceLegacyMatchingRegistryID" = 4295143938
| "kUSBVendorString" = "Arduino LLC"
| "USB Product Name" = "Arduino MKRZero"
| "iManufacturer" = 1
| "idVendor" = 9025
| "Device Speed" = 1
| "kUSBCurrentConfiguration" = 1
| "idProduct" = 32847
| "bcdDevice" = 256
| "UsbDeviceSignature" = <41234f8000013644443433463341353035303332333333393230323032304646313033303330ef02010202000a0000>
| "sessionID" = 242600890807977
| "USB Address" = 7
| "non-removable" = "no"
| "IOCFPlugInTypes" = {"9dc7b780-9ec0-11d4-a54f-000a27052861"="IOUSBHostFamily.kext/Contents/PlugIns/IOUSBLib.bundle"}
| "IOClassNameOverride" = "IOUSBDevice"
| "USBPortType" = 0
| "bDeviceProtocol" = 1
| "locationID" = 338690048
| "kUSBAddress" = 7
| "bcdUSB" = 512
| "IOGeneralInterest" = "IOCommand is not serializable"
| "bMaxPacketSize0" = 64
| }
|
+-o AppleUSBHostLegacyClient <class AppleUSBHostLegacyClient, id 0x10002b203, !registered, !matched, active, busy 0, retain 8>
| {
| "IOPowerManagement" = {"DevicePowerState"=0,"CurrentPowerState"=1,"CapabilityFlags"=65536,"MaxPowerState"=2,"DriverPowerState"=1}
| }
|
+-o AppleUSBCDCCompositeDevice <class AppleUSBCDCCompositeDevice, id 0x10002b206, !registered, !matched, active, busy 0, retain 4>
| {
| "IOClass" = "AppleUSBCDCCompositeDevice"
| "CFBundleIdentifier" = "com.apple.driver.usb.cdc"
| "IOProviderClass" = "IOUSBHostDevice"
| "kUSBPreferredConfiguration" = 1
| "IOProbeScore" = 60000
| "IOMatchedAtBoot" = Yes
| "IOMatchCategory" = "IODefaultMatchCategory"
| "IOMatchDefer" = Yes
| "bDeviceSubClass" = 2
| "bDeviceProtocol" = 1
| "CFBundleIdentifierKernel" = "com.apple.driver.usb.cdc"
| "IOPrimaryDriverTerminateOptions" = Yes
| "bDeviceClass" = 239
| }
|
+-o IOUSBHostInterface@0 <class IOUSBHostInterface, id 0x10002b209, registered, matched, active, busy 0 (8 ms), retain 10>
| | {
| | "USBSpeed" = 1
| | "iInterface" = 0
| | "IOServiceLegacyMatchingRegistryID" = 4295143947
| | "bInterfaceProtocol" = 0
| | "bAlternateSetting" = 0
| | "idProduct" = 32847
| | "bcdDevice" = 256
| | "USB Product Name" = "Arduino MKRZero"
| | "locationID" = 338690048
| | "bInterfaceClass" = 2
| | "bInterfaceSubClass" = 2
| | "IOCFPlugInTypes" = {"2d9786c6-9ef3-11d4-ad51-000a27052861"="IOUSBHostFamily.kext/Contents/PlugIns/IOUSBLib.bundle"}
| | "UsbExclusiveOwner" = "AppleUSBACMControl"
| | "USBPortType" = 0
| | "bConfigurationValue" = 1
| | "bInterfaceNumber" = 0
| | "USB Vendor Name" = "Arduino LLC"
| | "IOServiceDEXTEntitlements" = (("com.apple.developer.driverkit.transport.usb"))
| | "idVendor" = 9025
| | "bNumEndpoints" = 1
| | "USB Serial Number" = "6DD43F3A5050323339202020FF103030"
| | "IOGeneralInterest" = "IOCommand is not serializable"
| | "IOClassNameOverride" = "IOUSBInterface"
| | }
| |
| +-o AppleUSBACMControl <class AppleUSBACMControl, id 0x10002b20d, registered, matched, active, busy 0 (1 ms), retain 7>
| {
| "IOProbeScore" = 60000
| "CFBundleIdentifier" = "com.apple.driver.usb.cdc.acm"
| "IOMatchDefer" = Yes
| "IOClass" = "AppleUSBACMControl"
| "IOProviderClass" = "IOUSBHostInterface"
| "bInterfaceClass" = 2
| "CFBundleIdentifierKernel" = "com.apple.driver.usb.cdc.acm"
| "IOMatchCategory" = "IODefaultMatchCategory"
| "bInterfaceSubClass" = 2
| "InterruptPipeReturn" = 0
| "bInterfaceProtocol" = 0
| }
|
+-o IOUSBHostInterface@1 <class IOUSBHostInterface, id 0x10002b20a, registered, matched, active, busy 0 (227 ms), retain 7>
| | {
| | "USBSpeed" = 1
| | "iInterface" = 0
| | "IOServiceLegacyMatchingRegistryID" = 4295143948
| | "bInterfaceProtocol" = 0
| | "bAlternateSetting" = 0
| | "idProduct" = 32847
| | "bcdDevice" = 256
| | "USB Product Name" = "Arduino MKRZero"
| | "locationID" = 338690048
| | "Product Name" = "Arduino MKRZero"
| | "bInterfaceClass" = 10
| | "bInterfaceSubClass" = 0
| | "IOCFPlugInTypes" = {"2d9786c6-9ef3-11d4-ad51-000a27052861"="IOUSBHostFamily.kext/Contents/PlugIns/IOUSBLib.bundle"}
| | "USBPortType" = 0
| | "bConfigurationValue" = 1
| | "bInterfaceNumber" = 1
| | "USB Vendor Name" = "Arduino LLC"
| | "IOServiceDEXTEntitlements" = (("com.apple.developer.driverkit.transport.usb"))
| | "idVendor" = 9025
| | "IODEXTMatchCount" = 1
| | "bNumEndpoints" = 2
| | "USB Serial Number" = "6DD43F3A5050323339202020FF103030"
| | "IOGeneralInterest" = "IOCommand is not serializable"
| | "IOClassNameOverride" = "IOUSBInterface"
| | }
| |
| +-o AppleUSBACMData <class AppleUSBACMData, id 0x10002b210, registered, matched, active, busy 0 (2 ms), retain 7>
| | {
| | "IOClass" = "AppleUSBACMData"
| | "CFBundleIdentifier" = "com.apple.driver.usb.cdc.acm"
| | "IOProviderClass" = "IOUSBHostInterface"
| | "IOTTYBaseName" = "usbmodem"
| | "idProduct" = 32847
| | "IOProbeScore" = 49998
| | "bInterfaceSubClass" = 0
| | "IOMatchCategory" = "IODefaultMatchCategory"
| | "IOMatchDefer" = Yes
| | "idVendor" = 9025
| | "CFBundleIdentifierKernel" = "com.apple.driver.usb.cdc.acm"
| | "IOTTYSuffix" = "14301"
| | "bInterfaceClass" = 10
| | }
| |
| +-o IOSerialBSDClient <class IOSerialBSDClient, id 0x10002b216, registered, matched, active, busy 0 (1 ms), retain 5>
| {
| "IOClass" = "IOSerialBSDClient"
| "CFBundleIdentifier" = "com.apple.iokit.IOSerialFamily"
| "IOProviderClass" = "IOSerialStreamSync"
| "IOTTYBaseName" = "usbmodem"
| "IOSerialBSDClientType" = "IOSerialStream"
| "IOProbeScore" = 1000
| "IOResourceMatch" = "IOBSD"
| "IOMatchedAtBoot" = Yes
| "IOMatchCategory" = "IODefaultMatchCategory"
| "IOTTYDevice" = "usbmodem14301"
| "IOCalloutDevice" = "/dev/cu.usbmodem14301"
| "IODialinDevice" = "/dev/tty.usbmodem14301"
| "IOGeneralInterest" = "IOCommand is not serializable"
| "IOPersonalityPublisher" = "com.apple.iokit.IOSerialFamily"
| "CFBundleIdentifierKernel" = "com.apple.iokit.IOSerialFamily"
| "IOTTYSuffix" = "14301"
| }
|
+-o Google Chrome <class AppleUSBHostDeviceUserClient, id 0x10002b214, !registered, !matched, active, busy 0, retain 7>
{
"IOUserClientCreator" = "pid 13262, Google Chrome"
"IOUserClientDefaultLocking" = Yes
}
+-o Seeed XIAO M0@14400000 <class IOUSBHostDevice, id 0x10002b359, registered, matched, active, busy 0 (612 ms), retain 36>
| {
| "kUSBSerialNumberString" = "7EF958DA5055344A322E3120FF010B15"
| "bDeviceClass" = 0
| "bDeviceSubClass" = 0
| "iSerialNumber" = 3
| "Built-In" = No
| "kUSBFailedRemoteWake" = Yes
| "IOServiceDEXTEntitlements" = (("com.apple.developer.driverkit.transport.usb"))
| "iProduct" = 2
| "USB Serial Number" = "7EF958DA5055344A322E3120FF010B15"
| "USB Vendor Name" = "Seeed"
| "USBSpeed" = 1
| "IOPowerManagement" = {"PowerOverrideOn"=Yes,"CapabilityFlags"=32768,"MaxPowerState"=2,"DevicePowerState"=2,"DriverPowerState"=0,"ChildrenPowerState"=1,"CurrentPowerState"=2}
| "bNumConfigurations" = 1
| "kUSBProductString" = "Seeed XIAO M0"
| "IOServiceLegacyMatchingRegistryID" = 4295144283
| "kUSBVendorString" = "Seeed"
| "USB Product Name" = "Seeed XIAO M0"
| "iManufacturer" = 1
| "idVendor" = 10374
| "Device Speed" = 1
| "kUSBCurrentConfiguration" = 1
| "idProduct" = 32815
| "bcdDevice" = 260
| "UsbDeviceSignature" = <86282f80040137454639353844413530353533343441333232453331323046463031304231350000000202000a0000>
| "sessionID" = 242710715877150
| "USB Address" = 10
| "non-removable" = "no"
| "IOCFPlugInTypes" = {"9dc7b780-9ec0-11d4-a54f-000a27052861"="IOUSBHostFamily.kext/Contents/PlugIns/IOUSBLib.bundle"}
| "IOClassNameOverride" = "IOUSBDevice"
| "USBPortType" = 0
| "bDeviceProtocol" = 0
| "locationID" = 339738624
| "kUSBAddress" = 10
| "bcdUSB" = 512
| "IOGeneralInterest" = "IOCommand is not serializable"
| "bMaxPacketSize0" = 64
| }
|
+-o AppleUSBHostLegacyClient <class AppleUSBHostLegacyClient, id 0x10002b35c, !registered, !matched, active, busy 0, retain 8>
| {
| "IOPowerManagement" = {"DevicePowerState"=0,"CurrentPowerState"=1,"CapabilityFlags"=65536,"MaxPowerState"=2,"DriverPowerState"=1}
| }
|
+-o AppleUSBCDCCompositeDevice <class AppleUSBCDCCompositeDevice, id 0x10002b35f, !registered, !matched, active, busy 0, retain 4>
| {
| "IOClass" = "AppleUSBCDCCompositeDevice"
| "CFBundleIdentifier" = "com.apple.driver.usb.cdc"
| "IOProviderClass" = "IOUSBHostDevice"
| "kUSBPreferredConfiguration" = 1
| "IOProbeScore" = 60000
| "IOMatchedAtBoot" = Yes
| "IOMatchCategory" = "IODefaultMatchCategory"
| "bDeviceSubClass" = 0
| "bDeviceProtocol" = 0
| "CFBundleIdentifierKernel" = "com.apple.driver.usb.cdc"
| "IOPrimaryDriverTerminateOptions" = Yes
| "bDeviceClass" = 0
| }
|
+-o IOUSBHostInterface@0 <class IOUSBHostInterface, id 0x10002b362, registered, matched, active, busy 0 (8 ms), retain 11>
| | {
| | "USBSpeed" = 1
| | "iInterface" = 0
| | "IOServiceLegacyMatchingRegistryID" = 4295144292
| | "bInterfaceProtocol" = 0
| | "bAlternateSetting" = 0
| | "idProduct" = 32815
| | "bcdDevice" = 260
| | "USB Product Name" = "Seeed XIAO M0"
| | "locationID" = 339738624
| | "bInterfaceClass" = 2
| | "bInterfaceSubClass" = 2
| | "IOCFPlugInTypes" = {"2d9786c6-9ef3-11d4-ad51-000a27052861"="IOUSBHostFamily.kext/Contents/PlugIns/IOUSBLib.bundle"}
| | "UsbExclusiveOwner" = "AppleUSBACMControl"
| | "USBPortType" = 0
| | "bConfigurationValue" = 1
| | "bInterfaceNumber" = 0
| | "USB Vendor Name" = "Seeed"
| | "IOServiceDEXTEntitlements" = (("com.apple.developer.driverkit.transport.usb"))
| | "idVendor" = 10374
| | "bNumEndpoints" = 1
| | "USB Serial Number" = "7EF958DA5055344A322E3120FF010B15"
| | "IOGeneralInterest" = "IOCommand is not serializable"
| | "IOClassNameOverride" = "IOUSBInterface"
| | }
| |
| +-o AppleUSBACMControl <class AppleUSBACMControl, id 0x10002b366, registered, matched, active, busy 0 (1 ms), retain 7>
| {
| "IOProbeScore" = 60000
| "CFBundleIdentifier" = "com.apple.driver.usb.cdc.acm"
| "IOMatchDefer" = Yes
| "IOClass" = "AppleUSBACMControl"
| "IOProviderClass" = "IOUSBHostInterface"
| "bInterfaceClass" = 2
| "CFBundleIdentifierKernel" = "com.apple.driver.usb.cdc.acm"
| "IOMatchCategory" = "IODefaultMatchCategory"
| "bInterfaceSubClass" = 2
| "InterruptPipeReturn" = 0
| "bInterfaceProtocol" = 0
| }
|
+-o IOUSBHostInterface@1 <class IOUSBHostInterface, id 0x10002b363, registered, matched, active, busy 0 (227 ms), retain 9>
| | {
| | "USBSpeed" = 1
| | "iInterface" = 0
| | "IOServiceLegacyMatchingRegistryID" = 4295144293
| | "bInterfaceProtocol" = 0
| | "bAlternateSetting" = 0
| | "idProduct" = 32815
| | "bcdDevice" = 260
| | "USB Product Name" = "Seeed XIAO M0"
| | "locationID" = 339738624
| | "Product Name" = "Seeed XIAO M0"
| | "bInterfaceClass" = 10
| | "bInterfaceSubClass" = 0
| | "IOCFPlugInTypes" = {"2d9786c6-9ef3-11d4-ad51-000a27052861"="IOUSBHostFamily.kext/Contents/PlugIns/IOUSBLib.bundle"}
| | "USBPortType" = 0
| | "bConfigurationValue" = 1
| | "bInterfaceNumber" = 1
| | "USB Vendor Name" = "Seeed"
| | "IOServiceDEXTEntitlements" = (("com.apple.developer.driverkit.transport.usb"))
| | "idVendor" = 10374
| | "IODEXTMatchCount" = 1
| | "bNumEndpoints" = 2
| | "USB Serial Number" = "7EF958DA5055344A322E3120FF010B15"
| | "IOGeneralInterest" = "IOCommand is not serializable"
| | "IOClassNameOverride" = "IOUSBInterface"
| | }
| |
| +-o AppleUSBACMData <class AppleUSBACMData, id 0x10002b369, registered, matched, active, busy 0 (2 ms), retain 7>
| | {
| | "IOClass" = "AppleUSBACMData"
| | "CFBundleIdentifier" = "com.apple.driver.usb.cdc.acm"
| | "IOProviderClass" = "IOUSBHostInterface"
| | "IOTTYBaseName" = "usbmodem"
| | "idProduct" = 32815
| | "IOProbeScore" = 49998
| | "bInterfaceSubClass" = 0
| | "IOMatchCategory" = "IODefaultMatchCategory"
| | "IOMatchDefer" = Yes
| | "idVendor" = 10374
| | "CFBundleIdentifierKernel" = "com.apple.driver.usb.cdc.acm"
| | "IOTTYSuffix" = "14401"
| | "bInterfaceClass" = 10
| | }
| |
| +-o IOSerialBSDClient <class IOSerialBSDClient, id 0x10002b36f, registered, matched, active, busy 0 (1 ms), retain 5>
| {
| "IOClass" = "IOSerialBSDClient"
| "CFBundleIdentifier" = "com.apple.iokit.IOSerialFamily"
| "IOProviderClass" = "IOSerialStreamSync"
| "IOTTYBaseName" = "usbmodem"
| "IOSerialBSDClientType" = "IOSerialStream"
| "IOProbeScore" = 1000
| "IOResourceMatch" = "IOBSD"
| "IOMatchedAtBoot" = Yes
| "IOMatchCategory" = "IODefaultMatchCategory"
| "IOTTYDevice" = "usbmodem14401"
| "IOCalloutDevice" = "/dev/cu.usbmodem14401"
| "IODialinDevice" = "/dev/tty.usbmodem14401"
| "IOGeneralInterest" = "IOCommand is not serializable"
| "IOPersonalityPublisher" = "com.apple.iokit.IOSerialFamily"
| "CFBundleIdentifierKernel" = "com.apple.iokit.IOSerialFamily"
| "IOTTYSuffix" = "14401"
| }
|
+-o Google Chrome <class AppleUSBHostDeviceUserClient, id 0x10002b36d, !registered, !matched, active, busy 0, retain 7>
{
"IOUserClientCreator" = "pid 13262, Google Chrome"
"IOUserClientDefaultLocking" = Yes
}
2020年モデルの13インチMacBookPro(4ポートThunderbolt3)上で確認した結果です。
実行結果の一部を切り取っています。
-cオプションを有効にしていますが、これでもIOUSBHostDeviceの結果は、3000行近く、900KBほどのテキスト情報です。
IOCalloutDeviceキーまたは、IODialinDeviceキーに、/dev/〜のポート名が格納されていることがわかります。
これらのキーを探して、上位ツリーからプロダクト名等を取得すると、ボード名表示までできそうです。
ちなみに、locationIDが一致しない?と思われるでしょうが、デフォルトで10進数表示になっているからです。
-xオプションの指定で16進数表示になるため、基本的には-c, -l, -r, -xオプションは有効にして使用するものになると思われますが、XML出力では16進数で出力はされないようです。
情報:
- Is there a way to access a USB-serial port by the device id, not by the tty.* port?
- Mapping between /dev/tty.usbmodemXXXX and Location ID
Mac OS X : ioreg の出力を xmllint でフィルタする方法
Mac OS Xに入っているコマンドラインツールでXMLをパースできないか調べると、grep, sed, awkで頑張っている例や、perlに渡してしまって文字列処理するような例が見つかります。
ioregの出力は-aオプションでXML出力が可能で、XML構造になっているため、きちんと情報を取り出すにはXMLをパースするのが一番都合が良いです。
ioreg -alrx -c IOUSBHostDevice
をパースする方法を考えてみましょう。
Mac OS Xにはxmllintが入っているため、XPathで指定してみることにします。
% /usr/sbin/ioreg -alrx -c IOUSBHostDevice | /usr/bin/xmllint --xpath "//dict[key[text()='IOCalloutDevice']]" -
<dict>
<key>CFBundleIdentifier</key>
<string>com.apple.iokit.IOSerialFamily</string>
<key>CFBundleIdentifierKernel</key>
<string>com.apple.iokit.IOSerialFamily</string>
<key>IOCalloutDevice</key>
<string>/dev/cu.usbmodem14301</string>
<key>IOClass</key>
<string>IOSerialBSDClient</string>
<key>IODialinDevice</key>
<string>/dev/tty.usbmodem14301</string>
<key>IOGeneralInterest</key>
<string>IOCommand is not serializable</string>
<key>IOMatchCategory</key>
<string>IODefaultMatchCategory</string>
<key>IOMatchedAtBoot</key>
<true/>
<key>IOObjectClass</key>
<string>IOSerialBSDClient</string>
<key>IOObjectRetainCount</key>
<integer>5</integer>
<key>IOPersonalityPublisher</key>
<string>com.apple.iokit.IOSerialFamily</string>
<key>IOProbeScore</key>
<integer>1000</integer>
<key>IOProviderClass</key>
<string>IOSerialStreamSync</string>
<key>IORegistryEntryID</key>
<integer>4295143958</integer>
<key>IORegistryEntryName</key>
<string>IOSerialBSDClient</string>
<key>IOResourceMatch</key>
<string>IOBSD</string>
<key>IOSerialBSDClientType</key>
<string>IOSerialStream</string>
<key>IOServiceBusyState</key>
<integer>0</integer>
<key>IOServiceBusyTime</key>
<integer>1005816</integer>
<key>IOServiceState</key>
<integer>30</integer>
<key>IOTTYBaseName</key>
<string>usbmodem</string>
<key>IOTTYDevice</key>
<string>usbmodem14301</string>
<key>IOTTYSuffix</key>
<string>14301</string>
</dict>
<dict>
<key>CFBundleIdentifier</key>
<string>com.apple.iokit.IOSerialFamily</string>
<key>CFBundleIdentifierKernel</key>
<string>com.apple.iokit.IOSerialFamily</string>
<key>IOCalloutDevice</key>
<string>/dev/cu.usbmodem14401</string>
<key>IOClass</key>
<string>IOSerialBSDClient</string>
<key>IODialinDevice</key>
<string>/dev/tty.usbmodem14401</string>
<key>IOGeneralInterest</key>
<string>IOCommand is not serializable</string>
<key>IOMatchCategory</key>
<string>IODefaultMatchCategory</string>
<key>IOMatchedAtBoot</key>
<true/>
<key>IOObjectClass</key>
<string>IOSerialBSDClient</string>
<key>IOObjectRetainCount</key>
<integer>5</integer>
<key>IOPersonalityPublisher</key>
<string>com.apple.iokit.IOSerialFamily</string>
<key>IOProbeScore</key>
<integer>1000</integer>
<key>IOProviderClass</key>
<string>IOSerialStreamSync</string>
<key>IORegistryEntryID</key>
<integer>4295144303</integer>
<key>IORegistryEntryName</key>
<string>IOSerialBSDClient</string>
<key>IOResourceMatch</key>
<string>IOBSD</string>
<key>IOSerialBSDClientType</key>
<string>IOSerialStream</string>
<key>IOServiceBusyState</key>
<integer>0</integer>
<key>IOServiceBusyTime</key>
<integer>1479657</integer>
<key>IOServiceState</key>
<integer>30</integer>
<key>IOTTYBaseName</key>
<string>usbmodem</string>
<key>IOTTYDevice</key>
<string>usbmodem14401</string>
<key>IOTTYSuffix</key>
<string>14401</string>
</dict>
情報取得できますね。
では、シリアルポート名からプロダクト名を引いてみましょう。
% /usr/sbin/ioreg -alrx -c IOUSBHostDevice | /usr/bin/xmllint --xpath "//dict[string[text()='/dev/cu.usbmodem14301']]/../../../../key[text()='Product Name']/following::string[position()=1]/text()" -
Arduino MKRZero
% /usr/sbin/ioreg -alrx -c IOUSBHostDevice | /usr/bin/xmllint --xpath "//dict[string[text()='/dev/cu.usbmodem14401']]/../../../../key[text()='Product Name']/following::string[position()=1]/text()" -
Seeed XIAO M0
% /usr/sbin/ioreg -alrx -c IOUSBHostDevice | /usr/bin/xmllint --xpath "//dict[string[text()='/dev/cu.NO_DEVICE']]/../../../../key[text()='Product Name']/following::string[position()=1]/text()" -
XPath set is empty
簡単ですね。
「/dev/cu.usbmodem14301」と一致する<dict>内の<string>を検索し、そのノードの4階層上にある<key>Product Name</key>を探し、なおかつその次の要素となる<string>からテキストを抜き出しています。
一致がない場合は「XPath set is empty」という応答(標準エラー出力)になります。
PureBasicで実装する例です。
Structure SerialDevice
Port.s
hasName.b
Name.s
EndStructure
Global Dim SerialPortList.SerialDevice(0)
cmdFind = RunProgram("/usr/bin/find", "/dev -name cu.*", "", #PB_Program_Open | #PB_Program_Read)
i.l = 0
If cmdFind
While ProgramRunning(cmdFind)
If AvailableProgramOutput(cmdFind)
ReDim SerialPortList(i)
SerialPortList(i) \ Port = ReadProgramString(cmdFind)
i = i + 1
EndIf
Wend
CloseProgram(cmdFind)
EndIf
For i.l = 0 To ArraySize(SerialPortList())
cmdXPath.s = "//dict[string[text()='\''" + SerialPortList(i) \ Port + "'\'']]/../../../../key[text()='\''Product Name'\'']/following::string[position()=1]/text()"
cmdPortName.s = "/usr/sbin/ioreg -alrx -c IOUSBHostDevice | /usr/bin/xmllint --xpath '" + cmdXPath + "' -"
cmdIoreg = RunProgram("/bin/sh", "-c " + Chr(34) + cmdPortName + Chr(34), "", #PB_Program_Open | #PB_Program_Read)
If cmdIoreg
While ProgramRunning(cmdIoreg)
If AvailableProgramOutput(cmdIoreg)
SerialPortList(i) \ hasName = 1
SerialPortList(i) \ Name = ReadProgramString(cmdIoreg)
EndIf
Wend
CloseProgram(cmdIoreg)
EndIf
Next
For i.l = 0 To ArraySize(SerialPortList())
If SerialPortList(i) \ hasName
Debug SerialPortList(i) \ Port + " @ " + SerialPortList(i) \ Name
Else
Debug SerialPortList(i) \ Port
EndIf
Next
RunProgramの引数で#PB_Program_Connectを指定するとパイプをつなげることができるようなのですが、いまいち動作させることができませんでした。
RunProgramをioregとxmllintで2行書くのですが、間にDelay()を入れると動くなど、タイミングの問題か書き方の問題なのか、まだ調査できていません。
とりあえずは、/bin/shにすべて渡してしまうことにして、RunProgram1行で書く例です。
シリアルポートをリストアップした後、それぞれのシリアルポートに対してコマンドを実行しているため、ポート数が多くなると遅くなると思われます。
ioregの内容をバッファしたほうが良いかと思いますが、コマンドの実行結果の行数が増えると極端に重くなってしまったため、このような形での実装となりました。
他に良い実装の方法があればご連絡ください!
Linux上でシリアルポートの一覧を出す方法