M5Stackからスマホ/PCにCO2濃度上昇を通知する(Pushbullet)
概要
先日の記事で紹介した、M5StackによるCO2濃度モニタはただ数値とグラフを表示するだけだった。
このままではCO2濃度が高いのか低いのか、人が忘れないようにチェックする必要がある。
そこで今回は、それに改良を加えCO2濃度が一定レベルを超えたらスマホ/PCに通知を送るようにした。
通知にはPushbulletを使っているが、M5StackからPushbulletを使って通知を送る方法について述べる。
ソースコードはGithubで公開しているので、記事を読むのが面倒な人はこちらを参照のこと。 (この記事に対応するソースコードのバージョンはv2.0である)
最終的にはPushbulletのアプリを通じてこんな感じにスマホへプッシュ通知される。PushbulletにPC(Chrome拡張)を登録しておけば、PCにも同時に通知される。
スマホ・PCへのプッシュ通知はPushbulletが便利
Pushbulletは、様々な端末同士をつないでチャットしたりメッセージや写真を送ったり通知を共有するサービスである。
情報は、テキスト・URL・写真など何でも良い。
スマホとPC間のちょっとしたデータの共有に便利なので、電子工作に限らず私はよく使っている。
また、APIが豊富に用意されているため、インターネットに繋がってHTTPが使えるデバイスからも利用可能である。
以前ブログに書いた「RaspberryPiで再配達を撲滅するシステム」でも、スマホ/PCへの通知にPushbulletを使っている。
M5Stackからプッシュ通知を送る
さて、ここからM5Stackからスマホ・PCへPushbulletを使ってプッシュ通知を行う方法を述べる。
Pushbulletアカウント、Access Tokenの取得
この後は、すでにPushbulletのアカウントを持ち、Access Tokenを取得している前提とする。
Access Tokenの取得方法については以下の過去記事参照。
プッシュ通知タイミングの設計
今回は、以下のしきい値でプッシュ通知を送ることとした。
1000ppmを超えたら、注意レベルとし、換気を促す。
2000ppmを超えたら、危険レベルとし、速やかな換気を要請する。
しきい値付近では値がふらつくことが予想されるため、一度通知をプッシュしたら一定時間は通知をしないようにした。
ソースコード
ソースコードは全てGithub上で公開している。詳細はそちらを参照のこと。
なお、この記事では詳細は書いていないが、Ambientを使ったセンサ値のログも同時に実装している。ESPによるAmbientの使い方は公式チュートリアルが豊富にあるのでそちらを参照のこと。 以下では、ソースコードのうち特に重要な点について抜粋して述べる。
まず、Pushbulletではhttpsを使うため、WiFi.h
に加えてWiFiClientSecure.h
が必要である。
#include <M5Stack.h> #include <WiFi.h> #include <WiFiClientSecure.h> const char *ssid = "YOUR-WIFI-SSID"; // write your WiFi SSID (2.4GHz) const char *password = "YOUR-WIFI-PASSWORD"; // write your WiFi password WiFiClientSecure secureClient; #define PB_APIKEY "YOUR-PUSHBULLET-API-KEY" // write your Pushbullet API key
ESP32は2.4GHzのWiFiのみ対応しているので、SSIDには2.4GHzの方を記述すること。
setup()
内で、WIFIをセットアップしておく。Pushbullet用関数(後述)はWIFIさえセットアップしておけば、他にセットアップは必要ない。
// Wifi setup M5.Lcd.print("WiFi setup..."); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } M5.Lcd.println("done"); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.print(WiFi.localIP());
次に、loop()
内でセンサの値を取得したら、前回のCO2濃度と比較し、濃度しきい値を超えたらユーザへ通知を送る(notifyUser()
、後述)。
co2_ppm
にCO2濃度が格納されているものとする。
ここでは、CO2濃度をNORMAL, CAUTION, WARNINGの三段階に分け、前回取得時よりもレベルが上がっていたら通知を出している。
ただし、一度通知を出すと通知がpauseされ(pause_notify_[caution|warning]
)、一定時間通知は出されない。また、プログラム開始からも一定時間は通知を出さない(起動直後の値のふらつき対策)。
#define CO2_CAUTION_PPM 1000 #define CO2_WARNING_PPM 2000 int notify_timer_caution = 0; int notify_timer_warning = 0; bool pause_notify_caution = true; bool pause_notify_warning = true; #define PAUSE_LENGTH 600 // do not notify PAUSE_LENGTH [s] once notified enum { LEVEL_NORMAL, LEVEL_CAUTION, LEVEL_WARNING }; int co2_level_last = LEVEL_NORMAL; // (中略) void loop() { if (airSensor.dataAvailable()) { // get sensor data(前回記事参照) co2_ppm = airSensor.getCO2(); int co2_level_now; // check co2 level if (co2_ppm < CO2_CAUTION_PPM) co2_level_now = LEVEL_NORMAL; else if (co2_ppm < CO2_WARNING_PPM) co2_level_now = LEVEL_CAUTION; else co2_level_now = LEVEL_WARNING; // notify user when co2 level exceed threshold if (co2_level_now > co2_level_last) { if (co2_level_now == LEVEL_CAUTION && !pause_notify_caution) { if(notifyUser(co2_level_now)){ Serial.println("notifyUser(): CAUTION"); }else{ Serial.println("notifyUser(): failed!"); } pause_notify_caution = true; } if (co2_level_now == LEVEL_WARNING && !pause_notify_warning) { if(notifyUser(co2_level_now)){ Serial.println("notifyUser(): WARNING"); }else{ Serial.println("notifyUser(): failed!"); } pause_notify_warning = true; } } co2_level_last = co2_level_now; } delay(SENSOR_INTERVAL_S * 1000); // SENSOR_INTERVAL_S [秒]ごとにセンサ値取得 // 一度通知を出したら一定時間通知を出さないためのタイマー if (pause_notify_caution) { notify_timer_caution += SENSOR_INTERVAL_S; Serial.printf("notify_timer_caution: %d\n", notify_timer_caution); if (notify_timer_caution > PAUSE_LENGTH) { notify_timer_caution = 0; pause_notify_caution = false; Serial.println("notify_timer_caution set false"); } } if (pause_notify_warning) { notify_timer_warning += SENSOR_INTERVAL_S; Serial.printf("notify_timer_warning: %d\n", notify_timer_warning); if (notify_timer_warning > PAUSE_LENGTH) { notify_timer_warning = 0; pause_notify_warning = false; Serial.println("notify_timer_warning set false"); } } }
最後に、ユーザへ通知をプッシュする関数は以下の通りである。
PushbulletへのHTTP通信は、コード中にも記載の通りこちらのサイトを使わせていただいた。
PushbulletはREST APIが利用でき、HTTP POSTでプッシュ通知を実装できる。
通知中の情報としては、title(通知タイトル、ここではCO2 Monitor)、body(通知文)を与えられる。
// reference: https://fipsok.de/Esp32-Webserver/push-Esp32-tab bool pushbullet(const String &message) { const char *APIKEY{PB_APIKEY}; const uint16_t timeout{5000}; const char *HOST{"api.pushbullet.com"}; String messagebody = R"({"type": "note", "title": "CO2 Monitor", "body": ")" + message + R"("})"; uint32_t broadcastingTime{millis()}; if (!secureClient.connect(HOST, 443)) { Serial.println("Pushbullet connection failed!"); return false; } else { secureClient.printf("POST /v2/pushes HTTP/1.1\r\nHost: %s\r\nAuthorization: Bearer %s\r\nContent-Type: application/json\r\nContent-Length: %d\r\n\r\n%s\r\n", HOST, APIKEY, messagebody.length(), messagebody.c_str()); Serial.println("Push sent"); } while (!secureClient.available()) { if (millis() - broadcastingTime > timeout) { Serial.println("Pushbullet Client Timeout !"); secureClient.stop(); return false; } } while (secureClient.available()) { String line = secureClient.readStringUntil('\n'); if (line.startsWith("HTTP/1.1 200 OK")) { secureClient.stop(); return true; } } return false; } bool notifyUser(int level) { const char *title = "CO2 Monitor"; char body[100]; switch (level) { case LEVEL_CAUTION: sprintf(body, "CO2 exceeded %d ppm. Ventilate please.", CO2_CAUTION_PPM); return pushbullet(body); case LEVEL_WARNING: sprintf(body, "CO2 exceeded %d ppm. Ventilate immediately.", CO2_WARNING_PPM); return pushbullet(body); default: return false; } }
ここまでできれば、CO2が一定レベルを超えるとプッシュ通知される。
スマホへは記事トップのように通知されるが、PC(Windows10)の場合は以下のような感じである。
まとめ
前回作成したCO2濃度モニタに、Pushbulletを使ったプッシュ通知機能を実装した。
これでより意識的に換気できるだろう。
さぁ、空気を入れ替えよう。