はじめに
最近CO2センサーが流行っていますね。三密を避ける指標にしたり、集中力が欠如していることを知らせるなど、CO2が一つの指標として使えるからだそうです。最近在宅勤務が増えてこともあるので、CO2の変化を測ってみたくなりました。
そこで、今回obnizのコンテストということもあり、obnizを使ってオリジナルのCO2センシングシステムを構築してみました。
まずは、どんなものか動画をみてみましょう。
こんな感じでobnizがセンシングをすると、その結果がLINEに届きます
デモ動画では即時実行していますが、普段の運用ではサーバーレストリガーで定期的に動くようにしています。
早速どのように開発したか紹介します
準備するもの
今回準備したものは以下になります
- obniz 1Y (コンテスト応募で入手)
- SCD30 (CO2センサー)
- アクリル板(ケース)
- ネジとスペーサー(obniz固定はM2 筐体はM3)
センサーが高かったです。1個で6000円オーバー・・・
システム構成
システム構成は以下の図のようになります
- obniz cloudからサーバーレスイベントトリガーが実行される
- obniz 1YがI2Cでセンサーと通信。センサー情報を取得し、その結果をwebhookで送信
- IFTTTを経由してLINE Notifyでセンサー情報を表示
という流れになります。このシステムですが、obnizを使っているからとても簡単に出来ており、普通にEPS32などを使うともう少し複雑になります。
obnizのエコシステムすごく便利だなと思います。
今回使うセンサーはobnizのライブラリが用意されていないということもあり、I2Cを使って生データを読み、その値を変換して利用することにします。少しハードルが高いですが、勉強がてらやってみます。
ハードウェアの準備
ハートウェアの準備といってもとてもシンプルで、SCD30にピンヘッダを半田付けします。
今回はL型のピンヘッダを使いました。7箇所半田付けしたらOK!
これだけでも実際動くのですが、センサーの機能を十分に活かすことができません。
そこで、obniz 1Yに用意されている、マイナスと3.3Vからセンサーの電源を供給することができるようにします。
半田付けができたら、写真のobnizにSCD30を取り付けて、ハードウェアの準備は完了。
IFTTT でAppletを作成
先にIFTTTでAppletを作成しておきます。
まずは "If this" を選択します。
そして、検索窓にwebhookと入力して選択しましょう。
"Receive a web request"を選択します
"getSensorData"などお好きなEvent nameを入力して Create triggerを選択します。これでトリガーの定義は完了です。
次にThatの項目を選択して、LINEを探しましょう。(手順は同じなので割愛)
send messageを選択します。
最後に、送られてくるメッセージを定義します。
正直そのままでもいいですが、Value 1をCO2,Value 2を 温度, value 3を湿度 と書き換えると、わかりやすいと思います
またvalue1 などはそのまま残しておきます。ここにobnizから送られてくるデータが表示されます。
※OccuredAtは右下のAdd ingredientから追加できます。
appletを定義する間に、webhookの詳細な情報が見れれなかったと思います。
出来上がったAppletから赤丸ので示したwebhookを選択し、右上のdocumentationを見ると、詳細なwebhookの説明が見れます。(後で必要)
コードを書く
それでは、実際にコードを書いていきます。
このシステムはサーバーレスということで、実はscriptしか使いません。なので、scriptのみのコードを紹介します。
細かい説明についてはコード内のコメントを確認してください
CO2センサー処理
<script>
var obniz = new Obniz("****-****"); //あなたのobnizの8桁のIDを入力
let address = 0x61;//CO2センサーのアドレス。 0x61固定です。
//データシートに記載のコマンドリスト
const START = [0x00, 0x10, 0x00, 0x00, 0x81]; //計測自体を始める
const STOP = [0x01, 0x04]; //計測自体を止める
const GET_READY = [0x02, 0x02]; //データが読める状態か確認
const READ_REQUEST = [0x03, 0x00]; //データを読む
//各センサー情報を格納する箱
var CO2 = 0;
var HUMIDITY = 0;
var TEMPERATURE = 0;
//センサーから取得した値をfloatの値に変換する
function floatConverter(array) {
// Create a buffer
var buf = new ArrayBuffer(4);
// Create a data view of it
var view = new DataView(buf);
// set bytes
array.forEach(function (b, i) {
view.setUint8(i, b);
});
// Read the bits as a float; note that by doing this, we're implicitly
// converting it from a 32-bit float into JavaScript's native 64-bit double
var num = view.getFloat32(0);
// Done
console.log(num);
return num;
}
//obnizに接続された時に動く処理
obniz.onconnect = async function () {
var led = obniz.wired("LED", { anode: 0, cathode: 1 });
obniz.display.clear();
obniz.display.print("Start CO2 sensing");
//電源を取る ※この方法をとると、トリガー起動時しかセンサーは起動しない。そのため、オートキャリブレーションなどの機能が使えない。
//推奨は電源をobniz 1Yの 3.3Vと-から電源を取ること
// obniz.io11.drive("5v"); //obniz
// obniz.io11.output(true);//CO2センサーを5Vで駆動。obnizのGPIOは3Vで、センサーの要求電源電圧より低いため
// obniz.io10.output(false); //GNDに設定
// await obniz.wait(1000); // 電源投入後1秒待つ。
//I2C接続する
var i2c = obniz.getFreeI2C();
i2c.start({ mode: "master", sda: 8, scl: 9, clock: 50000, pull: "3v" }); //センサーは100khzまで。とりあえず50khzで動かしてみます。
//sda,sclを3Vでプルアップする。こうすることでプルアップ抵抗が不要になる
//接続成功の確認
i2c.onerror = function (err) {
console.error(err);
return;
};
//計測開始する
i2c.write(address, START);
//await obniz.wait(3000); //電源を取っていない場合、起動後すぐには取得できないので、少し待つ必要がある。
var ret;
for (var step = 0; step < 4; step++) {
i2c.write(address, GET_READY);//センサーデータが取得できているか確認する。
try {
ret = await i2c.readWait(address, 3);
console.log("read " + ret);
if (ret[0] == 0x00 && ret[1] == 0x01) { //取得できていれば、決まったレスポンスが返ってくる
break;
} else {
ret = null;
}
} catch (e) {
console.error(e);
}
await obniz.wait(500);
}
if (ret == null) {
//エラーと判断
console.error("device didn't get ready");
return;
}
//実際に値を取得する
i2c.write(address, READ_REQUEST);
try {
var ret = await i2c.readWait(address, 18);//データシートより18byteの情報がくる
// console.log("read " + ret); //生データ
//送られてきたデータをfloatの数字データに変換
//しかし、値が大きすぎるので、小数点2桁で切り捨てる
CO2 =
Math.round(floatConverter([ret[0], ret[1], ret[3], ret[4]]) * 100) / 100;
TEMPERATURE =
Math.round(floatConverter([ret[6], ret[7], ret[9], ret[10]]) * 100) / 100;
HUMIDITY =
Math.round(floatConverter([ret[12], ret[13], ret[15], ret[16]]) * 100) / 100;
//読み込む配列が一部飛んでいるが、2byteごとにCRCが来ているため。
console.log("co2 :" + CO2);
console.log("temp :" + TEMPERATURE);
console.log("humi :" + HUMIDITY);
//取得した結果をiftttに送る
let token = "******"; //あなたのIFTTTのtokenを記入
let eventName = "getSensorData"; //IFTTTのapletを設定した時のイベント名
let url =
"https://maker.ifttt.com/trigger/" + eventName + "/with/key/" + token;
let payload = {
value1: CO2 + "ppm",
value2: TEMPERATURE + "℃",
value3: HUMIDITY + "%",
}; //JSON.stringifyする必要ない!
//webhookを送る
$.ajax({
type: "POST",
dataType: "json",
url: url,
processData: true,
data: payload,
}).then(
function (data) {},
function (XMLHttpRequest, textStatus, errorThrown) {
console.log("post to bot");
}
);
obniz.display.clear();
obniz.display.print("complete!");
await obniz.wait(1500);
obniz.display.clear();
} catch (e) {
console.error(e);
}
// i2c.write(address, STOP);//このコマンドを実行すると、計測を止めてしまう。
};
</script>
コードはこれでOK。
実行してみると、console.logに温度や湿度、そしてCO2の結果が見れるようになります。
サーバーレスイベントトリガーを設定する
コードが動くことが確認できたら、サーバーレスイベントの設定をしましょう
実行選択項目から、"(レガシー)サーバーレスイベントで実行"を選択します
すると以下のページが表示されます。
そこで、イベントの名前とトリガーを設定することができます。設定したら"作成"を選択して完了です
注意しなければならないのはobnizは1日に150回までしかイベントトリガーを出せないことです。
なので、1分おきなどは出ません。縮めることができても最大10分毎になります。
サーバーレスイベントを作成すると、トリガーのタイミングになったら際にLINEにセンサー情報が届くようになります。
筐体を作ってみる
置いて使いたいなぁと思ったので、簡単ですがobnizを立てられるようアクリル板とスペーサーで筐体を作ってみました。
以下リンクにDXFファイルがありますので、レーザーカッターを使ってカットしてもらえればと思います。
アクリル板同士の固定はM3のスペーサー、obnizの固定にはM2のスペーサーを使っています。
どちらも秋月電子で購入可能です。
スマートホームIoTとして利用するためにコードを修正
前述のコードはとても基本的な使い方の説明となっており、定期的にデータが送られてくるだけです。流石にこれは使いにくいかと思います。
そこで毎回送るのではなく、obnizのセンサー情報からある条件になった時にのみメッセージを送るように変更してみます。
CO2濃度が2000ppmを超えたらお知らせしたり、気温が30℃を超えたらエアコンをつけるようにお知らせするようにしてみます。もちろん新たにIFTTTのAppletを作ります。
全部コードを載せると長くなってしまうので、センサー情報を読み込むところのみ記載します。
スマートホーム仕様
//実際に値を取得する
i2c.write(address, READ_REQUEST);
try {
var ret = await i2c.readWait(address, 18); //データシートより18byteの情報がくる
// console.log("read " + ret); //生データ
//送られてきたデータをfloatの数字データに変換
//しかし、値が大きすぎるので、小数点2桁で切り捨てる
CO2 =
Math.round(floatConverter([ret[0], ret[1], ret[3], ret[4]]) * 100) / 100;
TEMPERATURE =
Math.round(floatConverter([ret[6], ret[7], ret[9], ret[10]]) * 100) / 100;
HUMIDITY =
Math.round(floatConverter([ret[12], ret[13], ret[15], ret[16]]) * 100) /
100;
console.log("co2 :" + CO2);
console.log("temp :" + TEMPERATURE);
console.log("humi :" + HUMIDITY);
var eventName = "";
if(CO2 >= 2000){ //2000ppmを超えると、換気をした方が良くなる
eventName = "NoteExchangeAir"
}else if(TEMPERATURE >= 30){ //温度が高くなったら、エアコンをつけるように知らせる
eventName = "TurnOnAirConditioner"
}
//取得した結果をiftttに送る
let token = "*************"; //あなたのIFTTTのtokenを記入
// eventName = "getSensorData"; //IFTTTのapletを設定した時のイベント名
let url =
"https://maker.ifttt.com/trigger/" + eventName + "/with/key/" + token;
let payload = {
value1: CO2 + "ppm",
value2: TEMPERATURE + "℃",
value3: HUMIDITY + "%",
}; //JSON.stringifyする必要ない!
if (eventName != ""){//eventNameが入っている時だけwebhookを送る
$.ajax({
type: "POST",
dataType: "json",
url: url,
processData: true,
data: payload,
}).then(
function (data) {},
function (XMLHttpRequest, textStatus, errorThrown) {
console.log("post to bot");
}
);
obniz.display.clear();
obniz.display.print("complete!");
await obniz.wait(1500);
obniz.display.clear();
}
} catch (e) {
console.error(e);
}
こうすることで、何か知らせた方がいい時にしか通知が来ないので、鬱陶しいという事はなくなります。
最後に
今回CO2センサーを使ったobnizのシステムを構築してみました。
締め切った部屋だと2000ppmを超えて、本当は換気をしなければならないことがわかりました。新しい発見です。
こうも簡単にCO2濃度は変化するとは知りませんでした。正直なかなかいいものが出来たなと思っています。
話は変わり、obnizはすでにライブラリが用意されたデバイスを使えば簡単にコードが書けますが、用意されていないモジュールに対してもI2Cが用意されていて、ちゃんと信号を取得することができました。ハードウェア的にもミニマム構成で実現できるようになっていることに驚きました。
物だけ見るとすごくシンプルな構成でなんだかモノ作った感がないですが、逆にそれで実現できることがobnizの凄さ。
今回はイベントトリガーを初めて使ってみましたが、とても便利に進化していました。以前はlambdaにobniz のライブラリを入れて・・・と大変なことをやっていたのですが、こちらもobnizの開発者コンソールでほぼ完結するようになっていて、開発時間を短くすることができました。
今回作ったモノ、特にコードは参考になると思うので、ご活用いただけると幸いです。
投稿者の人気記事
-
norippy
さんが
2021/05/09
に
編集
をしました。
(メッセージ: 初版)
-
norippy
さんが
2021/05/09
に
編集
をしました。
-
norippy
さんが
2021/05/10
に
編集
をしました。
ログインしてコメントを投稿する