日別アーカイブ: 2022年9月24日

Web HID APIを触ってみた

メジャーなCO2センサー(ZGm053U)の値を拾おうとしたらHIDで通信しているということでPC版のChrome・Edge(あとOperaも?)なら使用できるWeb HID APIを使ってみた。なお、新しめの読み込みが公開されているタイプのみ対応。(古いタイプだと値が暗号化されて読み込み機能が公開されているわけではないので自重。読み方はネットに落ちてはいる)

日本だとCO2miniとして売られている大人気のモデル。

読み込み用のページはこちら

人気CO2センサーZGm053Uの値をブラウザ(PC版のChrome/Edge限定)で見れる便利ページ暫定公開しました。

使う人多そうだったら適宜バージョンアップします

使い方はpcにusb繋げて値取得開始ボタン押すだけ。 終了はおもむろに閉じてください。

コード的にはシンプルで、

if (“hid” in navigator) にてWebHIDが使用できるか確認。

const [device] = await navigator.hid.requestDevice({ filters }); にて対象デバイスを取得。CO2センサーのvendorId: 0x04d9,productId: 0xa052は少し上で指定。Windowsだとデバイスマネージャーのヒューマンインターフェースマネージャーにそれっぽいのがいるはず。

await device.open().then(() => { の中で接続時の処理を指定。

中でやっているのはdevice.addEventListener(“inputreport”, handleInputReport);とdevice.sendFeatureReport(0, new Uint8Array([0,0,0,0,0,0,0,0]));

sendFeatureReportはしないとセンサーが動かない。このCO2センサーに必要な送信の起動条件だと思う。

The sendFeatureReport() method of the HIDDevice interface sends a feature report to the HID device. Feature reports are a way for HID devices and applications to exchange non-standardized HID data.

https://developer.mozilla.org/en-US/docs/Web/API/HIDDevice/sendFeatureReport

addEventListenerで登録したfunction内で受信したReport(HIDの通信単位らしい)を適宜処理。ZGm053Uの場合は、0バイト目にデータの種類、received[1] * 256 + received[2]で値。チェックサムとかも入っているけどちゃんと見てはいない。

<script>
    const filters = [
      {
        vendorId: 0x04d9,
        productId: 0xa052
      },
    ];

    function handleInputReport(e) {
      let received = new Uint8Array(e.data.buffer);
      console.log(received);
      if(received[0]==80){ // ID 80 がCO2
        let resultValue =  received[1] * 256 + received[2]; // 値をそのまま信じる(チェックサム等は未確認)
        document.getElementById("co2value").innerText = resultValue;
        document.getElementById("ppm").innerText = "ppm";
      }
    }

    async function checkHID() {
      if ("hid" in navigator) {
        const [device] = await navigator.hid.requestDevice({ filters });
        await device.open().then(() => {
          device.addEventListener("inputreport", handleInputReport);
          device.sendFeatureReport(0, new Uint8Array([0,0,0,0,0,0,0,0])); // 値取得コマンド送信が必要な模様
        })
      } else {
        alert("USB接続を確認してください");
      }
    }
  </script>

データ送信はしていないけどこんな感じらしい。

const enableVibrationData = [1, 0, 1, 64, 64, 0, 1, 64, 64, 0x48, 0x01];
await device.sendReport(0x01, new Uint8Array(enableVibrationData));

// Then, send a command to make the Joy-Con device rumble.
// Actual bytes are available in the sample below.
const rumbleData = [ /* … */ ];
await device.sendReport(0x10, new Uint8Array(rumbleData));

あとクローズも

await device.close();

そのまま切っても大丈夫のものが多いと思うけど行儀は悪いのでちゃんとクローズしたほうが良い。(けど作ったソースではやっていない)

ちなみに、ブラウザでのボタン操作等必須。自動(onloadとか)で接続しようとすると以下のように怒られる。

zyco2.html:52 Uncaught (in promise) DOMException: Failed to execute 'requestDevice' on 'HID': Must be handling a user gesture to show a permission request.