前回は「AWS(アマゾンウェブサービス)」の「AWS IoT」でビジュアルプログラミングの「UiFlow」を使用したクラウド経由の遠隔操作、データ監視をする方法を紹介しました。
今回はより自由度の高い「Aruduinoコマンド」を使用した「C言語」によるMQTT通信でのクラウド経由の遠隔操作、データ監視をする方法を詳しく紹介します。
動作については前回「UiFlow」で作成したものとほぼ同じ動作です。
「ビジュアルプログラミング」の「UiFlow」を使用して、より簡単にMQTT通信による遠隔操作、データ監視を体験できる方法は以下のリンクで詳しく紹介しています。
AWSの利用料金については以下のリンクにてご確認ください。
私は好奇心だけで無知なまま始めたので、使い方を間違って¥5,000程捨ててしまいましたw
これについては失敗談として以下のリンクで詳しく紹介しています。
こうならないためには「AWS」の「請求状況の確認」と「意図しない課金が発生した場合の停止方法」を知っておくことが大切なため、この方法も以下のリンクで紹介しています。
1.今回実現したいこと
・動作紹介、全体の構成
・使うもの
・Arduinoとは
2.AWS(Amazon Web Services)とは
3.通信方式 MQTTとは
4.サンプルプログラミングで動作確認
・ArduinoコマンドでMQTT通信する方法
・セキュリティ証明書参照ファイルの準備
・ホスト(親機)側サンプルプログラム
・ローカル(子機)側サンプルプログラム
・動作確認
5.まとめ
1.今回実現したいこと
今回はインターネットに接続可能な2個のデバイス(M5Stack社製)を使用して「Arduinoコマンド」でクラウド経由の遠隔操作とデータ監視を行います。
以下から動作や全体の構成、使用するものやプログラミング環境について詳しく紹介します。
・動作紹介、全体の構成
遠隔操作の例としては、親機側(CORE2)から子機側(M5StickC Plus)のLEDを操作します。
子機側からの信号を親機側が受けた時には画面の色を一瞬変化させます。
データ監視の例としては、子機側で測定した温湿度、気圧のデータを親機側からの操作で取得して親機に表示させます。子機側からの操作でも測定データを送信して親機に表示させます。
通信にはMQTTを使用し、全体の構成は下画像のようになります。
・使うもの
使うものとしては下画像のように「M5Stack社」製の2つのデバイス(モノ)とセンサーを使用します。
親機としては「CORE2(画像左側)」を使用し、子機には「M5StickC Plus」に環境センサ「ENVⅢ」を接続(画像右側)して温湿度と気圧を測定し表示します。
「CORE2」「M5StickC Plus」「ENVⅢ」については以下のリンクで詳しく紹介しています。
使用するものは以下になりますが「M5Stack社」のデバイスなら何でも良いので、慣れた方なら手持ちのデバイスに置き換えて動作確認してみると、より理解が深まると思います。
・Arduinoとは
「Arduino IDE」とはArduino」の開発環境ですが「M5Stackシリーズ」でも使えて無償でダウンロードすることができます。
複雑で専門的な知識を必要とするプログラムが「ライブラリ」としてまとめられており、直感的に理解しやすいコマンドで「ライブラリ」を使用することで、初心者でも簡単に複雑な動作を実現することができます。
プログラミング言語は「C言語」をベースに、マイコンボードを制御するための「Arduino」独自のコマンドを使用して、マイコンボードを動かしながらプログラミングを進めることで「C言語」も自然に身につくため「C言語」の学習にも最適です。
「Arduino IDE」のインストール方法、使い方は以下のリンクで詳しく紹介しています。
2.AWS(Amazon Web Services)とは
「AWS」とは「Amazon Web Services」の略で「アマゾン」がインターネット経由で200以上のサービスを提供している「クラウドコンピューティングサービス」のことです。
「AWS」ではクラウドサーバーを使用して情報処理から大容量データ記録、データベースによるデータ管理から機械学習、AIによるデータ分析等、用途に応じて最適なツールを選択して使用することができます。
今回使用するIoTの機能「AWS IoT Core」にも様々な機能があり、何十億ものデバイスを安全に接続、管理でき、様々な業種向けにデータ収集、保存、分析するツールも提供されています。
詳細は以下リンクの「AWS」の公式ページでご確認ください。
3.通信方式 MQTTとは
今回クラウド経由のデータ通信を行う方法としてMQTT (Message Queuing Telemetry Transport)を使用します。MQTTとは、データパケットの小さい軽量な通信手段です。
HTTP通信では、送信側のリクエストと、受信側のレスポンスデータそれぞれに詳細な情報を含むヘッダーを使用しますが、MQTTのヘッダーは通信に必要な最小限の情報のみです。
通信方法は「パブリッシュ(送信)ー サブスクライブ(受信)」パターンを使用して、デバイス間でメッセージを送受信します。これにはブローカーという中継サーバーを使用します。
データの送信先は「トピック」として任意の名前を設定して「トピック」宛に送受信を行います。
- パブリッシュ(Publish)とは
メッセージを送信するデバイスが、送信先としてトピック(Topic)を指定して、ブローカーに対してメッセージを発行(送信)することです。 - サブスクライブ(Subscribe)とは
メッセージを受信するデバイスが、受信先としてトピック(Topic)を指定して、ブローカーに対してメッセージを購読(受信)することです。 - ブローカー(Broker)とは
ブローカーは「パブリッシュ」されたメッセージを「サブスクライブ」しているデバイスに対して、メッセージを配信します。
トピックに対してのアクセス制御や認証などのセキュリティ機能も提供することができます。
MQTTではシステムやネットワークが、障害や故障などの予期しない状況に対して、影響を最小限に抑えるための複数のバックアップや代替手段を持っています。
例えば、ブローカーAとブローカーBがあって、ブローカーAが故障した場合、ブローカーBが自動的にブローカーAの役割を引き継ぎ、通信を継続することができるため、確実にデータを送信することができます。
4.サンプルプログラミングで動作確認
「コピペ」で動作確認できるように親機(CORE2)と子機(M5StickC-Plus)のサンプルプログラムを準備しました。
・ArduinoコマンドでMQTT通信する方法
「AWS IoT」で「MQTT」通信するための初期設定から送受信方法は以下のようになります。
AWS IoTの初期設定
「AWS IoT」の初期設定のみを抜粋すると以下のようになります。
※別途Wi-Fi接続の設定が必要ですが、ここでは省略しています。この後の全体のサンプルプログラムで確認してください。
#include <PubSubClient.h> // ① MQTT通信用ライブラリ
// ② AWS IoT 接続情報
#define DEVICE_NAME "core2_01" // AWS IoT 登録デバイス名
#define AWS_IOT_ENDPOINT "xxxxxxxxxx.amazonaws.com" // AWSエンドポイント(接続先アドレス)
#define AWS_IOT_PORT 8883 // AWS接続ポート番号(固定)
#define QOS 0 // 通信品質設定(0:送信完了の確認をしない,高速)
// ③ 外部ファイル(certs.cpp)から認証情報を取得(この後詳しく紹介しています。)
extern const char *root_ca; // ルート証明書
extern const char *certificate; // クライアント証明書
extern const char *private_key; // プライベートキー
// ④ インスタンス
WiFiClientSecure https_client; // https通信用
PubSubClient mqtt_client(https_client); // MQTT通信用
// ⑤ AWS IoT初期設定関数 ******************************************
void connect_aws(){
https_client.setCACert(root_ca); // ルート証明書を設定
https_client.setCertificate(certificate); // クライアント証明書を設定
https_client.setPrivateKey(private_key); // プライベートキーを設定
mqtt_client.setServer(AWS_IOT_ENDPOINT, AWS_IOT_PORT); // エンドポイントとポート番号設定
mqtt_client.setCallback(subscribe); // サブスクライブ(受信)した時に呼び出す関数設定
// ⑥ MQTT接続開始
while (!mqtt_client.connected()) { // MQTT接続完了までループ
delay(1000);
if (mqtt_client.connect(DEVICE_NAME)) { // DEVICE_NAMEに接続完了なら
mqtt_client.subscribe("topic1", QOS); // サブスクライブ(受信)するトピックを設定
// mqtt_client.subscribe("topic2", QOS); // トピックが複数ある場合は続けて設定
delay(1000); // 接続完了表示時間
} else { // 接続失敗なら
// エラー処理
}
}
}
「AWS IoT」の初期設定は以下の手順となります。
①ライブラリの準備
MQTT通信に必要なライブラリとして「PubSubClient」をインクルードします。
②AWS IoT 接続情報の準備
「AWS IoT」で設定した「デバイス名」や決められた「エンドポイント(接続先アドレス)」等を設定します。
③クライアント証明書、秘密鍵の準備
SSL通信のための証明情報を外部ファイルから読み込んで設定します。(この後詳しく紹介)
④インスタンスの準備
https通信とMQTT通信のために必要なインスタンスを準備します。
⑥MQTT接続開始
登録した情報でAWSへの接続を開始し、MQTT通信の接続を確立します。
接続完了したら通信先の「トピック」を設定(複数設定可)します。
ここで設定する「AWS IoT」の接続情報の設定や「エンドポイント」の確認方法は以下のリンクで詳しく紹介しています。
サブスクライブ(受信)方法
MQTT通信でデータをサブスクライブ(受信)するための方法を抜粋したものは以下になります。
#include <ArduinoJson.h> // ① JSONライブラリ
// ② インスタンス
StaticJsonDocument<200> doc; // JSONファイル変換用
// ③ 変数宣言
String temp = "0"; // 温度データ文字列格納用
String hum = "0"; // 湿度データ文字列格納用
// ④ AWS IoTサブスクライブ(受信)処理関数 ************************************
void subscribe(char* topic, byte* payload, unsigned int length) {
Serial.println(topic); // 受信したtopicを表示
deserializeJson(doc, payload, length); // 受信したjsonデータをdoc(StaticJsonDocument)に変換
String val1 = doc["temp"]; // docのキー"temp"の値を取り出す
String val2 = doc["hum"]; // docのキー"hum"の値を取り出す
temp = val1;
hum = val2;
Serial.printf("%s, %s\n", temp, hum);
}
// ⑤ メイン *****************************************
void loop() {
mqtt_client.loop(); // サブスクライブ(受信)するために必要
delay(200); // 遅延時間
}
サブスクライブ(受信)したデータの処理方法は以下の手順になります。
①ライブラリの準備
JSONデータを扱うために必要なライブラリとして「ArduinoJson」をインクルードします。
②インスタンスの準備
ArduinoJsonを使用してJSONファイルを扱えるようにインスタンスを準備します。
③変数宣言
受信したデータを文字列として格納するための変数を準備します。
④サブスクライブ(受信)したデータの処理関数作成
設定したトピックごとに、データが送信されてきた時の処理を関数として作成しておきます。
ここではJSON形式で受信したデータの「key(キー)」ごとに「value(値)」を抽出して文字列に変換してシリアル出力しています。
⑤メイン処理内にサブスクライブ(受信)状態で待ち受けるよう設定
メイン処理内に「mqtt_client.loop();」を記入することで、サブスクライブするたびに④の関数を呼び出せるようになります。
パブリッシュ(送信)方法
MQTT通信でデータをパブリッシュ(送信)するための方法を抜粋したものは以下になります。
// ① AWS IoTパブリッシュ(送信)処理関数
void publish() {
// ② 送信データを文字列として準備
dtostrf(s_tmp, 4, 1, temp); // 温度データを文字列に変換(値, 文字桁数, 小数点桁数, 格納先文字配列)
dtostrf(s_hum, 4, 1, hum); // 湿度データを文字列に変換
// ③ 送信データをJSON形式に変換
char json_str[100]; // 送信するjsonデータ格納用文字列
doc.clear(); // JSONデータをクリア
doc["temp"] = temp; // 温度データをjsonデータのキーと値(バリュー)でセット
doc["hum"] = hum; // 温度データをjsonデータのキーと値(バリュー)でセット
serializeJson(doc, json_str); // doc(StaticJsonDocument)をjsonデータに変換
// ④ トピックを指定して送信
mqtt_client.publish("topic1", json_str); // jsonデータをトピック"topic1"に送信
Serial.println(json_str); // JSONデータ確認用に表示
}
データをパブリッシュ(送信)する方法は、以下の手順になります。
①パブリッシュ(送信)するデータの処理関数作成
設定したトピックごとに送信するデータの処理を関数として作成しておきます。
②送信するデータを文字列に変換
データをJSON形式に変換するためにまず文字列として準備します。
浮動小数点数の桁数等を指定するにはArduinoの独自関数である「dtostrf」が便利です。
③JSON形式に変換
送信するデータを「ArduinoJson」ライブラリでkey(キー)」と「value(値)」を設定してJSON形式に変換します。
④トピックを設定して送信
JSONデータを「トピック」を指定してパブリッシュ(送信)します。
・セキュリティ証明書参照ファイルの準備
MQTTで安全な「SSL通信」を行うための「クライアント証明書」と「秘密鍵」は別ファイルに文字列として準備して読み込ませます。
メインのプログラムに以下のように「extern」をつけて文字列変数を準備することで、別ファイルを参照して証明書等の情報を読み込ませることができます。
// 外部ファイル(certs.cpp)から認証情報を取得
extern const char *root_ca; // ルート証明書
extern const char *certificate; // クライアント証明書
extern const char *private_key; // プライベートキー
外部ファイルとして、メインのプログラムファイルと同じ場所に「certs.cpp」というファイルを作成して以下のように証明情報を文字列で記入します。
証明情報は証明書等の各ファイルをメモ帳等で開いて内容をコピーし、以下の「xxxxxxxx」の部分と置き換えて作成してください。
const char *root_ca = R"(-----BEGIN CERTIFICATE-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END CERTIFICATE-----
)";
const char *certificate = R"(-----BEGIN CERTIFICATE-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END CERTIFICATE-----
)";
const char *private_key = R"(-----BEGIN RSA PRIVATE KEY-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END RSA PRIVATE KEY-----
)";
・ホスト(親機)側サンプルプログラム
親機として使用する「CORE2」のサンプルプログラムの全ては以下のようになります。
※下コード(黒枠)内の右上角にある小さなアイコンのクリックでコピーできます。
#include <M5Core2.h>
#include <ArduinoJson.h> // JSONライブラリ
#include <WiFiClient.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h> // MQTTライブラリ
#define WIFI_SSID "xxxxxxxxxxxx" // WiFi接続SSID(自分の環境に合わせてください)
#define WIFI_PASSWORD "xxxxxxxxxxxxx" // WiFi接続パスワード(自分の環境に合わせてください)
#define DEVICE_NAME "core2_01" // デバイス名
#define AWS_IOT_ENDPOINT "xxxxxxxxxxxx.amazonaws.com" // AWSエンドポイント(接続先アドレス)を設定
#define AWS_IOT_PORT 8883 // AWS接続ポート番号(固定)
#define QOS 0 // 通信品質設定(0:送信完了の確認をしない,高速)
// 外部ファイル(certs.cpp)から認証情報を取得
extern const char *root_ca; // ルート証明書
extern const char *certificate; // クライアント証明書
extern const char *private_key; // プライベートキー
// インスタンス
WiFiClientSecure https_client; // https通信用
PubSubClient mqtt_client(https_client); // MQTT通信用
StaticJsonDocument<200> doc; // JSONファイル変換用
// 変数宣言
String temp = "0"; // 温度データ文字列格納用
String hum = "0"; // 湿度データ文字列格納用
String press = "0"; // 気圧データ文字列格納用
// 関数 *****************************************
// 液晶タイトル表示
void lcdTitle(uint32_t color) {
M5.Lcd.fillScreen(color); // 背景色
M5.Lcd.setTextFont(4); // フォント 1(8px), 2(16px), 4(26px)
// 6(36px数字-.:apm), 7(7セグ-.:), 8(75px数字-.:)
M5.Lcd.drawFastHLine(0, 0, 320, WHITE); // 指定座標から平行線
M5.Lcd.fillRect(0, 1, 320, 27, (uint16_t)0x098a); // タイトルエリア背景
M5.Lcd.drawFastHLine(0, 29, 320, WHITE); // 指定座標から平行線
M5.Lcd.setTextColor(WHITE); // 文字色
M5.Lcd.drawCentreString("AWS IoT MQTT HOST", 160, 4, 4); // 指定座標を中心に文字表示(x, y, font)
}
// WiFi接続
void connect_wifi() {
M5.Lcd.setTextFont(4); // フォント設定
M5.Lcd.println("WiFi Connecting to ");
M5.Lcd.println(WIFI_SSID); // SSID表示
WiFi.begin(WIFI_SSID, WIFI_PASSWORD); // Wi-Fi接続開始
while (WiFi.status() != WL_CONNECTED) { // Wi-Fi接続完了するまでループ
M5.Lcd.print(".");
delay(2000);
}
M5.Lcd.println("Connected");
M5.Lcd.printf("IP: %s\n\n", WiFi.localIP().toString().c_str()); // IPアドレス表示
}
// AWS IoTパブリッシュ(送信)関数
void publish() {
char json_string[100]; // 送信するjsonデータ格納用文字列
doc.clear();
doc[""] = ""; // 空のjsonデータのキーと値(バリュー)
serializeJson(doc, json_string); // doc(StaticJsonDocument)をjsonデータに変換
mqtt_client.publish("device/01/local", json_string); // jsonデータをトピック"device/01/local"に送信
}
// AWS IoTサブスクライブ(受信)した時のJsonを処理して表示
void subscribe(char* topic, byte* payload, unsigned int length) {
Serial.println(topic); // 受信したtopicを表示
deserializeJson(doc, payload, length); // 受信したjsonデータをdoc(StaticJsonDocument)に変換
String val1 = doc["temp"]; // docのキー"temp"の値を取り出す
String val2 = doc["hum"]; // docのキー"hum"の値を取り出す
String val3 = doc["press"]; // docのキー"press"の値を取り出す
temp = val1;
hum = val2;
press = val3;
Serial.printf("%s, %s, %s\n", temp, hum, press);
lcdTitle(DARKCYAN); // 液晶表示タイトル表示関数(背景色渡し)
delay(500);
lcdTitle(BLACK); // 液晶表示タイトル表示関数(背景色渡し)
}
// AWS IoT初期設定関数
void connect_aws(){
https_client.setCACert(root_ca); // ルート証明書を設定
https_client.setCertificate(certificate); // クライアント証明書を設定
https_client.setPrivateKey(private_key); // プライベートキーを設定
mqtt_client.setServer(AWS_IOT_ENDPOINT, AWS_IOT_PORT); // エンドポイントとポート番号設定
mqtt_client.setCallback(subscribe); // サブスクライブ(受信)した時に呼び出す関数設定
// MQTT接続開始
M5.Lcd.println("MQTT Connection");
while (!mqtt_client.connected()) { // MQTT接続完了までループ
M5.Lcd.print(".");
delay(1000);
if (mqtt_client.connect(DEVICE_NAME)) { // DEVICE_NAMEに接続完了なら
M5.Lcd.println("Connected!!");
mqtt_client.subscribe("device/host", QOS); // サブスクライブ(受信)するトピックを設定
// mqtt_client.subscribe("topic", QOS); // トピックが複数ある場合は続けて設定
delay(1000); // 接続完了表示時間
} else { // 接続失敗なら
M5.Lcd.printf("Failed!!, rc=%d\n", mqtt_client.state());
}
}
}
// 初期設定 *****************************************
void setup() {
M5.begin(); // 本体初期化
Serial.begin(9600); // シリアル通信開始
connect_wifi(); // Wi-Fi接続関数呼び出し
connect_aws(); // AWS IoT初期設定関数呼び出し
lcdTitle(BLACK); // 液晶表示タイトル表示関数(背景色渡し)
}
// メイン *****************************************
void loop() {
M5.update(); // ボタン状態更新
mqtt_client.loop(); // サブスクライブ(受信)するために必要
// 本体ボタン処理
if (M5.BtnA.wasPressed()) {
Serial.println("BTN_A ON!"); // 標準のシリアル出力
lcdTitle(DARKCYAN); // 液晶表示タイトル表示関数(背景色渡し)
publish(); // publish処理関数呼び出し
}
// 取得データ表示
M5.Lcd.setTextFont(4); // フォント
M5.Lcd.setCursor(10, 50); M5.Lcd.setTextColor(WHITE, BLACK); // 座標指定(x, y) // 文字色、背景
M5.Lcd.printf("Temp : %s C'", temp); // 温度表示
M5.Lcd.setCursor(10, 110); // 座標指定(x, y)
M5.Lcd.printf("Hum : %s %%", hum); // 湿度表示
M5.Lcd.setCursor(10, 170); // 座標指定(x, y)
M5.Lcd.printf("Press : %s hPa", press); // 湿度表示
delay(200); // 遅延時間
}
・ローカル(子機)側サンプルプログラム
子機として使用する「M5StickC Plus」のサンプルプログラムの全ては以下のようになります。
※下コード(黒枠)内の右上角にある小さなアイコンのクリックでコピーできます。
#include <M5StickCPlus.h> // ヘッダーファイル
#include <ArduinoJson.h> // JSONライブラリ
#include <WiFiClient.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h> // MQTTライブラリ
#include "M5_ENV.h" // ENVⅢ用ライブラリ
// MQTT通信設定
#define WIFI_SSID "xxxxxxxxxxxx" // WiFi接続SSID(自分の環境に合わせてください)
#define WIFI_PASSWORD "xxxxxxxxxxxxx" // WiFi接続パスワード(自分の環境に合わせてください)
#define DEVICE_NAME "m5stickc_plus_01" // デバイス名
#define AWS_IOT_ENDPOINT "xxxxxxxxxxx.amazonaws.com" // AWSエンドポイント(接続先アドレス)を設定
#define AWS_IOT_PORT 8883 // AWS接続ポート番号(固定)
#define QOS 0 // 通信品質設定(0:送信完了の確認をしない,高速)
// 外部ファイル(certs.cpp)から認証情報を取得
extern const char *root_ca; // ルート証明書
extern const char *certificate; // クライアント証明書
extern const char *private_key; // プライベートキー
// インスタンス
WiFiClientSecure https_client; // https通信用
PubSubClient mqtt_client(https_client); // MQTT通信用
StaticJsonDocument<200> doc; // JSONファイル変換用
// 環境センサENVⅢ設定
SHT3X sht30; // 温湿度センサ インスタンス化
QMP6988 qmp6988; // 気圧センサ インスタンス化
float s_tmp = 0.0; // 温度測定データ格納用
float s_hum = 0.0; // 湿度測定データ格納用
float s_press = 0.0; // 気圧測定データ格納用
// 変数宣言
char temp[20]; // 温度データ文字列格納用
char hum[20]; // 湿度データ文字列格納用
char press[20]; // 気圧データ文字列格納用
// 関数 *****************************************
// WiFi接続
void connect_wifi() {
M5.Lcd.setTextFont(2); // テキストフォント指定
M5.Lcd.println("WiFi Connecting to ");
M5.Lcd.println(WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD); // Wi-Fi接続開始
while (WiFi.status() != WL_CONNECTED) { // Wi-Fi接続完了するまでループ
M5.Lcd.print(".");
delay(2000);
}
M5.Lcd.println("Connected");
M5.Lcd.printf("IP: %s\n\n", WiFi.localIP().toString().c_str()); // IPアドレス表示
}
// AWS IoTパブリッシュ(送信)関数
void publish() {
dtostrf(s_tmp, 4, 1, temp); // 温度データを文字列に変換(値, 文字桁数, 小数点桁数, 格納先文字配列)
dtostrf(s_hum, 4, 1, hum); // 湿度データを文字列に変換
s_press = s_press / 100; // PaをhPaに変換
dtostrf(s_press, 6, 1, press); // 気圧データを文字列に変換
// JSONパブリッシュ処理
char json_str[100]; // 送信するjsonデータ格納用文字列
doc.clear(); // JSONデータをクリア
doc["temp"] = temp; // 温度データをjsonデータのキーと値(バリュー)でセット
doc["hum"] = hum; // 温度データをjsonデータのキーと値(バリュー)でセット
doc["press"] = press; // 温度データをjsonデータのキーと値(バリュー)でセット
serializeJson(doc, json_str); // doc(StaticJsonDocument)をjsonデータに変換
mqtt_client.publish("device/host", json_str); // jsonデータをトピック"device/01/local"に送信
Serial.println(json_str); // JSONデータを表示
delay(500);
digitalWrite(10, HIGH); // 本体LED赤消灯
}
// AWS IoTサブスクライブ(受信)した時のJsonを処理して表示
void subscribe(char* topic, byte* payload, unsigned int length) {
digitalWrite(10, LOW); // 本体LED赤点灯
Serial.println(topic); // 受信したtopicを表示
deserializeJson(doc, payload, length); // 受信したjsonデータをdoc(StaticJsonDocument)に変換
Serial.printf("%s\n", payload); // 受信データを表示
publish(); // publish処理関数呼び出し
}
// AWS IoT初期設定関数
void connect_aws(){
https_client.setCACert(root_ca); // ルート証明書を設定
https_client.setCertificate(certificate); // クライアント証明書を設定
https_client.setPrivateKey(private_key); // プライベートキーを設定
mqtt_client.setServer(AWS_IOT_ENDPOINT, AWS_IOT_PORT); // エンドポイントとポート番号設定
mqtt_client.setCallback(subscribe); // サブスクライブ(受信)した時に呼び出す関数設定
M5.Lcd.println("MQTT Connection");
while (!mqtt_client.connected()) { // MQTT接続完了までループ
M5.Lcd.print(".");
delay(1000);
if (mqtt_client.connect(DEVICE_NAME)) { // DEVICE_NAMEに接続完了なら
M5.Lcd.println("Connected!!");
mqtt_client.subscribe("device/01/local", QOS); // サブスクライブ(受信)するトピックを設定
// mqtt_client.subscribe("topic", QOS); // トピックが複数ある場合は続けて設定
delay(1000); // 接続完了表示時間
} else { // 接続失敗なら
M5.Lcd.printf("Failed!!, rc=%d\n", mqtt_client.state());
}
}
}
// 初期設定 *****************************************
void setup() {
M5.begin(); // 本体初期化
Serial.begin(9600); // シリアル通信開始
Wire.begin(32, 33); // Wire init, adding the I2C bus. Wire初始化, 加入i2c总线
qmp6988.init(); // 気圧センサの初期化
M5.Lcd.setRotation(3); // 画面表示回転
connect_wifi(); // Wi-Fi接続関数呼び出し
connect_aws(); // AWS IoT初期設定関数呼び出し
pinMode(10, OUTPUT); //本体LED赤
digitalWrite(10, HIGH); //本体LED初期値OFF(HIGH)
//LCD表示
M5.Lcd.fillScreen(BLACK); // 背景色
M5.Lcd.setTextFont(2); // テキストフォント指定
M5.Lcd.drawFastHLine(0, 0, 240, WHITE); // 指定座標から平行線
M5.Lcd.fillRect(0, 1, 240, 17, (uint16_t)0x098a); // タイトルエリア背景
M5.Lcd.drawFastHLine(0, 19, 240, WHITE); // 指定座標から平行線
M5.Lcd.setTextColor(WHITE); // 文字色
M5.Lcd.drawCentreString("AWS IoT MQTT LOCAL ENV3", 120, 2, 2); // 指定座標を中心に文字表示(x, y, font)
}
// メイン *****************************************
void loop() {
M5.update(); // ボタン状態更新
mqtt_client.loop(); // サブスクライブ(受信)するために必要
// 環境センサENVⅢ測定データ取得
if (sht30.get() == 0) { // 温湿度センサからデータ取得したら
s_tmp = sht30.cTemp; // 温度データを取得
s_hum = sht30.humidity; // 湿度データを取得
} else { // データが取得できなければ
s_tmp = 0, s_hum = 0; // 温湿度を0セット
}
s_press = qmp6988.calcPressure(); // 気圧データ取得
// 本体ボタン処理
if (M5.BtnA.wasPressed()) {
digitalWrite(10, LOW); // 本体LED赤点灯
Serial.println("BTN_A ON!"); // 標準のシリアル出力
publish(); // publish処理関数呼び出し
}
// アナログ入出力電圧(アナログ入出力値)表示
M5.Lcd.setTextFont(4); // フォント設定
M5.Lcd.setCursor(10, 32); M5.Lcd.setTextColor(WHITE, BLACK); // 座標指定(x, y) // 文字色、背景
M5.Lcd.printf("Temp : %2.1f C'", s_tmp); // 温度表示
M5.Lcd.setCursor(10, 64); // 座標指定(x, y)
M5.Lcd.printf("Hum : %2.1f %%", s_hum); // 湿度表示
M5.Lcd.setCursor(10, 96); // 座標指定(x, y)
M5.Lcd.printf("Press : %2.1f hPa", s_press/100); // 気圧表示
delay(200); // 遅延時間
}
・動作確認
実際に動作確認してみましょう。
MQTTの通信データ確認には「AWS IoT」の「MQTTテストクライアント」を使用します。
「MQTTテストクライアント」は下画像のように「AWS IoT」のページからアクセスできます。
「MQTTクライアント」の使用方法は以下のリンクで詳しく紹介しています。
サンプルプログラムを書き込んだ2つのデバイスを並べて「AWS」のクラウドサーバー経由で動作確認を行います。
画像の左側が親機の「CORE2」、右側が子機の「M5StickC Plus」に環境センサ「ENVⅢ」を接続したものです。
実際に動作確認をしたものは下画像のようになります。
親機「CORE2」の電源を入れると、上画像のようにWi-Fi接続が開始されます。
Wi-Fi接続が完了するとAWSとのMQTT接続が開始され、接続完了で次の画面に切り換わります。
子機「M5StickC Plus」も同様に電源を入れるとWi-Fi接続からMQTT接続が開始され、接続完了で次の画面に切り換わります。
通信接続が完了すると上画像のようになり「M5StickC Plus」側には環境センサ「ENVⅢ」の測定データが表示されており「CORE2」にはまだデータは表示されていません。
「CORE2」のボタンAを押すと、トピック[device/01/local]に空のJOSNデータ「{“”:””}」をパブリッシュ(送信)します。
「CORE2」の画面が青色になり、画像ではわかりにくいですが「M5StickC」本体の赤色LEDが点灯します。
「M5StickC」はトピック[device/01/local]へのサブスクライブ(受信)によって、センサのデータを「CORE2」用のトピック[device/host]にJSON形式でパブリッシュ(送信)します。
「CORE2」がデータを受信すると、画面にデータを表示し、画面が青から黒色に変わります。
ここまでの動作をAWS IoTの「MQTTテストクライアント」で確認すると下画像のようになります。
トピック[device/01/local]のサブスクライブを受けて[device/host]へ測定データがパブリッシュされているのが確認できます。(下画像のJSONデータの内容は上写真とは異なります。)
「M5StickC」のボタンAを押すと「CORE2」のトピック[device/host]へ測定データをJSON形式でパブリッシュ(送信)します。
「CORE2」のトピック[device/host]へのサブスクライブ(受信)によって「CORE2」では受信したJSONデータを処理して画面に表示します。
ここまでの動作をAWS Iotの「MQTTテストクライアント」で確認すると下画像のようになります。
トピック[device/host]へ測定データがパブリッシュされているのが確認できます。
(下画像のJSONデータの内容は上写真とは異なります。)
5.まとめ
M5Stack社の「CORE2」と「M5StickC Plus」に「ENVⅢ」を接続して「C言語」ベースの「Arduinoコマンド」で「AWS IoT」を使用したクラウド経由の遠隔操作、データ監視を行う方法を紹介しました。
「AWS IoT」を使用して、デバイスの登録を行うことで、個別の「セキュリティ証明書」と「秘密鍵」を作成することができ、クラウド経由での安全なデータ通信を行うことができます。
通信にはデータパケットの小さい「MQTT」通信で行い、設定した「トピック」宛に「パブリッシュ(送信)ー サブスクライブ(受信)」パターンを使用して、中継サーバーのブローカー(Broker)経由で行われます。
「C言語」ベースの「Arduinoコマンド」を使用することで、より自由度の高いプログラムが組めるため、MQTT通信を使用したクラウド経由の遠隔操作、データ監視環境を思い通りに構築することができます。
今回はシンプルな相互データ通信の方法を紹介しましたが、これができれば複雑な遠隔操作やデータ監視もできることになります。サーバーにデータを蓄積して管理、解析等行う方法は「AWS」で提供(制限付き無償期間あり)されているので、ここでも無償で利用できる範囲で色々紹介していければと思います。
コメント
参考にさせていただいています。
一つ質問です。
certs.cpp
の
const char *root_ca
const char *certificate
const char *private_key
には、それぞれ
*-certificate.pem.crt
*-private.pem.key
*-public.pem.key
のどの内容をコピーしたら良いのでしょうか?
root_ca, certificateが
—–BEGIN CERTIFICATE—–
となっていて、certificate に*-certificate.pem.crt を入れたら良いと考えていますが、そうなるとroot_caには何をコピーしたら良いでしょうか?
https://logikara.blog/aws-iot-setup/#mokuji_4
に「ルート証明書は必要に応じて後からでもダウンロードできる」と記述されているこのルート証明書のことでしょうか?
これはどこからダウンロードできますか?
アドバイスいただけると助かります。
こんにちは。
const char *root_ca
const char *certificate
const char *private_key
には、それぞれ
root-CA.crt
*-certificate.pem.crt
*-private.pem.key
の内容をコピーします。
*-public.pem.key は使用しません。
「root-CA.crt」を入手されていないように思いますので、以下の手順でダウンロードして設定してみてください。
1.AWSコンソールにサインインします。
2.AWS IoTのダッシュボードに移動します。
3.左側のメニューから「セキュリティ」をクリックします。
4.「証明書」をクリックします。
5.[証明書を作成]ボタンをクリックして「証明書を作成」を選択します。
6.「証明書のステータス」は「非アクティブ」で良いので、[作成]ボタンをクリックします。
7.ダウンロードできる証明書の一覧のウインドウが表示されるので下の方の「ルート CA 証明書(AmazonルートCA 1)」をダウンロードします。
8.ダウンロードしたファイルをメモ帳等で開いて「const char *root_ca」にコピペします。
今作成した証明書は不要なので、チェックボックスにチェックを入れて「アクション」のドロップダウンリストから削除(アクティブだと削除できません)を選択して削除してください。
「ルート証明書」は共通なのでデバイスが変わっても毎回同じものを設定すれば良いです。
これでうまくいくと思いますのでお試しください♪