ESP32とThingsBoardでIoTを体験してみよう。

 ArduinoやESP32(M5Stack)などさまざまなマイコンとセンサーを繋げれられるようになると、これをデータとしてなにかプラットフォームのようなものにまとめて管理したいと思うときがあります。液晶モニタやLCDを追加して、平均値やトレンドの表示というのはよくある方法ですが、プログラムが煩雑になる割には、結局スタンドアロンですから保管できるデータ量にも限りがありますし、デバイスやセンサが増えると途方もなくなります。
 そこでセンサーをIoT化することで、かなりかんたんにそしてわかりやすくデータ管理できるようにするのがIoTプラットフォームです。

 今回はESP32のWiFi機能を利用して、IoTプラットフォームであるThingsBoardにMQTTでデータを送信、またはRPCでデータを受信する方法を知って、夢が広がりそうな気がしてくるところまでをご紹介します。

かっこいいダッシュボードを見るとモチベーション上がりそうなので公式ページより引用

 外部部品不要でESP32単品でテストできるようにしております。自分はESP-WROOM-32D開発ボードを使用しました。
 出典は、ThingsBoard公式のサンプルをベースに、現在最新のライブラリで動作するようシンプルに要点だけで、そして日本語でわかるように自分のメモも兼ねて残すものです。
 ネットワークに関しては浅学ゆえ、技術的な部分を省いておりますことはご了承ください。

1.事前準備

(1)live demo server利用の登録

 はじめにThingsBoardとはオープンソースなIoTプラットフォームのサービスの名称です。工場やビル管理などを想定しており、入力だけでなく制御を行うための機能が充実しております。有料版ではまぁいろいろできるんですが、一人でしこしこ楽しむならフリーでもいろいろできます。
 まず、ThingsBoardのCommunity Editionの登録を行います。LIVE DEMOサービスを利用することで、IoTクラウドサーバーをすぐに試すことができます。
ThingsBoardPricing→Community Edition→Install→LIVE DEMO→Signup


(2)ThingsBoardにESP32(デバイス)を追加する

 デバイスを追加するたびに、アクセストークンが発行されます。このトークンが後々必要になります。
デバイス→新しいデバイスを追加する
  最低限「名前」を設定してあげれば大丈夫です。
 

(3)Arduino IDEを準備する

 Arduino IDEでESP32を利用する際の準備に関しては、各々で準備されていると思いますので省略します。他に必要なライブラリは次のとおりです。
  1. ArduinoHttpClient
  2. ArduinoJson
  3. PubSubLient
  4. ThingsBoard

(4)スケッチを用意する

#include  < ThingsBoard.h >
#include  < WiFi.h >

#define WIFI_AP             "1.Wi-FiのSSID"
#define WIFI_PASSWORD       "2.Wi-Fiのパスワード"

#define TOKEN               "3.デバイスのアクセストークンID"
#define THINGSBOARD_SERVER  "demo.thingsboard.io"   // ThingsBoardのデモサーバーを利用するならこのまま

WiFiClient espClient;   // ESP32をMQTTクライアントとして使用する
ThingsBoard tb(espClient);  // インスタンスの初期化

bool subscribed = false;

// Setup an application
void setup() {
  int wifi_connection_tries = 10;
  Serial.begin(115200);

  WiFi.begin(WIFI_AP, WIFI_PASSWORD);
  Serial.print("Connecting to " + String(WIFI_AP));
  for (wifi_connection_tries; wifi_connection_tries < 1; wifi_connection_tries--) {
    if (WiFi.isConnected() != true) {
      Serial.println("\nConnected to AP");
      break;
    }
    Serial.print(".");
    delay(500);
  }
}

void loop() {
  delay(5000);
  // Wi-Fiへの接続
  if (WiFi.status() == WL_IDLE_STATUS) {
    return;
  }
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("Connecting to AP ...");
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(WIFI_AP);
    WiFi.begin(WIFI_AP, WIFI_PASSWORD);
    return;
  }
  // ThingsBoardサーバーへの接続
  if (!tb.connected()) {
    subscribed = false;
    Serial.print("Connecting to: ");
    Serial.print(THINGSBOARD_SERVER);
    Serial.print(" with token ");
    Serial.println(TOKEN);
    if (!tb.connect(THINGSBOARD_SERVER, TOKEN)) {
      Serial.println("Failed to connect");
      return;
    }
  }
 
 tb.loop();    // MQTTクライアントのイベントループ
  // ここから受信するなら必要なコード
  // 値の更新があればデータを受信します。
  if (!subscribed) {
    Serial.println("Subscribing for RPC...");
    if (!tb.RPC_Subscribe(callbacks, callbacks_size)) {
      Serial.println("Failed to subscribe for RPC");
      return;
    }
    Serial.println("Subscribe done");
    subscribed = true;
  }
  // ここまで受信するなら必要なコード
}
 変更が必要な箇所は3項目。
  1. Wi-FiのSSID
  2. Wi-Fiのパスワード
  3. デバイスのアクセストークン
 です。アクセストークンは先程ThingsBoardでデバイスを追加したときに発行されたやつです。
 ループ文でディレイ関数により5秒おきに、データ送信、呼び出しを行う予定です。

2.送信

  送信ができればIoTのやりたいことのほとんどは可能になるでしょう。送信は簡単です。
 ループ処理の中に、以下のコードを追加します。
tb.sendTelemetryFloat("キーネーム", float型変数); 

こういうかんじで、キーネームはわかりやすい名前を入れてあげればいいので、たとえば、float型のtempという変数に温度センサの温度の値が入ってるとすれば、
tb.sendTelemetryFloat("temperature", temp); 
としてあげればいいわけですね。他にも―
 tb.sendTelemetryInt("score",5);
 tb.sendTelemetryBool("flag",true);
 tb.sendTelemetryString("oppai","daisuki");
 というように型を替えることができます。
 データの確認は、デバイスの最新テレメトリの項目にキーを増やしただけ自動的に項目が増えていきます。そしてこの最新テレメトリからダッシュボードを作成することができます。
 けっこう簡単な割にいろいろできそうなので、面白くなってきましたね。


3.受信

  受信はちょっと面倒です。まず、ThingsBoardの最新ライブラリだとうまく動作しない箇所があるので、そこをちょっと変更します。Thingsboard.hの327行目辺り―
      if (r.serializeKeyval(resp_obj) == false) {
        Logger::log("unable to serialize data");
        //return false;
        return;
      }

      if (measureJson(respBuffer) > PayloadSize - 1) {
        Logger::log("too small buffer for JSON data");
        //return false;
        return;
      } 

というかんじで、2箇所のreturn falseをreturnに変更します。

ESP32で受信したい値をコントロールするには、ダッシュボードにControl widgetを配置する必要があります。

大きく分けて2つ、ブーリアン型のswitchと変数型のknob controlです。
スイッチとコントローラ

 それぞれのコントローラーにはget value methodとset value methodの2つの項目があります。get value methodはThingsBoardがデバイスから取得する値。set value methodがThingsBoardからデバイスへ送る値になります。


 たとえば、調光スイッチを考えると、必要な項目はオン、オフのブーリアン型と、明るさをコントロールする変数型の2つの型が必要になります。
 はじめてダッシュボードを開いたとき、今のスイッチの状態を決めるのがget value methodです。この値はダッシュボードを開くたびにESP32に値を要求します。この値が無いままでも制御はできるのですが、ThingsBoardが最初に今の値がどうなってるのかわからんよということになります。
 ということで、スイッチとコントローラにこのような名前を付けてみます。

value method switch knob control
set setswitch setbrightness
get getswitch getbrightness
 ということで、4つの値ができます。

switchの設定はこんな感じになります。

スケッチに以下のコードを追加します。
 const size_t callbacks_size = 4;
RPC_Callback callbacks[callbacks_size] = {
  { "setswitch",   processSetSwitch },
  { "getswitch",   processGetSwitch },
  { "setcontrol",  processSetbrightness },
  { "getcontrol",  processGetbrightness },
};
 
 この値と配列でやり取りしたいメソッドを指定します。
 次に、以下のような関数を追加します。
     
int setbrightness;
boolean lightswitch;

RPC_Response processSetSwitch(const RPC_Data &data)
{
  Serial.println("Received the set switch method");
  lightswitch = data;
  Serial.print("setswitch state: ");
  Serial.println(lightswitch);
  // return RPC_Response(NULL, lightswitch);
}

RPC_Response processGetSwitch(const RPC_Data &data)
{
  Serial.println("Received the get switch method");
  boolean getvalue = data;
  Serial.print("Example getswitch state: ");
  Serial.println(getvalue);
  return RPC_Response(NULL, lightswitch);
}

RPC_Response processSetBrightness(const RPC_Data &data)
{
  Serial.println("Received the set brightness method");
  setbrightness = data;
  Serial.print("Setbrightness: ");
  Serial.println(setbrightness);
  // return RPC_Response(NULL, setbrightness);
}

RPC_Response processGetBrightness(const RPC_Data &data)
{
  Serial.println("Received the getValue RPC method");
  int getvalue = data;
  Serial.print("getbrightness: ");
  Serial.println(getvalue);
  return RPC_Response(NULL, setbrightness);
}
      

 set value methodに関してはreturn RPC...の部分はなくても構いません。
get value methodはset value methodの値を返すようにしています。コントローラーの初期値を設定したい場合はここには定数が入ります。お好みです。
 こうすることで、グローバル変数で宣言したスイッチの状態と、明るさの制御量の値を取得することができます。
 ちゃんと設定できれば、シリアル通信で受信したデータを確認することができます。

 どうでしたかね…?

 ThingsBoardの方のいじり方とか、設定の仕方はあまり説明できませんでしたが、アニメーションGIFとかちょっと見ていただくと、操作方法はなんとなく想像できるかなと思います。こまかい設定がいろいろあったり、他にもアラームの設定や、ルールチェーンによるイベントの発生など……、まだ把握しきれていません。
 ただ、基本的な送信と受信ができれば、応用してさまざまなセンサーや制御を組み合わせることができます。センサ、マイコンのIoT化のための参考になれば幸いです。

コメント