Open3

WebUSB APIを使ってANT+デバイスと通信するライブラリを作ってみた話

8beeeaaat8beeeaaat

ANT+をJavaScriptで扱うための有名所ライブラリとしては Loghorn/ant-plus が有名。(実はcommitterだったりする)

Loghorn/ant-plus はNodeJS + libusbが必要で、Electronなどデスクトップアプリにしか使えないのがネック。
シンプルにWebアプリからANT+デバイスに接続する方法が無かった。じゃあ作るしかない。

NodeJS + libusbではなく、WebUSB APIを使うアプローチを試みてみた。
完成したものがコチラになります。

https://www.npmjs.com/package/web-ant-plus

https://www.youtube.com/watch?v=3XKP9zcMnw8

8beeeaaat8beeeaaat

立ちはだかった壁1: パケットの取り扱い

Buffer は DataView に置き換えような

デバイスから受け取ったパケットを Longhorn (libusb) 版では Bufferとして扱っている。
WebではNodeJSのClassであるBufferを扱えないので、DataView に込めて扱うことにした。

Before

https://github.com/Loghorn/ant-plus/blob/0bdb441ae02952fe76a0fe8bd28e20b8d4d20646/src/ant.ts#L383-L399

After

https://github.com/8beeeaaat/web-ant-plus/blob/e0f0fa0332906008c47a677af2d93c14cf8ccd8c/src/USBDriver.ts#L95-L113

躓きポイントとしてリトルエンディアンの取り扱いを挙げておく。
DataViewと向き合う機会が普段ないので大変勉強になった。

  • Buffer はリトルエンディアンを専用のメソッドで取り扱う
    例: Buffer.readUInt16LE(n)

https://github.com/Loghorn/ant-plus/blob/0bdb441ae02952fe76a0fe8bd28e20b8d4d20646/src/ant.ts#L844-L845

  • 対して DataView ではオプションでリトルエンディアンの取り扱いを宣言する
    例: DataView.getUint16(n, boolean)

https://github.com/8beeeaaat/web-ant-plus/blob/7916f5cebdee2f3080e1437b62ca0837181e75ed/src/sensors/AntPlusSensor.ts#L41-L42

8beeeaaat8beeeaaat

立ちはだかった壁2: WebUSBにはread pollingが無い!!

欲しいものは自分で取りに行こうね

libusb版のEndpointでは対象デバイスからパケットを継続して受け取れる様にEventEmitterを継承している。

https://github.com/Loghorn/ant-plus/blob/0bdb441ae02952fe76a0fe8bd28e20b8d4d20646/src/ant.ts#L312

node-usb に startPollというpolling用の関数が用意されてるんですね。
https://node-usb.github.io/node-usb/classes/InEndpoint.html#startPoll

ところがWebUSBではEndpointはEventEmitterを継承しておらず、自前でloopさせて device.transferIn でパケットを取りに行く必要がある。
https://wicg.github.io/webusb/#device-usage

くー。

Before

https://github.com/Loghorn/ant-plus/blob/0bdb441ae02952fe76a0fe8bd28e20b8d4d20646/src/ant.ts#L369-L410

After

https://github.com/8beeeaaat/web-ant-plus/blob/e0f0fa0332906008c47a677af2d93c14cf8ccd8c/src/USBDriver.ts#L64-L121