ESP32でJSONを扱うためメモ書きです。Arduino用のJSONライブラリを使うと、Web APIやMQTTメッセージで使われるJSONデータ利用が簡単になります。Zigbee2MQTTで動く開閉センサーのJSONデータを読み込み、LEDを点滅させました。
JSON超入門
JSON (JavaScript Object Notation)は、データ交換に使うフォーマットです。キーと値のペアで、以下のようにテキスト表現します。例えば、temperatureが24.5でhumidityが60.5だった場合に、
{ "temperature": 24.5, "humidity': 60.5 }
人間が読みやすいように、スペースとか改行とか入れても良いですが、コメント文はありません。
キーはテキストです。値には、
- 文字列: 例えば”hello”
- 数値: 例えば12345
- null: null
- bool値: true または false
- オブジェクト: 他のJSON表記など
- 配列: 例えば[1,2,3]
が使えます。HomebridgeのコンフィグファイルもJSONですね。
Arduino用JSONライブラリ
Arduinoで簡単にJSONを使うためのライブラリが用意されてます。Arduino IDEから、「スケッチ」「ライブラリをインクルード」「ライブラリを管理…」を選択し、ライブラリマネージャを開きます。検索窓に、jsonと入れると、複数のライブラリが現れました。More Info見て、更新が頻繁で、一番メジャーな感じのArduinoJsonを使うことにしました。
こちらのサイトで、ArduinoJsonをESP32から使う方法が簡潔に説明されてます。使用方法は、ArduinoJsonのホームページで説明されてます。
JSONを作る
ArduinoJsonホームページのチュートリアルを参考に、ESP32でJSONデータを作ってみます。前述のtemperatureが24.5でhumidityが60.5というデータをJSONに変換します。データから一連のテキスト表現に変換することから、シリアル化とも呼ぶようです。
下が、Arduinoの簡単なプログラムです。ArduinoJson.hをインクルードして、docというJSONデータを宣言してます。StaticJsonDocumentを使うと、スタックの中に静的にメモリを確保します。StaticJsonDocumentの代わりに、DynamicJsonDocumentを使う方法もあります。こちらは、ヒープの中に動的にメモリを確保します。Dynamicの方が、使い方によってはメモリ効率が良いですが、処理速度はStaticの方が少しだけ高速とのことです。宣言で使う数値は、確保するメモリ量です。目安として1kB以下はStaticが、1kB以上はDynamicがおすすめらしいです。
#include <ArduinoJson.h>
StaticJsonDocument<200> doc;
void setup() {
// Initialize Serial port
Serial.begin(115200); while (!Serial);
doc["temperature"] = 24.5;
doc["humidity"] = 60.5;
serializeJson(doc, Serial);
}
void loop(){}
実際に必要とするメモリ量は、
で計算してくれます。簡単なデータなら200もあれば余裕のようです。今回はどのサンプルでも、初期化のメモリー量を200バイトにしました。以下の温度・湿度の例では100バイトでも動作しましたが、後述のZigbeeセンサーのデータの場合は、100バイトではdeserializeJson関数でエラーが出て、200バイト必要でした。
プログラムにあるように、serializeJson関数で、出力先をSerialにすると、シリアルモニタに直接出力されます。この結果、シリアルモニタに以下のように表示されます。
{"temperature":24.5,"humidity":60.5}
もちろんchar[]型やString型のデータを作ることもできます。例えば、以下のようにすれば、JSON形式のテキストデータを、Cのchar型配列に書き込んでくれます。
#include <ArduinoJson.h>
StaticJsonDocument<200> doc;
static char outputtext[100]="";
void setup() {
// Initialize Serial port
Serial.begin(115200); while (!Serial);
doc["temperature"] = 24.5;
doc["humidity"] = 60.5;
serializeJson(doc, outputtext, 100);
Serial.println(outputtext);
}
void loop(){}
とても楽です。でもこれくらい単純なJSONデータならば、String変数に追記しても簡単に作成できるので、それほど省力化にはならないかもしれません。
JSONを読む
逆に、JSONデータを解釈して読み込むのは、ライブラリに任せると楽です。元のデータに空白や改行があってもライブラリが処理してくれます。上記のサンプルと同様のJSONテキスト列を、変数に戻すArduinoプログラミング例を、下に示します。デシリアル化とも呼ぶようです。
#include <ArduinoJson.h>
StaticJsonDocument<200> doc;
static char jsontext[]="{\"temperature\":24.5,\"humidity\":60.5}";
void setup() {
// Initialize Serial port
Serial.begin(115200); while (!Serial);
DeserializationError error = deserializeJson(doc, jsontext );
if(error) Serial.println("Deserialization error.");
else {
float temp=doc["temperature"];
float humi=doc["humidity"];
Serial.println(temp); Serial.println(humi);
}
}
void loop(){}
この結果、シリアルモニタには、
24.50 60.50
と表示されます。
{"temperature":24.5,"humidity":60.5}
というJSONデータから、float型変数に読み込まれた結果です。標準ライブラリの文字列検索機能を使ってもそれほど大変なプログラミングではないですが、JSONライブラリを使うと楽でした。
Zigbee開閉センサでLチカする
以前の記事で、Zigbee開閉センサーをZigbee2MQTTに接続し、Homebridgeプラグインを使ってHomeKitから使用しました。
今回は、このセンサーからZigbee2MQTT経由でMQTTに流れているJSONデータを、ESP32で読み取って、開発ボード上の内蔵LEDを点灯します。
この構成では、Zigbeeセンサーからのデータを、Zigbee2MQTTがMQTTにパブリッシュします。Zigbee2MQTTの設定ファイルで、例えばこのセンサーにmagsensorという名前をつけておくと、zigbee2mqt/magsensorというトピックにメッセージが流れます。mosquitto_subコマンドでモニターすると、磁石を近づけ・遠ざけることで、それぞれ、以下のメッセージが流れていることがわかります。
$ mosquitto_sub -t zigbee2mqtt/magsensor/# -v
zigbee2mqtt/magsensor {"battery":100,"battery_low":false,"contact":false,"linkquality":127,"tamper":false,"voltage":3000}
zigbee2mqtt/magsensor {"battery":100,"battery_low":false,"contact":true,"linkquality":138,"tamper":false,"voltage":3000}
メッセージ部分はJSON形式になっています。このJSONデータ中の、contactというキーの値が磁石検出結果で、磁石が離れるとfalseで、近づくとtrueになります。これを受け取って、LEDを点灯・消灯するプログラムを書きます。全体は以下です。
#define LED_BUILTIN 2
//MQTT
#include <EspMQTTClient.h>
EspMQTTClient *client; //instance of MQTT
//JSON
#include <ArduinoJson.h>
StaticJsonDocument<200> doc;
//WiFi & MQTT parameters
const char SSID[] = "XXXXXXXX"; //WiFi SSID
const char PASS[] = "xxxxxxxx"; //WiFi password
char CLIENTID[] = "IRremote_2834578"; //something random
const char MQTTADD[] = "192.168.xxx.xxx"; //Broker IP address
const short MQTTPORT = 1883; //Broker port
const char MQTTUSER[] = "";//Can be omitted if not needed
const char MQTTPASS[] = "";//Can be omitted if not needed
const char SUBTOPIC[] = "zigbee2mqtt/magsensor";
void setup() {
// Initialize Serial port
Serial.begin(115200); while (!Serial);
Serial.println("Serial started.");
//initialize MQTT
client = new EspMQTTClient(SSID,PASS,MQTTADD,MQTTUSER,MQTTPASS,CLIENTID,MQTTPORT);
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
delay(1000);
}
//MQTT: connection callback
void onConnectionEstablished() {
client->subscribe(SUBTOPIC, onMessageReceived); //set callback function
Serial.println("MQTT-connection established.");
}
//MQTT: subtopic call back
void onMessageReceived(const String& topic, const String& message) {
Serial.println(message); //for debug
DeserializationError error = deserializeJson(doc, message);
if(error) Serial.println("Deserialization error.");
else if (doc["contact"] == true) digitalWrite(LED_BUILTIN,LOW);
else if (doc["contact"] == false) digitalWrite(LED_BUILTIN,HIGH);
}
void loop() {
client->loop();
}
MQTTを扱うEspMQTTClientライブラリの使用方法はこちらをご覧ください。このプログラムでは、MQTTのzigbee2mqtt/magsensorトピックにメッセージが流れると、onMessageReceived関数が呼ばれます。このメッセージは、上で確認したように、JSON形式で、磁石の接触・非接触により、”contact”キーの値がtrueかfalseになります。
{"battery":100,"battery_low":false,"contact":true,"linkquality":138,"tamper":false,"voltage":3000}
{"battery":100,"battery_low":false,"contact":false,"linkquality":127,"tamper":false,"voltage":3000}
これをArduinoJsonのdeserializeJson関数を使って読み出し、その結果に従って、ESP32開発ボードの上の青色LEDをOn/Offさせてます。動作している様子を以下に示します。
まとめ
ArduinoでJSONを扱うために、ArduinoJasonライブラリを使ってみました。このライブラリを使えば、JSONの読み書きが簡単に行えます。今回は、Zigbee接触センサーのMQTTメッセージをESP32で受け取り、そのJSONデータを解読し、LEDを点灯・消灯するプログラムを作成しました。
コメント