5-2.屋外からの通信(プッシュ通知)
屋外からの通信(プッシュ通知)概要
IoT端末をクラウドに接続する通信プロトコルはMQTT、HTTP、WebSocket などいくつか利用されていますが、今回はMQTTを用いてクラウドに接続する電子工作を行います。
MQTTでクラウドに常時接続することで屋外からの通信を可能にします。
利用するクラウドについては無料で一定量まで利用できMQTT機能だけでなくHTTPでもアクセスできるREST-API機能を有している「Beebotte」を利用します。REST-API機能はHTTP通信で容易に連携できるため多くのシステムで用いられています。また、Beebotteは1日に50,000メッセージを無料で利用できますので、学習などの利用なら問題なく利用できます。構成イメージを以下に示します。
クラウド(Beebotte)利用設定
Beebotteは以下のURLからアクセスできます。
「Beebotte」 https://beebotte.com
MQTT利用時もTLSで暗号化されていますので一定のセキュリティは確保されています。ただ、トークン自体は変更されないので、定期的に変更するのが望ましいです。利用方法について次に説明していきます。
(1)Beebotteホーム(https://beebotte.com)から「SignUp」をクリック
(2)アカウント作成画面がなりますので、Username/E-mail/Passwordを入力し「SIGN UP」をクリックしアカウントを作成します。
(3)登録したメールアドレス宛に確認メールが送付されてきますのでクリックして確認します。
(4)Beebotteホーム(https://beebotte.com)でログインし、新しいChannelを登録するため、「CreateNew」をクリックします。
(5)登録するChannelデータを入力します。入力する文字列は任意ですが、MQTT端末からTOPICに利用しますので、スケッチでの設定が必要となります。
今回は以下の登録を行いますが、任意の文字列で問題ありません。
・Channel Name : TestChannel
・Channel Description : This is MQTT Test Channel
・Resource Name : resource1
上記の通り入力し、「Create channel」をクリックし作成します。
(6)Channelが登録されるとトークン(赤文字部分)が作成されますので取得しておきます。
このトークンは変更されませんので、永遠に使い続けることが可能です。トークンが漏洩するとアクセスされてしまう可能性があるため、定期的にChannelを作り直しトークンも変更して運用することが望ましいです。
以上で、クラウド側の設定は完了となります。
電気回路と電子工作
今回は、MQTTでクラウドに接続するだけのため、LEDのみ接続して状態を表示します。(回路は2-1と同じです。)
【ESP32-DevKitCの入出力端子はこちらを参照下さい】
スケッチ(制御ソフトウェア)
今回、スケッチはCA証明書を設定する必要があり設定が多くなるので設定を行う”config.h”ファイルとプログラム”9_1_mqtt.ino”ファイルに分けて作成しています。
(1) config.h(設定ファイル)
スケッチで利用するグローバル変数について設定しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// ①ピン配置など const byte LED_PIN = 25; // LED緑 const char* host = "mqtt.beebotte.com"; bool ledFlag = true; // LED制御フラグ // ②Wi-Fi設定 const char *ssid = "##### SSID #####"; const char *password = "### PASSWORD ###"; IPAddress ip(192, 168, 1, 123); // IPアドレス(本機が利用するIP) IPAddress gateway(192, 168, 1, 1); // デフォルトゲートウェイ IPAddress subnet(255, 255, 255, 0); // サブネットマスク IPAddress DNS(192, 168, 1, 1); // DNSサーバ // ③BeebotteクライアントID(任意) const char *clientID = "esp32_001"; // ④Beebotteチャンネルトークン const char* channelToken = "### TOKEN ###"; // ⑤Beebotteトピック名("channel/resource"の形式) const char* topic = "TestChannel/resource1"; // ⑥Beebotte CA証明書(https://beebotte.com/certs/mqtt.beebotte.com.pem) const char* beebottle_ca_cert = \ "-----BEGIN CERTIFICATE-----\n" \ "MIIFWDCCBECgAwIBAgIQc+iqrz6j/mo1ROEggN1a9TANBgkqhkiG9w0BAQsFADCB\n" \ (省略) "mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=\n" \ "-----END CERTIFICATE-----\n" \ ; |
設定ファイル内のssid,passwordは今まで学習した内容と同様に接続するWi-Fiの値を設定してください。
clientIDは今回”esp32_001”を設定していますが、自由に任意の文字列を登録しても問題ありません。ただし、利用する端末で一意になるように任意のワードを設定して下さい。
channelTokenは最初にBeebotteに設定した値に変更して下さい。また、topicもBeebotteに設定した「Channel Name/ Resource Name」になっていることを確認して下さい。変更した場合は書き換えて設定する必要があります。Channel NameはTestChannel、Resource Nameはresource1を設定した場合、topicは「TestChannel/resource1」となります。
(2)5_2_mqtt.ino(スケッチファイル)
電子工作を制御するスケッチを以下に示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
// IoT電子工作キット // 5-2.屋外からの通信(プッシュ通知) // ①ライブラリ、Configファイルを読み込み #include <WiFiClientSecure.h> #include <PubSubClient.h> #include <ArduinoJson.h> #include "config.h" // ②利用ライブラリの設定を変更(PubSubClientライブラリは128バイトなのを拡張) #define MQTT_MAX_PACKET_SIZE 1024 // ③Wi-Fi、MQTTクライアントを設定 WiFiClientSecure wifiClient; PubSubClient mqttClient(host, 8883, wifiClient); void setup(void) { // ④Serial設定 Serial.begin(115200); Serial.println ( ); // ⑤LED設定 pinMode ( LED_PIN, OUTPUT ); } // ⑥無線Wi-Fi設定 void setup_wifi() { // ⑦無線Wi-Fi情報設定 Serial.println ( "Wi-Fi SetUp" ); WiFi.config( ip, gateway, subnet, DNS ); // DNSがないとMQTT接続不可 WiFi.begin ( ssid, password ); // ⑧Wi-Fi接続処理(接続するまで無限ループ) while ( WiFi.waitForConnectResult() != WL_CONNECTED ) { // ⑨LEDを1秒毎に点滅する ledFlag = !ledFlag; digitalWrite(LED_PIN, ledFlag); // ⑩1秒間Wait delay ( 1000 ); Serial.print ( "." ); } // ⑪Wi-Fi接続できたのでシリアルモニターにIPアドレス表示 Serial.print ( "Wi-Fi Connected! IP address: " ); Serial.println ( WiFi.localIP() ); // ⑫LED点灯(Wi-Fi接続状態) digitalWrite ( LED_PIN, true ); // ⑬クライアントにCA証明書を設定 wifiClient.setCACert(beebottle_ca_cert); } void loop(void){ // ⑭Wi-Fi状態を確認し接続状態でなかったらWi-Fi接続処理 if (WiFi.status() != WL_CONNECTED) { setup_wifi(); } // ⑮MQTT状態を確認し接続状態でなかったらMQTT接続処理 if (!mqttClient.connected()) { reconnect(); } // ⑯MQTTクライアント処理 mqttClient.loop(); } // ⑰MQTT接続処理 void reconnect() { // ⑱MQTT接続状態までループ処理 while (!mqttClient.connected()) { Serial.println("Attempting MQTT connection..."); // ⑲MQTT設定情報定義 String username = "token:"; username += channelToken; // ⑳MQTT接続処理 mqttClient.connect(clientID, username.c_str(), NULL); delay(2000); } Serial.println("MQTT connected"); // ㉑MQTTメッセージ受信時の処理設定 mqttClient.setCallback(callback); // ㉒MQTTメッセージを受信するTOPICの設定 mqttClient.subscribe(topic); } // ㉓MQTTメッセージ受信時の処理 void callback(char* topic, byte* payload, unsigned int length) { // ㉔MQTT受信メッセージを変数に保存 char recvData[MQTT_MAX_PACKET_SIZE]; snprintf(recvData, sizeof(recvData), "%s", payload); // ㉕MQTT受信メッセージをシリアルモニタ表示 Serial.print("Message arrived ["); Serial.print(topic); Serial.println("] "); Serial.println(recvData); // ㉖受信データのJSON形式を解析し変数へ保存 StaticJsonBuffer<MQTT_MAX_PACKET_SIZE> jsonBuffer; JsonObject& jsonBuf = jsonBuffer.parseObject(recvData); // ㉗JSON形式解析が成功しなかったらエラー表示し終了 if (!jsonBuf.success()) { Serial.println("parseObject() failed"); return; } // ㉘受信データ(data)を取得保存 const char* parsedPayload = jsonBuf["data"]; // ㉙受信データ(data)が存在するか判定 if (parsedPayload != NULL) { Serial.print("payload: "); Serial.println(parsedPayload); // ㉚受信データ(data)が"led_on"の場合にLEDをON if (strcmp(parsedPayload, "led_on") == 0) { digitalWrite(LED_PIN, HIGH); // ㉛受信データ(data)が"led_off"の場合にLEDをOFF } else if (strcmp(parsedPayload, "led_off") == 0) { digitalWrite(LED_PIN, LOW); } } } |
①で以下のライブラリを読み込んでいます。
(1)TLS利用のため「WiFiClientSecure.h」
(2)MQTT利用のため「PubSubClient.h」
(3)JSONデータ解析のため「ArduinoJson.h」
(4)別ファイルで定義した変数を読み込むため「config.h」
(1)についてはHTTPS通信のためにTLSで暗号化するために利用します。
(2)はMQTTを使えるようにするために利用します。PubSubClientライブラリはBeebotteに接続するだけではなく一般的なMQTTプロトコルを利用するためのライブラリですので、他のクラウドでMQTT接続したい場合でも利用可能です。
(3)はJSONデータ解析のために利用します。ただし、ArduinoIDEのライブラリ管理から「ArduinoJson by Benolt Blanchon 5.13.5」のバージョンをインストールして下さい。6.x以降のバージョンでは正常に動作しない場合があります。
(4)は別ファイルで定義した「config.h」を読み込んで利用できるようにしています。
(また、includeは””で括られると同じディレクトリを優先し、カッコの場合は標準ディレクトリを優先して探しますので、config.hのみ””で定義しています。)
②はPubSubClientクライアントライブラリで利用するパケットサイズが128Byteと小さいために個別にMQTT_MAX_PACKET_SIZEを1024Byteに拡張しています。この設定によりMQTTで利用できるパケットサイズが最大1024Byteまで利用可能となります。
③でWi-FiとMQTTで利用する変数を定義しています。⑫までのWi-Fi接続までは今までのプログラムと基本的に同様ですが、⑬でTLSを利用するためにCA証明書を設定しています。
loop内の処理として⑭はWi-Fi接続状態を確認し接続されていない場合に再接続する処理を定義しています。⑮も同様にMQTTが接続状態でない場合は再接続する処理を記載し、⑯でMQTTの受信がないか(CallBack処理)を確認しています。
MQTTの接続処理について⑰のreconnect関数で処理しています。⑱接続状態でない場合はWhile内を処理し、⑲でトークン文字列を作成し⑳でMQTTの接続処理を行なっています。⑱は接続するまでループしていますので接続するまで2秒待って(delay)トライするようになっています。
㉑では受信があった場合にcallback関数を処理するように設定しています。また、㉒ではMQTTのどのTopicを受信(MQTTではSubscribeと言っています)するかを設定しています。
受信があった場合は㉑で設定したようにcallback関数を処理します。callback関数は㉔で受信データを取得し、㉖でJSONデータを扱えるように変数に変換しています。これは{“id1”:”data1”, “id2”:”data2”}のようなデータがあった場合に、何文字目から何文字目までを取り出すというような面倒なことをしなくても良いようにjsonBuf[“id1”]は”data1”を表すようにし容易に扱えるようにしています。
㉖でJSONデータの”data”を取得しています。取得した内容によりLEDを制御するプログラムとなっており、”led_on”の場合は㉚でLEDを点灯させ”led_off”の場合は㉛でLEDを消灯しています。
動作確認
今回、利用するBeebotteはREST-APIも実装していますので、その機能を利用して端末にデータを送信して動作を確認します。REST-APIについては端的に説明するとHTTP通信を利用して外部からプログラムを呼び出すAPI(アプリケーションプログラミングインターフェース)ということになります。
Beebotteを用いた動作確認を行う構成図を以下に示し具体的に動作確認を通して説明します。
WindowsやMac端末の標準で利用できるREST-API(HTTP通信)を利用して確認していきます。
BeebotteのREST-APIは以下の内容で確認します。Beebotte は送信データに変数名にdataを指定することでIoT端末へ変数名dataを送信できます。
・HTTPヘッダ : Content-Type: application/json
・HTTPメソッド : POST
・Channel Name : TestChannel
・Resource Name : resource1
・送信データ : “data”:”led_on”
各端末でのREST-APIでの確認方法について説明していきます。
(1)Windows10端末の場合
[Windows]+[R]キーを押し[名前]に「cmd」と入力しOKをクリックし、コマンドプロンプトを表示し、以下のコマンドを入力し実行します。
1 |
curl https://api.beebotte.com/v1/data/publish/TestChannel/resource1?token=###TOKEN### -H "Content-Type: application/json" -d "{\"data\":\"led_on\"}" -X POST |
コマンド実行後は次行に「true」が表示されます。trueが表示されれば実行が成功(正常に完了)しています。
IoT端末のシリアルモニタには以下のように表示されます。
受信データは{}で囲まれたデータになります。変数名dataは確認端末の内容がそのままIoT端末で受信できます。そのため、送信したデータ内容に応じてIoT端末で動作する内容を変更できます。今回はスケッチでled_onの場合はLEDを点灯するようにしていますのでdataの値をled_on/led_offに変更することでLED制御できることを確認して下さい。
(2)Mac/Linux端末の場合
Mac端末でも同様に確認できます。Terminalを立ち上げて以下のコマンドを実行し同様に動作を確認して下さい。
1 |
curl 'https://api.beebotte.com/v1/data/publish/TestChannel/resource1?token=###TOKEN###' -d '{"data":"led_on"}' -H "Content-Type: application/json" -X POST |
実行時には同様にtrueが返答されます。そのため、trueを確認することで動作が正常に完了しているかを確認できます。
Terminalの画面としては以下のようになります。(trueの後に改行されないので少し確認しずらいですが、行頭に記載されますので確認下さい)
本コマンドを利用し、dataの値をled_on/led_offに変更することでLED制御できることを確認して下さい。