M5StickCにENV HAT(気温、湿度、気圧)をサーバーにアップする その4(IIJ Machinist編)

現時点での情報です。最新情報はM5StickC非公式日本語リファレンスを確認してください。

概要

ENV HAT(気温、湿度、気圧)の情報を1分ごとにMachinistにアップしました。また基礎編をベースにしていますので、基礎部分の解説は省いています。

Machinistとは?

IIJが提供するデータ収集サービスです。日本の会社が運営しているので、日本語で利用することができ、有料プランも用意されています。

比較的あたらしいサービスのため、情報量は他に比べると少ないです。

事前作業

Machinistにてあらかじめエージェントを作成する必要があります。エージェント名とエージェントIDが必要になります。その他データを保存するネームスペース名や、端末などを識別するタグの設定をESP32内部のpreferencesに保存しておいてください。

スケッチ(ライブラリ利用版)

/*
    note: need add library Adafruit_BMP280 from library manage
    Github: https://github.com/adafruit/Adafruit_BMP280_Library

    note: need add library IIJMachinistClient from library manage
    Github: https://github.com/nara256/IIJMachinistClient
*/

#include <M5StickC.h>
#include <Preferences.h>
#include <WiFi.h>
#include "DHT12.h"
#include <Adafruit_BMP280.h>
#include "IIJMachinistClient.h"

Preferences preferences;
char wifi_ssid[33];
char wifi_key[65];

DHT12 dht12;
Adafruit_BMP280 bme280;
unsigned long nextUpdate = 0;

char machinist_apiKey[40];
char machinist_agentName[40];
char machinist_namespace[40];
char machinist_tagName[40];
char machinist_tagValue[40];
IIJMachinistClient *machinist;

void setup() {
  // M5StickC初期化
  M5.begin();
  M5.Lcd.setRotation(3);
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setCursor(0, 0, 2);
  M5.Lcd.println("ENV TEST");

  // Wi-Fiアクセスポイント情報取得
  preferences.begin("Wi-Fi", true);
  preferences.getString("ssid", wifi_ssid, sizeof(wifi_ssid));
  preferences.getString("key", wifi_key, sizeof(wifi_key));
  preferences.end();

  // Wi-Fi接続
  Serial.printf("接続中 - %s ", wifi_ssid);
  WiFi.begin(wifi_ssid, wifi_key);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println(" 接続完了");

  // I2C初期化
  Wire.begin(0, 26);

  // BME280初期化
  if (!bme280.begin(0x76)) {
    Serial.println("BME280センサーが見つかりません");
    while (1);
  }

  // Machinist情報取得
  preferences.begin("Machinist", true);
  preferences.getString("apiKey", machinist_apiKey, sizeof(machinist_apiKey));
  preferences.getString("agentName", machinist_agentName, sizeof(machinist_agentName));
  preferences.getString("namespace", machinist_namespace, sizeof(machinist_namespace));
  preferences.getString("tagName", machinist_tagName, sizeof(machinist_tagName));
  preferences.getString("tagValue", machinist_tagValue, sizeof(machinist_tagValue));
  preferences.end();

  // API KEYを指定してMachinistの初期化
  machinist = new IIJMachinistClient(machinist_apiKey);
}

void loop() {
  // 気温
  float tmp = dht12.readTemperature();
  M5.Lcd.setCursor(0, 20, 2);
  M5.Lcd.printf("Temp: %2.1f C", tmp);

  // 湿度
  float hum = dht12.readHumidity();
  M5.Lcd.setCursor(0, 40, 2);
  M5.Lcd.printf("Humi: %2.0f %%", hum);

  // 気圧
  float pressure = bme280.readPressure() / 100.0F;
  M5.Lcd.setCursor(0, 60, 2);
  M5.Lcd.printf("pressure: %6.1f hPa", pressure);

  // 1分に1度データ送信
  if ( nextUpdate < millis() ) {
    // シリアル出力
    Serial.printf("Temp: %2.1f C\n", tmp);
    Serial.printf("Humi: %2.0f %%\n", hum);
    Serial.printf("pressure: %6.1f hPa\n", pressure);

    // Machinist送信
    machinist->post(machinist_agentName, machinist_namespace, "Temp", tmp, machinist_tagName, machinist_tagValue);
    machinist->post(machinist_agentName, machinist_namespace, "Humi", hum, machinist_tagName, machinist_tagValue);
    machinist->post(machinist_agentName, machinist_namespace, "Pressure", pressure, machinist_tagName, machinist_tagValue);

    // 時間更新(60秒後)
    nextUpdate = millis() + 60000;
  }

  // 1秒待機
  delay(1000);
}

Machinistオフィシャルのライブラリは提供されていません。そのためIIJMachinistClientライブラリを利用しました。

このライブラリはSSLのRoot CAを内部に保存していますが、本日ためしたところサーバー側が更新されていたようで、エラーがでて通信できませんでした。

新しいRoot CAを取得して、プルリクエストを送っておきましたが、今後も変わる可能性があります。そのためRoot CAを利用しないバージョンも作成してみました。

スケッチ(自作関数利用版)

/*
    note: need add library Adafruit_BMP280 from library manage
    Github: https://github.com/adafruit/Adafruit_BMP280_Library
*/

#include <M5StickC.h>
#include <Preferences.h>
#include <WiFi.h>
#include "DHT12.h"
#include <Adafruit_BMP280.h>
#include <HTTPClient.h>

Preferences preferences;
char wifi_ssid[33];
char wifi_key[65];

DHT12 dht12;
Adafruit_BMP280 bme280;
unsigned long nextUpdate = 0;

char machinist_apiKey[40];
char machinist_agentName[40];
char machinist_namespace[40];
char machinist_tagName[40];
char machinist_tagValue[40];

void sendMachinist(const char *apiKey, const char *agentName, const char *nameSpace, const char *name, double value , const char *tagName, const char *tagValue) {
  HTTPClient http;

  http.begin("https://gw.machinist.iij.jp/endpoint");
  http.addHeader("Content-Type", "application/json");
  http.addHeader("Authorization", String("Bearer ") + String(apiKey));
  char json[1024];
  sprintf( json,
           "{\n"\
           "  \"agent\": \"%s\",\n"\
           "  \"metrics\": [\n"\
           "    {\n"\
           "      \"namespace\": \"%s\",\n"\
           "      \"name\": \"%s\",\n"\
           "      \"tags\": {\n"\
           "        \"%s\": \"%s\"\n"\
           "      },\n"\
           "      \"data_point\": {\n"\
           "        \"value\": %f\n"\
           "      }\n"
           "    }\n"
           "  ]\n"
           "}\n"
           , agentName
           , nameSpace
           , name
           , tagName
           , tagValue
           , value
         );
  Serial.println(json);

  http.POST(json);
}

void setup() {
  // M5StickC初期化
  M5.begin();
  M5.Lcd.setRotation(3);
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setCursor(0, 0, 2);
  M5.Lcd.println("ENV TEST");

  // Wi-Fiアクセスポイント情報取得
  preferences.begin("Wi-Fi", true);
  preferences.getString("ssid", wifi_ssid, sizeof(wifi_ssid));
  preferences.getString("key", wifi_key, sizeof(wifi_key));
  preferences.end();

  // Wi-Fi接続
  Serial.printf("接続中 - %s ", wifi_ssid);
  WiFi.begin(wifi_ssid, wifi_key);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println(" 接続完了");

  // I2C初期化
  Wire.begin(0, 26);

  // BME280初期化
  if (!bme280.begin(0x76)) {
    Serial.println("BME280センサーが見つかりません");
    while (1);
  }

  // Machinist情報取得
  preferences.begin("Machinist", true);
  preferences.getString("apiKey", machinist_apiKey, sizeof(machinist_apiKey));
  preferences.getString("agentName", machinist_agentName, sizeof(machinist_agentName));
  preferences.getString("namespace", machinist_namespace, sizeof(machinist_namespace));
  preferences.getString("tagName", machinist_tagName, sizeof(machinist_tagName));
  preferences.getString("tagValue", machinist_tagValue, sizeof(machinist_tagValue));
  preferences.end();
}

void loop() {
  // 気温
  float tmp = dht12.readTemperature();
  M5.Lcd.setCursor(0, 20, 2);
  M5.Lcd.printf("Temp: %2.1f C", tmp);

  // 湿度
  float hum = dht12.readHumidity();
  M5.Lcd.setCursor(0, 40, 2);
  M5.Lcd.printf("Humi: %2.0f %%", hum);

  // 気圧
  float pressure = bme280.readPressure() / 100.0F;
  M5.Lcd.setCursor(0, 60, 2);
  M5.Lcd.printf("pressure: %6.1f hPa", pressure);

  // 1分に1度データ送信
  if ( nextUpdate < millis() ) {
    // シリアル出力
    Serial.printf("Temp: %2.1f C\n", tmp);
    Serial.printf("Humi: %2.0f %%\n", hum);
    Serial.printf("pressure: %6.1f hPa\n", pressure);

    // Machinist送信
    sendMachinist(machinist_apiKey, machinist_agentName, machinist_namespace, "Temp", tmp, machinist_tagName, machinist_tagValue);
    sendMachinist(machinist_apiKey, machinist_agentName, machinist_namespace, "Humi", hum, machinist_tagName, machinist_tagValue);
    sendMachinist(machinist_apiKey, machinist_agentName, machinist_namespace, "Pressure", pressure, machinist_tagName, machinist_tagValue);

    // 時間更新(60秒後)
    nextUpdate = millis() + 60000;
  }

  // 1秒待機
  delay(1000);
}

エラー処理をしていない、簡易版です。自分でJSONを作ってPOSTしています。HTTPS接続時に証明書の確認はしていません。HTTPSには、通信の内容を暗号化する機能と、そのサーバーが本物かを認証する機能があります。

Root CAを利用しないということは、サーバーが本物かを認証していません。そのため、Wi-Fiルーターなどに細工されると、違うサーバーにデータを送信していても、気がつくことができません。

まあ、実際のところそこまでのセキュリティーが必要無いことが多いのと、他のサービスもHTTPで送信していたりするので、Root CAを利用していない危険性を理解した上で利用してください。

グラフはちょっと扱いにくくて、特定範囲だけキレイにレポーティングする用途には使いにくいかもしれません。複数のグラフをダッシュボードとして表示するのではなく、1つのグラフを拡大とかしながら確認する用途向きだと感じました。

まとめ

サービス自体はJSONで送信するだけなので単純なのですが、ESP32からだとオフィシャルライブラリがあったほうが良かったなと思います。

概要

コメント