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

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.

ダストセンサー2種類届いた

とPM1.0/ PM10/ PM2.5ダストセンサー ZH06とPM2.5ダストセンサーZPH02が届いた。AliのWinsen公式で買って13日。

ZH06はケーブルついていた。ZPH02は付属ケーブル無し。

使い方はこれから調べる

ZH06

データシートはこちら

出力はPWMとUART。コネクタは1.25t-8a、入手しにくいから公式で一緒に売ってくれないかな。データシートのFig9あたりに置き方書いてあるけど通気口ふさいだらよくないし置く確度とかも意識しろと。この間かったDSM501Aにも書いてあったけど通気関係は結構シビアっぽい。

湿度は80%まで。ちょいちょい引っかかりそうね。

想定寿命は10000時間以上。連続稼働だと1年ちょいなので短めは短めね。モジュール本体はケーブル接続なので交換部品だけ買えるようにしておけばよいか。 そして油/煙のある環境はあかんと。

数値の計算式”Please refer the document of ZH06 I-IV Series Communication Protocol.”とか書いているけど、そのドキュメントどこにあるんだ?どりあえず旧バージョンのデータシートにそれっぽいのがのっているのできっとこれでよいだろう。あとでサポートにきかなくては。あいかわらずWinsenのドキュメント類はうち並みの整備状況っぽい。

ZPH02

データシートはこちら(V1.0で公式の製品情報からリンクがある物)こちら(V1.2でGoogle検索で出ててきたもの)が見つかった届いたものは、UARTのレスポンスbyte[6]が1でbyte[7]が0なのでどちらのデータシートとも微妙に違う。サポートに確認しようと思ったけど、ドキュメント大体なのでbyte[7]が0というのが現在の動作なのだろう。(売るならちゃんと聞く) 公式からリンクが張られている方だった。よくみたらVOCセンサーがない場合はbyte6が0x00と補足に書いてあった。

PIN5をグランドにするかどうかでPWMモードかUARTモードかを切り替えるとのこと。

UARTは5Vで1秒ごとの受信のみ。

コネクター以外にピンヘッダー付きそうな端子があるけどデータシートに記載なし。同じものが引き出されていそうなきはするけど、少なくとも並び順はコネクターと違う。後で調べる。

使い方

こちら

PM2.5ダストセンサーDSM501Aが届いた

Aliで買ったダストセンサー(ほこりセンサー)が届いた。(こちら

発注から到着は11日。最後ヤマトさんが持ってきた。(購入時800円中500円送料)

商品のページだと可変抵抗器2こついているけど届いたのは一つしかない。公式にはもうデータシートなさそう。ネットに残っているデータシート見ると触るなと書いてあるし基盤の形も色々なのでフェイク品ではなく色々バージョンがあると信じて触ってみよう。

端子は銀色を左上において左から

#1Control Vout 1 control
#2Vout 2Vout 2 output
#3Vcc Positive power supply(5V)
#4Vout 1 Vout 1 output
#5GND Ground

附属品のコネクターケーブルの色はVCCとかと関係ない。

粒子の数が283mlあたりという微妙な単位で出てくるけど1立方フィートの1/1000ぽい。

リンクのドキュメントには置き方書いていない。ただ、ヒーターで上昇気流を発生させているから云々と書いてあってどこか他のサイトに縦置きして出入口をふさぐなというようなことが書いてあった。

レンズは汚れたら綿棒で拭けと。

配線等はこちらほぼそのまま参考に数値はとれた。あっているのか?

// DSM501A Pin3 → Arduino 5V
// DSM501A Pin5 → Arduino GND
// PM1.0以上を計測したい場合:DSM501A Pin2 → Arduino D8
// PM2.5以上を計測したい場合:DSM501A Pin4 → Arduino D8
// Wind Abaft Co., Ltd.

#include<string.h>
byte buff[2];
int pin = 8;//DSM501A input D8
unsigned long duration;
unsigned long starttime;
unsigned long endtime;
unsigned long sampletime_ms = 30000; // データシートの計測方法が30秒のため
unsigned long lowpulseoccupancy = 0;
float ratio = 0;
float concentration = 0;
 
int i=0;
void setup()
{
  Serial.begin(9600);
  pinMode(8,INPUT);
  starttime = millis(); 
  Serial.println("Start");
}
void loop()
{
  duration = pulseIn(pin, LOW);
  lowpulseoccupancy += duration;
  endtime = millis();
  if ((endtime-starttime) > sampletime_ms)
  {
    ratio = (lowpulseoccupancy-endtime+starttime + sampletime_ms)/(sampletime_ms*10.0);  // Integer percentage 0=>100
    concentration = 1.1*pow(ratio,3)-3.8*pow(ratio,2)+520*ratio+0.62; // データシートのグラフ参照
    Serial.print("lowパルス幅(30秒間):");
    Serial.print(lowpulseoccupancy);
    Serial.print("    Lowパルス幅の占有率[%]:");
    Serial.print(ratio);
    Serial.print("    DSM501A計測粒子数[pcs/283ml]:");
    Serial.println(concentration);
    Serial.print("    DSM501A計測粒子数[pcs/ml]:");// ここだけ追加
    Serial.println(concentration/283);
    lowpulseoccupancy = 0;
    starttime = millis();
  } 
}