読者です 読者をやめる 読者になる 読者になる

CO2モニタリングのためにセンサーをHackした話

昨年末にこの記事を読んで以来、昼過ぎに眠くなるのは CO2 濃度のせいでは無いかと思い始め、CO2センサーへの興味が湧いて仕方がなっかのだが、CO2センサーはいかんせん高い。そう悩んでいたところ、格安のセンサーを見つけたのでUSBで値を取れるようにしてみた。

f:id:r_kurain:20160126163549j:plain

まとめ

  • CO2mini というセンサーは何処にも書いてないが、USBデバイスとして認識できる
  • gem を作ったので bundle install すれば Mac でも Linuxでも値がとれる。
  • 僕のオフィスのCO2濃度は別に高く無さそう。

センサー選び

CO2 濃度を計れるセンサーは、専門家向けのものが多く価格帯が高い。 PCから値を取ろうと思うと、手頃のなのは

たぶんこの辺で、HTTPで叩けるAPIもあるので悪くない。 気温や湿度もとれる。お金に困らないひとはとりあえずこれで良さそう。

だが 2万4000円もするし、最近売り切れてたりするので、本稿では www.monotaro.com

カスタム CO2モニター CO2-MINI

カスタム CO2モニター CO2-MINI

これを使う。モノタロウから買うと税抜き8990円 amazon などでは 13000円 くらいで手に入る。

何処にもUSBで値がとれるとは書いてないので不安になるが、 http://www.co2meter.com/collections/fixed-wall-mount/products/co2mini-co2-indoor-air-quality-monitor これと同じなので実はUSBで値がとれる。 ただし、いつUSB出力の機能がオミットされるかはわからないので自己責任で。あるいは販売店に問い合わせてほしい。

値のとりかた。

ruby スクリプトを作ったのでこれを利用してもらえればと思う。 github.com

$ brew install hidapi 
  # or apt-get install libhidapi-hidraw0 など Linux では環境に応じてHIDAPIライブラリをインストールしてほしい
$ git clone https://github.com/kurain/co2mini.git
$ cd co2mini/examples
$ bundle install
$ bundle exec ruby -I./lib examples/co2show.rb

これだけだと、コンソールに値が見えるだけなので、僕の場合はMackerel に流して可視化 and 監視してる。 co2post.rb を参照してほしい。

※gem 化の準備はしてるのに、git を勧めるのは依存してるパッケージにPRを送ってるけれど採用されてないから!

参考資料兼余談

本稿で使ったもの以外のセンサーについて。

USB で繋がるものや、センサー単体で販売しているものが

http://www.co2meter.com/collections/co2-sensors

このサイトに沢山ある。日本でこの価格帯でかつ、オンライン販売で売ってるセンサーはほとんどない。 このサイトで買うとすると、安いセンサーで$80、輸送費が$35くらいなので、14000円くらい。 キャリブレーションどうするとかいう不安もある。

USBプロトコルについて

co2mini から値をとるソフトウェアは co2meter.com から取得できる。が windows 版しかない。 ありがたい事にプロトコルを解析してる人がいて

https://hackaday.io/project/5301-reverse-engineering-a-low-cost-usb-co-monitor

にまとめてくれている。逆アセンブルして調べたようで、上記のサイトの記事は相当おもしろい。おすすめである。 コードも github にまとまっている

https://github.com/henryk/fhem-co2mini

さらにサイトのコメントを見てると、各言語にポートしてる人も居て

node.js

https://github.com/maddindeiss/co2monitor

C言語

https://github.com/dmage/co2mon

の実装があるので、ruby 版を書くのはわりと簡単な話だった。

HIDAPI

co2mini はHIDデバイスとして認識される。HIDはUSBデバイスのなかの一つのクラスで、汎用的なのでよく使われているようだ。 ただ、libusb というメジャーな USB ライブラリからは扱いが難しく、libusb のサイトでも HIDデバイスを扱うときは

HIDAPI

を使えと書いてある。HIDAPIは各OS(Linux, Mac, Win)でのHIDの扱いを抽象化していて、これを使えばOSをまたいで動く コードが書ける。今回 Mac で実験して、実運用は Raspberry Pi というのを考えていたので、こちらを採用した。 先に紹介した他の人の実装は、オリジナルのperlのやつとnode.jsの実装は Linux 環境のみを想定している、C言語実装のものは、HIDAPIを利用している。

ちなみに Linux には /dev/hidraw にHIDがマウントされるという、便利機能があって HIDAPI はこれを利用する。(libusbをつかう事もできる) Macの場合はIOKITに含まれるIOHIDから制御可能で、HIDAPIも利用してるっぽい。MacのUSBまわりはこちらが詳しい。

ruby_hid_api

HIDAPIのrubyバインディングruby_hid_apiruby_hidapi の2つがある。前者はFFIを使っていて、後者は拡張ライブラリ。 FFIを使ってるほうが、OSが違った時に動く可能性が高いので、私は ruby_hid_api を利用している。 HIDAPIの send_featurer_request 関数に対応する部分が未実装だったので、実装してPR出しているけれど 音沙汰がないのが残念。

プロトコルの実際

プロトコルの詳細はコード見てくれという感じなんだけれど、面白かったので少しだけ解説しておく。

co2mini では 'Feature Report' でデバイスを初期化する。初期化前には 'Input Report' (HIDAPIにおけるread関数)では値がとれない。 初期化時に8バイトの値(マジックテーブル)を渡すのだけれど、この後 Input Report で返ってくる値は、マジックテーブルの値と XORが取られていたり、ビットシフト が行われたりしていて、簡単な暗号化がされている。 co2mini.rb の _decrypt が復号化メソッドなので、詳細はそちらを参考にしてほしい。 他のUSBデバイスを知らないので、一般的な動作なのかわからないのだが、不思議な挙動である。

 def _decrypt(key, data)
    offset  = [0x48,  0x74,  0x65,  0x6D,  0x70,  0x39,  0x39,  0x65]  #"Htemp99e"
    shuffle = [2, 4, 0, 7, 1, 6, 5, 3];

    phase1 = shuffle.map{|i| data[i] }
    phase2 = (0..7).map{|i| phase1[i] ^ key[i] }
    phase3 = (0..7).map{|i| ( (phase2[i] >> 3) | (phase2[ (i-1+8)%8 ] << 5) ) & 0xff }
    ctmp   = (0..7).map{|i| ( (offset[i] >> 4) | offset[i] << 4 )  & 0xff }
    result = (0..7).map{|i| (0x100 + phase3[i] - ctmp[i]) & 0xff }
    return result;
  end

おわりに

f:id:r_kurain:20160126164444p:plain というわけで、co2 濃度の可視化と記録を始めてみた。近代的オフィスなので、空調がとまると濃度が酷いことになる。 普通の時間帯は、空調とまらないのでほとんど問題ない。