MH-Z19 という格安 CO2 センサを読んでみた | tech - 氾濫原 を書いたあと、追加で3つ買って、4つ手元にある状態ですべて繋げて値を読んでみると、でてくる値がとんでもないのが2つあった。窓全開で十分に換気されている状態でも 800〜900ppm になってしまう。

センサの種類 (2000ppm or 5000ppm) が違うのか? と思ったけど、UART の値も PWM の計算値も同じ (UART では ppm が直接出力されるので、計算値の答えあわせになる) なので関係なさそう。

その後よくよく比較してみると、どれも全く動きかたがちがう。傾き(感度)も違う。やっぱこれはキャリブレーションをなんとかしてやるしかない気がする。うーん。

ゼロ点とスパン点どっちも難しい。なんか簡単にできる方法がないか考え中

  1. トップ
  2. tech
  3. MH-Z19 の個体差とキャリブレーションの必要性

OWON デジタルオシロスコープSDS7102 超薄型 1Gsサンプリング100MHzFFT機能付カラーポータブルフルセット【国内正規品】 - OWON

OWON

3.0 / 5.0

OWON SDS7102 大変つかってるが安いだけあって品質が微妙な点がいくつかある。特にロータリーエンコーダーがひどくて、ちゃんとレンジ切り替えができないことが多くあってイライラする。同様の現象が起きてないか検索したところいくつかヒットしたのでやってみた。

おおむね 500 Can't connect to www.candrian.gr:443 (certificate verify failed) を参考にすればいいのだが、ちゃんと外れないところがあった。

分解

裏蓋

  • 周囲のビス4本
  • アースのビス
  • 下側の接続ポートのビス

をはずす。裏蓋はハメこみなので、カードとかで隙間をひろげながらはずす。

電源スイッチがひっかかるので、これは押しながら抜く。

開きつつあると、裏蓋に繋がる電源線が3つあるので、基板側のコネクタとビスをはずす。

ロータリーエンコーダの基板まで

  • 写真のうち7つのビス (赤丸)
  • コネクタ1つ (紫丸)

をはずす。これでモニタも含めてすっぽり外れる。フラットケーブルとかで繋がってるので慎重にやる。

キャパシタの追加

赤丸部分がロータリーエンコーダなので、ここに 1608 サイズの 0.1μF を2つずつはんだづけする。

戻す

はずしたコネクタを通すところを間違えるとハメられないので気をつけてハメていく。それ以外はとくに問題ないはず。

効果

全体的に変な動きは減った。しかし中央(2ch) のレンジをかえるロータリーエンコーダはそもそも不良っぽくて完全には治らなかった。

ref.

  1. トップ
  2. tech
  3. OWON SDS7102 のロータリエンコーダの不具合をなおす

先にまとめておくと、おむつの支出が支配的で、それ以外だと直接子ども用のものをあんまり買ってない。絵本とかをあんまり買ってない気がする。

おしりふき

【おしりふき 詰替用】グーン 肌にやさしいおしりふき タップリッチ 840枚(70枚×12個) [ケース販売] 【Amazon.co.jp限定】 - タップリッチ

タップリッチ

5.0 / 5.0

3ヶ月に1回ぐらい買ってるっぽい。

おむつ

メリーズ L サイズ → グーン Big サイズ → グーン Big より大きいサイズ と変わった。「Big サイズ」と「Big より大きいサイズ」はまだ併用してて、保育園で昼寝のときに漏れる問題が発生して保育園用のだけワンサイズ大きくしている。

ざっと合計したら60454円だった。だいたい5000円/月ぐらい。だけど保育料の支配率が高いので誤差みたいなもの……

子ども向けの本

おもちゃ

クリスマスプレゼント多くない? って感じだけど、祖父母分もはいっている (祖父母からは現金が渡ってきて、こちらで適当にあわせて買うという方式)

家具

育児関係で大人用のやつ

Raspberry Pi で Wi-Fi が一度切れると二度と繋がらない、という現象に遭遇した。しかたないので変なことをした。

#!/bin/sh

msg() {
        echo $1
        logger $1
}

while true
do
        if ifconfig wlan0 | grep -q "inet addr:"; then
                sleep 60
        else
                msg "Network connection down! Attempting reconnection."
                ifdown --force wlan0
                sleep 5
                ifup wlan0
                /etc/init.d/dhcpcd reload
                sleep 30
        fi
done

ifplugd とかも触ってみたけど、これが結局確実っぽい。なんかどうも dhcpd が ifup 時に反応してくれなくて、手動で reload してる。うーん。もっと一発で解決する方法があればいいんだけど……

ref

  1. トップ
  2. tech
  3. Raspberry Pi の Wi-Fi 自動再接続

systemd になって inittab が消滅したので途方にくれましたが以下のようにするとできました。

serial ログイン

UART ピンからのログインの場合

sudo vim /lib/systemd/system/serial-getty@.service
ExecStart=-/sbin/agetty --keep-baud 115200,38400,9600 %I $TERM

となっているので

ExecStart=-/sbin/agetty -a pi --keep-baud 115200,38400,9600 %I $TERM

とする。

なお、この状態でシェルで reset を実行すると halt や reboot がハングするようになる。謎…… reset を殺して対応してるけど理由がわからなくて気持ちわるい。systemd がおかしいのだろうか?

console ログイン

ssh 経由とかの場合。

sudo raspi-config から設定可能です。なので基本的には raspi-config でやったほうがよさそう。

以下は手動でやる方法

sudo vim /lib/systemd/system/getty@.service 
ExecStart=-/sbin/agetty --noclear %I $TERM

となっているので、以下のように

ExecStart=-/sbin/getty --noclear -a pi %I $TERM
  1. トップ
  2. tech
  3. Raspberry Pi 3 で自動ログイン

mackerel にメトリクスとして送る

mackerel-plugin-h2o みたいなのは今のところないっぽい? ので、status/json からとれる内容をポストするプラグインを書いた。

https://github.com/cho45/mackerel-agent-plugins/tree/mackerel-plugin-h2o/mackerel-plugin-h2o

mackerel プラグインの書きかた

他のプラグインにならって go-mackerel-plugin-helper を使ってみた。FetchMetrics の返り値でちょっと悩んだけど、以下のようにすればよさそう。

  • FetchMetrics ではとにかくとれる値をすべて map に詰めてかえす
  • GraphDefinition でグラフと値の関連付けを行う

GraphDefinition に含まれないメトリクスは、FetchMetrics で map に詰めても送信されない。これが最初理解できてなかったので、FetchMetrics でややこしいことをやってしまったりした。

  1. トップ
  2. tech
  3. h2o の status/json を mackerel に送る

いままでSANYO時代のエネループ充電器を使っていたが、必要性が増してきたのでこのようなものを買った。

ニッケル水素充電池用充電器 単3単4兼用 ホワイト TGX12 - TGX

TGX

5.0 / 5.0

単3と単4どちらも充電できる。一本ずつ充電管理されているのでバランスとかは考えなくて良い。

端子がちょっと固くて、しっかり押しこんでやらないと入らない。一応充電状態にはなってしまうので気をつける。写真のうちいくつかはちゃんと入ってない (撮ったときは気付かなかった) 9 と 10 はちゃんと入っている。1とか4はちゃんと入ってない。

音がちょっとうるさい

本体側で若干コイル鳴きしててうるさい。といってコイル鳴きなので、かなり静かな環境でなければ聞こえない。

待機電力

一本も電池を入れてない場合、液晶のバックライトが消えてスタンバイ状態になるようだ。このときの消費電力は 0.3Wぐらい。

全て充電完了になっている場合、液晶のバックライトがついて充電完了を知らせる表示になる。このときの消費電力は0.5Wぐらい。

その他

ACアダプタは12V 1.5A (18W) のものが付属していた。ポートあたり最大 500mA (1.2V) らしいので十分っぽい。

  1. トップ
  2. tech
  3. 12本のNiMHを充電できる充電器を買った

意識高い (意識高い系ではなく、真の) みると、ただただつらい。アドベントカレンダーで毎日意識高いエントリが目につく。精神が攻撃されている。もっと気楽に生きていきたい………

ミラクルニキ-お着替えコーデRPG - Apps on Google Play これ。公式サイトはここ

元々中国のアプリ「奇迹暖暖」で、それをローカライズして販売してるっぽい。暖暖シリーズはいくつかあるっぽく、過去にも日本語版ローカライズ版はでてたりするっぽいがやったことはない。ありがちな日本語に不自然なところはあまりない(が、UIではときどきある)。日本の提供会社の情報があんまりなくて怪しい以外は気になるところはない。

とりあえず最高に楽しい。いろいろゲームやってみた結果、着せ替えができさえすれば楽しいってことには薄々気付きつつあったけど (ポケモン・サン/ムーンやったら着せ替えの衣装が足りなくてものたりなかった)、このゲームは完全に着せ替えオンリーを極めててよい。着せ替え対象のキャラクターは一人だけ(ニキ)だけど、衣装数が極めて多く、髪型も変えれるので気にならない。

キャラクターのデザイン自体も「可愛いが男媚びも女媚びもしてない」ぐらいのバランスだし、衣装も細かく描かれててめっちゃいい。ニキのキャラ設定自体も鼻につかない感じで余計な部分はない。あえていうなら付属してくる変なゆるキャラみたいのがウザいぐらい。

基本的にあんまりハマりどころはないけど、ちょいちょいキツいステージがある。4-12 は衣装がほぼ固定で高得点が出しにくく S がなかなかとれなかった。S限定の衣装をガール級の 5-12 で使うので手に入れたかった (なお 5-12 は 3-1(プリンセス) や 4-5(プリンセス) のチャイナでも代用可能)

結局のところ、衣装でどうにかならないのはスキルでどうにかするしかないことがわかった。このステージだと脱がされないので、厳しい視線を回避してバフかければSとれそう。

自分のときはこれでいけた

h2o の status ディレクティブのJSON出力を眺めていたら、duration-25 とか connect-time-50 とか 不思議なプロパティがいくつか含まれることに気がついた。しかしどうも全て0のようであるので、なんらかのコンパイルオプションなのだろうか?と思ってちょっと調べた。

結局これは global で duration-stats: ON すると有効になるみたいだった。そうすると以下のように数字が埋まりはじめる。

"duration-0": 0,
"duration-25": 0,
"duration-50": 0,
"duration-75": 4731,
"duration-99": 26829

おそらくμs単位のパーセンタイル値で、この場合たとえば duration-75 は 75% のリクエストが 4731μs 以内に納まるみたいなやつだと思う。

今のところドキュメントには書いてないっぽい、というかリリースされてないっぽいので HEAD が必要?なのかな。

メモ

  • h2o_now は ms 単位
  • durations は h2o.h の COMPUTE_DURATION で *delta_usec = h2o_timeval_subtract*1; となっていてμsになってるっぽい
  • server-status はどこのホストに書いても同じのがかえってくる (あくまでサーバ共通のステータスがとれる)
  1. トップ
  2. tech
  3. h2o の duration stats

MH-Z19 という格安 CO2 センサを読んでみた | tech - 氾濫原 の続きです。

Raspberry Pi で喋らせる

Open jtalk で喋るようにしてあるので

  • 1000ppm を超えたら換気をうながす (アラート)
  • 600ppm 未満になったら告知する (アラート解除)

みたいにした。

alert = false

loop do
        begin
                ppm = get_latest.last
                case
                when ppm > 2000
                        system("jsay.sh", "CO2濃度が2000ppmを超えました。ただちに換気を行ってください")
                        alert = true
                when ppm > 1000
                        system("jsay.sh", "CO2濃度が1000ppmを超えました。換気を行ってください")
                        alert = true
                when alert && ppm < 600
                        alert = false
                        system("jsay.sh", "CO2濃度が600ppmまで下がりました")
                end
        rescue Exception => e
                p e
        end
        sleep 5 * 60
end

get_latest は GrowthForecast からデータをとってくるやつ。センサーからデータを読むプロセスは別にしてあるので、間接的にgfからとってきてる。

常に表示する

家で余っているスマフォに常時表示するようにした。これも GrowthForecast からデータをとる。具体的には

グラフは vis.js を使ってみた。結構つかいやすい。example のうちの 404 Not Found を参考にしたらうまくできた。

今後

GrowthForecast にデータを投げてるので、gf の API をメインで使ってみたが、gf はそういう用途のためのものじゃないので、かなり無駄感がある。WebSockeet 経由でデータを転送するゲートウェイみたいなのをまず作ったほうがいいかもしれない。

  1. トップ
  2. tech
  3. CO2 センサの可視化

花王の漂白剤シリーズに「ハイター」というのがあるが、種類がいっぱいあってよくわからない。というところだけど、実は公式のFAQに一覧があって、どのように成分が違うのかと用途が記載されている。

大きな違いは「界面活性剤」の有無になる。汎用性が高いのは含まれていないほうなので、とりあえず買うなら「ハイターE」「月星ブリーチC」あたり、「ハイターE」は一般向けだと「ハイター 衣料用漂白剤」になる。

【業務用 衣料用塩素系漂白剤】ハイターE 5kg(花王プロフェッショナルシリーズ) - 花王

花王

5.0 / 5.0

なお基本的に「ハイター」には塩素系を期待すると思うが、酸素系の「ハイター」も存在しているので注意が必要。

消毒用途では基本的に10倍希薄して使うことになっている (商品は6%だが、塩素は揮発してしまうため 5% 扱いとする)。0.5%以上 (5000ppm) の濃度で使用しても消毒効果は上がらず、残ってしまう時間が長くなるだけなので、必ず守る。

なお次亜塩素酸ナトリウムが重宝される理由としては、蛋白質と接触すると食塩に変化するため残留リスクが低いことにある。

ref

  1. トップ
  2. tech
  3. ハイターの種類と選びかた。最も汎用性が高いのは

QAサイトで間違ったことが書いてあると、正解を書きたくなる心理を利用して、どんな回答にもまず間違った答えを機械が書くというのはどうか (あっててもいいけど)

さくらのVPSのウェブサーバでIPv6の接続をうける | tech - 氾濫原 で、IPv6 アクセス環境がないと書いたが、スマフォで簡単に IPv6 接続できることを知った。

APN 設定

IIJ mio の場合、APN の設定で IPv4/IPv6を選ぶだけで優先的に v6 接続されるようになる。

全ホストの IPv6 対応

自力で確認できる環境が身近にあることがわかったので、このサーバでホストしているホストを IPv6 対応してみた (もとも IPv6 インターフェイスも listen してるので単に AAAA レコードをひけるようにしただけ)

ちゃんと接続されてるのか?

見た目が何も変わらないのが正しい状態なのだけど、IPv6 接続されてるのかよくわからないので、仕掛けをしてみた。

判定は単に REMOTE_ADDR を見て、IPv6 アドレスだったら IPv6 接続と判定する。: を含んでいたら IPv6 と判定するようにする。

このサイトの場合、ページ全体をキャッシュしているため、内容をIPv4/IPv6で出しわけると持つべきキャッシュが倍になってしまい現実的ではない。ということで、以下のようにした。

  • CSS で適当なところに content: attr(data-ip-info); と書いておく
  • JS で接続情報を取得するエンドポイントを叩き data-ip-info を埋める

ref

  1. トップ
  2. tech
  3. IPv6 対応にした

郑州炜盛电子科技 という会社の MH-Z19 という CO2 センサを買ってみました。Aliexpress で $22 ぐらいでした。

  • 非分散形赤外線吸収式
  • 電源は 3.6〜5V
  • インターフェイスは UART または PWM 3.3V
  • プリヒートは3分
  • 温度補正あり。精度は±50ppm + 5%

5V の電源と 3.3V のインターフェイスが必要ですね。

とりあえず動かしてグラフ化

Ruby 用のライブラリを書いて読みだして、GrowthForecast に投げてみました。Raspberry Pi 上で動かしています。シリアルは USB 経由で、PWM では GPIO 経由で読んでいます。

結構おもしろくて、人がいるかいないかはグラフから一目でわかるレベルです。換気するとそくざに値に反映されるのもおもしろいです。

Ruby 用のライブラリ

Ruby で読み出し用のライブラリを書いてみました。

Serial 経由の読みこみとPWM (Linux のみ。sys/class/gpio 経由) どちらとも実装してあります。

PWM

Serial は仕様書通りに書いただけですが、PWM はちょっと工夫がいるのと、データシートそのままだとちゃんと値がでず試行錯誤したのでメモしておきます。

/sys/class/gpio でのエッジトリガ

PWM はエッジトリガをしかけて時間をそこそこ正確に計測する必要があります。Linux の GPIO でやる方法がわからなかったので、ちょっと調べて以下のようにしました。

  • "/sys/class/gpio/gpio#{pin}/direction" を in に
  • "/sys/class/gpio/gpio#{pin}/edge" を both に
  • "/sys/class/gpio/gpio#{pin}/value" を開いて fd を取得
  • select でエッジを待つ
  • read で内容を読む
  • seek で戻す
  • select に戻る

select でブロックしてエッジが検出されると制御が戻ります。read したあと seek で戻さないと、次の select でブロックせずビジーループになってしまうようでした。

def self.trigger(pin, edge, timeout=nil, &block)
	self.direction(pin, :in)
	self.edge(pin, edge) #=> write "/sys/class/gpio/gpio#{pin}/edge"
	File.open("/sys/class/gpio/gpio#{pin}/value", "r") do |f|
		fds = [f]
		buf = " "
		while true
			rs, ws, es = IO.select(nil, nil, fds, timeout)
			if es
				f.sysread(1, buf)
				block.call(buf.to_i)
				f.sysseek(0)
			else
				break
			end
		end
	end
end

処理速度が不安でしたが、1ms 単位でとれれば十分なので Ruby + /sys/class/gpio でも十分でした。

PWM の場合の計算方法

データシートだと以下の計算式になっています。ここで th は PWM の high の時間、tl は low の時間、ppm は CO2 濃度です。

ppm = 2000 * (th - 2e-3) / ((th + tl) - 4e-3)

が、どうみてもデータシート通りの値がでてきませんでした。どうやら型番が一緒で、検出レンジが 0〜2000ppm のものと、0〜5000ppm のものがあるようで、2000 の部分を 5000 にするとうまく計算できました。

キャリブレーション

シリアル経由だと、ゼロ補正とスパン補正というのがあります。仕様書にどうやってキャリブレーションするか書いてないのですが、おそらく以下の手順です

  • CO2 がない環境 (窒素100%とか) でゼロキャリブレーション
  • 既知のCO2濃度の環境でスパンキャリブレーション (スパンキャリブレーション時に既知の濃度を渡します)

↑ の手順おそらく間違いなので以下の正しいと思われる手順を書きます。MH-Z19B のデータシートの手順です。

  • CO2 濃度が 400ppm の環境でゼロキャリブレーション
  • CO2 濃度は 2000ppm の環境でスパンキャリブレーション (1000ppm 以上の既知の濃度を使うこと)

まぁキャリブレーションは難しいのですが、一定期間内の最低値を外気のCO2濃度 (約400ppm) とみなしてときどき自動で補正していく方法もあるようです。

気象庁の二酸化炭素濃度のグラフを見る限り、近年は 400ppm 程度のようです。季節変動もみられますが、センサーの精度よりも小さいので無視できます。

二酸化炭素濃度の基準

ビル衛生管理上では 1000ppm を目安に換気せよと書いてあります。これは二酸化炭素の影響だけではなくて、これを目安として他の有害物質の滞留を防ぐという意味合いもあるみたいです。

また CO2 単体でも 2000ppm以上になると自覚できる症状が現れることがあるようです。

ref.

  1. トップ
  2. tech
  3. MH-Z19 という格安 CO2 センサを読んでみた

internal がデフォルトで、特に問題もないしずっとこれにしていたが、h2o が再起動されるたびにキャッシュが消えるのではないか? どうせ memcached も起動してるのでそっちにストアすべきではないか? と思いはじめたので変えてみた。

まぁ弱小ウェブサイトだとそもそも resumption かかるようなことがぜんぜんないので意味ない。Google Bot も分散アクセスのせいか全く resumption しないし。

  1. トップ
  2. tech
  3. h2o の ssl-session-resumption のストア先を memcached に

最近子どもがとなりのトトロにハマってて毎日見てるんだけど、今朝は「お父さん」と「お母さん」が病院で会話しているシーンを見て、「星野源みたいねぇ」「ガッキーみたいねぇ」とか言いだして笑った。メガネかけてると星野源みたいねぇと言うのはしばしばあるんだけど、さらに「ガッキーみたいねぇ」は初めて聞いた。

最近は直接見てないはずなので (というか親が見ようとすると嫌がる)、結構前のを覚えているようだ。