はじめに
CO2センサー(MH-Z19C)を購入したので、AWS IoTのお勉強も兼ねて、値を送ってみました。
なお。今回は送った値をS3に保存するようにしました。
準備
ハードウェア
今回使用したハードは以下の通りです。
- ESP32-WROVER-E開発ボード(8MB)
-
【注意】ハードウェアシリアルポートが1つしかないため、今回はPWMで接続しています
ソフトウェアシリアル版はこちら
-
【注意】ハードウェアシリアルポートが1つしかないため、今回はPWMで接続しています
- MH-Z19C
- ブレッドボード
- ジャンパーケーブル
- USBケーブル
- PC(Windows)
ソフトウェア
ArduinoIDEを使用するに当たり、いくつかライブラリをインストールしました。
- MHZ19(githubからzipをダウンロードし、インストール)
- ArdionoJson
- PubSubClient
- WiFiClientSecure
- EspSoftwareSerial
配線
ESP32とMH-Z19Cの接続は以下の通りです。
- ESP32のIO14番をMH-Z19CのPWMに接続
- ESP32の5VをMH-Z19CのVinに接続
- ESP32のGNDをMH-Z19CのGNDに接続
プログラム
まず、MH-Z19ライブラリを修正します。
オリジナルでは、計測範囲が2,000PPM固定になっているため、今回使用するMH-Z19Cに合わせ、5,000PPMまで測れるようにします。
(途中省略)
// Pwm Data Flag
uint8_t PWM_DATA_SELECT = MHZ19_PWM_DATA::CALC_5000_PPM; // 2000→5000
};
#endif
Ardiono側のプログラムは以下の通りです。
#include <WiFiClient.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <Arduino_JSON.h>
#include <MHZ19.h>
// WiFi設定情報(自分の無線LAN環境)
const char* SSID = "mySSID"; // 変更すること
const char* PASSWORD = "myPASSWORD"; // 変更すること
// AWS IoT設定情報
const char* AWS_ENDPOINT = "xxx.iot.ap-northeast-1.amazonaws.com"; // 変更すること
const int AWS_PORT = 8883;
const char* PUB_TOPIC = "topic/fromDevice";
const char* SUB_TOPIC = "topic/fromCloud";
const char* CLIENT_ID = "esp32";
const char* ROOT_CA = "-----BEGIN CERTIFICATE-----\n" \
"****************************************************************\n" \
"-----END CERTIFICATE-----\n"; // 変更すること
const char* CERTIFICATE = "-----BEGIN CERTIFICATE-----\n" \
"****************************************************************\n" \
"-----END CERTIFICATE-----\n"; // 変更すること
const char* PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----\n" \
"****************************************************************\n" \
"-----END RSA PRIVATE KEY-----\n"; // 変更すること
// MQTT設定
#define QOS 0 // 届こうが届くまいが1回だけメッセージをPublishする(0か1を指定可)
WiFiClientSecure httpsClient;
PubSubClient mqttClient(httpsClient);
// MH-Z19C
const int pwm_pin = 14;
MHZ19* myMHZ19 = new MHZ19(pwm_pin);
char pubMessage[128];
void setup_wifi(){
Serial.print("Connecting to ");
Serial.println(SSID);
// ESP32でWiFiに繋がらなくなるときのための対策
WiFi.disconnect(true);
delay(1000);
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void setup_awsiot(){
httpsClient.setCACert(ROOT_CA);
httpsClient.setCertificate(CERTIFICATE);
httpsClient.setPrivateKey(PRIVATE_KEY);
mqttClient.setServer(AWS_ENDPOINT, AWS_PORT);
mqttClient.setCallback(mqttCallback);
}
void connect_awsiot() {
while (!mqttClient.connected()) {
Serial.print("Attempting MQTT connection...");
if (mqttClient.connect(CLIENT_ID)) {
Serial.println("Connected.");
mqttClient.subscribe(SUB_TOPIC, QOS);
Serial.println("Subscribed.");
} else {
Serial.print("Failed, rc=");
Serial.print(mqttClient.state());
Serial.println(" Try again in 5 seconds");
// 5秒後にリトライ
delay(5000);
}
}
}
void mqttCallback (char* topic, byte* payload, unsigned int length) {
Serial.print("Received. topic=");
Serial.println(topic);
char subMessage[length];
for (int i=0; i < length; i++) {
subMessage[i] = (char)payload[i];
}
Serial.println(subMessage);
// JSON前提でパースする
JSONVar obj = JSON.parse(subMessage);
// 今は受け取っても何も処理しない
}
void setup(){
Serial.begin(115200);
delay(100);
setup_wifi();
setup_awsiot();
}
void loop(){
// CO2取得
int CO2 = myMHZ19->getPpmPwm();
// Publishするメッセージの作成
sprintf(pubMessage, "{\"co2\": %d}", CO2);
Serial.print("Publishing message to topic ");
Serial.println(PUB_TOPIC);
Serial.println(pubMessage);
mqttClient.loop();
while (!mqttClient.publish(PUB_TOPIC, pubMessage)) {
if (!mqttClient.connected()) {
connect_awsiot();
}
mqttClient.loop();
}
Serial.println("Published.");
delay(1000*60*5); // 5min
}
- SSIDに、接続するWi-FiのSSIDを設定すること
- PASSWORDに、接続するWi-Fiのパスワードを設定すること
- AWS_ENDPOINTに、デバイスデータエンドポイント(後述)を設定すること
- ROOT_CAに、保存したルートCA(後述)を設定すること
- CERTIFICATEに、ダウンロードしたxxx.pem.crt(後述の「このモノの証明書」)を設定すること
- PRIVATE_KEYに、ダウンロードしたxxx-private.pem.key(後述の「プライベートキー」)を設定すること
なお、AWS IoTからのメッセージを受信できるようになっていますが、今は何もしていません。
(将来用です)
AWS IoT Coreの設定
ポリシーの作成
- アクションに「iot:*」を指定する
- リソースARNに「*」を指定する
- 効果を「許可」する
モノの作成
続いて、1-Click証明書を作成するために「証明書を作成」を選択します。
作成された証明書をすべてダウンロードします。
また、AWS IoTのルートCAもダウンロードします。
ルートCAは、この画面の「Amazon Root CA 1」をダウンロードします。
なお、ここでダウンロードしたファイルの中身を、ArduinoIDEで設定します。
また、「有効化」をクリックします。
ルールの作成
続けて、ルールクエリステートメントを設定します。
今回は、ESP32から送られてきたメッセージ全部を使用するため、クエリを「SELECT * FROM 'topic/fromDevice'」としました。
(ESP32からMQTTで送るとき、「topic/fromDevice」にpublishしているため)
次に、アクションを設定するため「アクションの追加」を選択します。
送り先のS3のバケットとキー(ファイル名みたいなもの)を指定します。
今回指定したキーには、モノの名前と時刻を使用して一意になるようにしました。
続けてロールを設定するため、「ロールの作成」を選択します。
デバイスデータエンドポイントの確認
ArduinoIDEで設定するデバイスデータエンドポイントの値を確認します。
動作確認
ちゃんと動いているかの確認のため、ESP32からデータを送ってみます。
まず、AWS IoT Coreに届いたかどうかは、テストで確認できます。
トピックのフィルターに「topic/fromDevice」を指定し、「サスクライブ」を選択すると、届いたメッセージが表示されます。
{"co2": 1470}
テストで受け取った値と同じものが、S3のファイルにも設定されていました。
まとめ
実際に使用する際にはグラフ等で可視化することが多いので、IoT Coreから直接ElasticSearchに送ったり、いったんDynamoDBに入れたりすることになると思います。
参考URL
https://qiita.com/nara256/items/a3cecc5fbf043b2d042c
https://scrapbox.io/kn1cht/MH-Z19_CO2%E3%82%BB%E3%83%B3%E3%82%B5_on_ESP32