IoTサービスのラベル取得(VPN) (M5Stack)

サンプルソースコード

概要

IIJ IoTの「ラベル機能」を使用してみます。

前回のサンプル に対して、ラベル機能を使用するような機能追加をしています。
特定のラベルが設定されていた場合は、そのラベルの値を取得し、その数値を使ってデータ送信間隔を変更します。

つまり、ラベル機能を使ってサーバーからクライアント側に向けて指示を出すようなイメージです。

ソースコード
m5stack_iijiot_vpn_label.ino
#include <M5Stack.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <WireGuard-ESP32.h>
#include "lwip/dns.h"
 
//--- TODO: WI-Fiルーターの SSIDとパスワードを指定してください
const char* ssid = "YOUR_SSID";
const char* pass = "YOUR_PASSWORD";
//---
 
//--- TODO: 自身のVPNアクセスの設定に書き換えてください。
const IPAddress local_ip(10, 10, 10, 10);               //IPアドレス
const char public_key[] = "YOUR PUBLIC KEY";            //公開鍵
const char private_key[] = "YOUR PRIVATE KEY";          //秘密鍵
const char endpoint_address[] = "vgw001.to.iot.iij.jp"; //接続先アドレス
const int endpoint_port = 9999;                         //接続先ポート番号
//---

const char* iij_iot_uri = "http://gw.iot.iij.jp/v1";
const char* iij_iot_label_uri = "http://gw.iot.iij.jp/v1/dvc-state/json/label-contents";
const char* sec_label_name = "delay_sec";

int delay_sec = 60;

static WireGuard wg;

/*
  setup関数
*/
void setup() {
  //M5Stackを初期化
  M5.begin();
  M5.Power.begin();
 
  //Wi-Fi接続
  WiFi.begin(ssid, pass);
  M5.Lcd.printf("Waiting connect to WiFi: %s ", ssid);
  while(WiFi.status() != WL_CONNECTED) {
    //接続完了まで待つ
    delay(1000);
    M5.Lcd.print(".");
  }
  //接続完了したらIPアドレスとMacアドレスを表示する
  M5.Lcd.println("\nWiFi connected");
  M5.Lcd.print("IP address: ");
  M5.Lcd.println(WiFi.localIP());
  M5.Lcd.print("MAC address:");
  M5.Lcd.println(WiFi.macAddress());
  delay(500);

  //現在時刻をセット
  M5.Lcd.println("Adjusting system time...");
  configTime(9 * 60 * 60, 0, "ntp.jst.mfeed.ad.jp", "ntp.nict.jp", "time.google.com");
  delay(2000);  

  //WireGuardでVPN接続する
  M5.Lcd.println("Initializing WireGuard...");
  if (wg.begin(
      local_ip,
      private_key,
      endpoint_address,
      public_key,
      endpoint_port)) {

    M5.Lcd.println("VPN connected.");
    //--- DNS参照先をVPN側に向ける
    ip_addr_t dnsserver = IPADDR4_INIT_BYTES(10, 64, 2, 11);
    dns_setserver(0, &dnsserver);
  } else {
    M5.Lcd.println("WireGuard initializing Failed.");
  }
  delay(3000);
}
 
/*
  IIJ IoTサービスにデータ送信する
    引数
      const char* ns     : 送信する namespace値
      const char* name   : 送信する name値
      const double value : 送信する value値
    戻り値
      bool true:送信成功 false:送信失敗
*/
bool send_data(const char* ns, const char* name, const double value) {
  //JSONオブジェクトを作成
  StaticJsonDocument<JSON_OBJECT_SIZE(3)> body;
  if (ns != nullptr && strlen(ns) > 0) {
    body["namespace"] = ns;
  }
  body["name"] = name;
  body["value"] = value;
 
  //JSONを文字列に変換
  String body_string;
  serializeJson(body, body_string);
 
  //JSON文字列をIIJ IoTサービスにPOST
  HTTPClient http;
  http.begin(iij_iot_uri);
  http.addHeader("Content-type", "application/json");
  int status_code = http.POST(body_string);
  http.end();
 
  return status_code >= 200 && status_code < 300;
}
 
/*
  IIJ IoTサービスに設定されているラベルのJSON文字列を取得する
    引数
      String& labels     : 取得したラベル文字列を格納するString参照
    戻り値
      String labelsを返す
*/
String get_label_string(String& labels) {
  HTTPClient c;
  c.begin(iij_iot_label_uri);
  int status_code = c.GET();
  if(status_code == 200) {
    labels = c.getString();
  } else {
    labels = "{}";
  }
  c.end();
  return labels;
}
 
/*
  ラベルJSON文字列内から、keyに対応する値を返す
    引数
      const String& labels     : 取得したラベル文字列を格納するString参照
      const char* key          : 値を取得するkey
      char* value              : 取得した値(文字列)を格納する変数
      size_t size              : value配列のサイズ
    戻り値
      char*  valueを返す
*/
char* get_label(const String& labels, const char* key, char* value, size_t size) {
  //ラベルのJSON文字列を解析する
  DynamicJsonDocument root(labels.length() * 3);
  deserializeJson(root, labels);
 
  //ラベルJSON内から、keyの値を取得する(keyが存在しない場合は空文字列)
  const char* src = root[key];
  memset(value, '\0', size);
  if(src != nullptr) {
    strncpy(value, src,  size-1);
  }
  //value値を返す
  return value;
}
 
/*
  IIJ IoTからラベルを取得し、keyに対応する値を返す
    引数
      const char* key          : 値を取得するkey
      char* value              : 取得した値(文字列)を格納する変数
      size_t size              : value配列のサイズ
    戻り値
      char*  valueを返す
*/
char* get_label(const char* key, char* value, size_t size) {
  // IIJ IoTからラベルを取得する
  String labels;
  get_label_string(labels);
  // 値を取得する
  return get_label(labels, key, value, size);
}
 
/*
  loop関数
*/
void loop() {
  //CPU温度を取得
  float temp = temperatureRead();
 
  //データ送信
  if (send_data("M5Stack", "temperature", temp)) {
    M5.Lcd.printf("data send OK: temp=%f \n", temp);
  } else {
    M5.Lcd.println("data send NG.");
  }
 
  //ラベル取得
  char value[32];
  get_label(sec_label_name, value, sizeof(value));
  int sec = String(value).toInt();
  //"delay_sec"のラベルが存在したら、送信間隔の時間をそのラベル値の数値に変更する
  if (sec > 0 && sec != delay_sec ) {
    delay_sec = sec;
    M5.Lcd.printf("update delay: %d sec.\n", delay_sec);
  }
 
  //待つ
  delay(delay_sec * 1000);
}
実行結果

IoTサービスのコントロールパネルから、ラベルを設定することで、送信間隔を変更することができます。

コントロールパネルの左側メニューから「デバイス」を選択し、続いて一覧の中から使用している「VPN」を選択し、画面上の「編集」ボタンをクリックします。


表示された画面から、ラベル名に delay_sec 、値に適当な数値を入力し、保存します。

M5Stack側で、設定したラベル値が読み込まれ、送信間隔が変更されます。
ラベル値に 30 と設定した場合は、下記の写真の通り「update delay: 30 sec」と表示され、次回以降は30秒間隔でデータ送信されます。

各種情報へのリンク

IIJ IoTサービスマニュアル

M5Stack

WireGuard-ESP32-Arduino

ArduinoJson