突然ですが、『Machinist』、みなさん使ってますか?
ホームページでは「メトリクス」という耳慣れないものの管理をするようなことが書かれていますが、IIJのプレスリリースでは「データ可視化・監視サービス」って誰にでもわかる言葉で書いてあるんですよ。IT業界ってかっこつけたがるんですよね。
まずは、どんな風に見えるか、ぼくが送ったデータのダッシュボードのキャプチャを載せますね。
こんな感じの表示なら、殆ど手間をかけずに作ることができます。
ただ、所々にこのサービスの「かっこつけ」が垣間見れ、「ダークUI」に切り替えるとこんな風になります。
ぼくも普段は暗い方が目に優しいのでダークモードを良く使うようになってるんですが、このカラーリングは...逆に目が痛い!情報が入ってこない!ので、使ってません。
特徴的なデータ収集方法
Machinistを使ってみて1番変わってるなと思ったのが、データの収集方法です。
普通のサービスだと、温度と湿度の2つの項目を収集するなら、事前に温度と湿度の項目を定義しておく必要がありますよね。
ThingSpeakだとChannel Settingsで使用するフィールドにチェックを付けるようになっています。
しかし、Machinistはデータを定義する設定画面がないんです。
Machinistでは事前に定義をしていなくても、送られてくるJSONのデータ形式をそのまま取り込むようになっています。一見非常に便利そうに感じますが、JSONのデータ構造が複雑になるのと、JSONでしかデータを送ることができなくなるため、Arduinoのような軽いエッジデバイスにとってはかえってデメリットになります。
上で紹介したグラフの元となるESP8266が送っているJSONデータはこんな感じで、非常に面倒くさいです。
{ "agent_id": "XXXXXXXXXXXXXXXX", "metrics": [ { "name": "Temperature", "namespace": "BME280", "data_point": { "value": 28.2 } }, { "name": "Humidity", "namespace": "BME280", "data_point": { "value": 52.1 } }, { "name": "Pressure", "namespace": "BME280", "data_point": { "value": 1019 } } ] }
ThingSpeakの場合は温度・湿度・気圧を送るのにこんなシンプルな形式で済みます。
{ "api_key":"XXXXXXXXXXXXXXXX", "field1":28.2, "field2":52.1, "field3":1019 }
ずいぶん違いますよね。
厄介なHTTPS送信
ESP8266を使ってMachinistにデータを送るのにはもう1つ厄介なことがあります。データの送信先がhttps://gw.machinist.iij.jp/endpoint
とHTTPSオンリーになっていることです。
Arduino IDEでESP8266を使えるようにするためには、ボードマネージャでESP8266のボード情報をインストールする必要があります。
このバージョンが2.4.xまではaxTLSというモジュールが使われていたのですが、2.5.xになってaxTLSは非推奨扱いになり以前のコードがコンパイルできなくなりました。ドキュメントにはTLS 1.2がサポートされていないからと書いてあります。
2.5.xではaxTLSに代わってBearSSLというモジュールが採用されているんですが、中間者攻撃を完全に防ぐにはアクセス先のサーバー証明書を発行したルート証明書を取り込む必要がありとんでもなく面倒なんです。
正直、そんなところまで自分のプログラムで面倒を見なくて済むラズパイなんかを使うのが正解なんでしょうが、単に温度や湿度のデータを送るだけだとラズパイはオーバースペックだし、電源をブツ切りできないし、バッテリー運用もしずらい!
結局、妥協としてBearSSLのサンプルプログラムに載っていた証明書を使わないで最も処理が軽い方法を使うことにしました。BearSSLを使うときは160MHzにしろとか書かれてあったんですが、これだと80MHzのままでも問題なく動くんです。
該当部分のコードはこんな感じになります。
BearSSL::WiFiClientSecure client; client.setInsecure(); client.setCiphersLessSecure(); client.connect(gw.machinist.iij.jp, 443);
ハードウェア
今回使ったパーツはこれです。
写真はこうなんですが...、基板の裏にも配線してるのでわかんないですよね。
配線
配線はいたって簡単でESP8266とBME280はI2C接続、センサーの電源はGPIO13、Deep Sleepから起きるためにGPIO16とRSTを繋いでいます。
ソフトウェア
ボードマネージャ
ESP8266のボード情報はバージョン2.5.xを使います。
ライブラリマネージャ
ArduinoJsonのバージョンは6.xだとコンパイルでエラーになるので、バージョン5.xを使ってください。
BME280にはAdafruit Unified SensorとAdafruit BME280 Libraryを使います。
スケッチ
スケッチを動作させるには、事前にMachinistのアカウントとエージェントを作っておく必要があります。
スケッチはESP8266のDeep Sleep機能を利用してDS_INTERVALで指定した分数毎に動くようになっているので、loop()にはコードを書いていません。
MachinistのJSONデータ形式はシンプルではないため、わかりやすいようにテンプレートの値だけを変更するようにしています。
/* * BME280 sensor program for Machinist using ESP8266 * October 02, 2019 * By Hiroyuki ITO * https://intellectualcuriosity.hatenablog.com/ * MIT Licensed. */ #include <Wire.h> #include <ESP8266WiFi.h> #include <ArduinoJson.h> #include <Adafruit_BME280.h> #define DS_INTERVAL 1 // Deep Sleep Interval Time (minutes) #define WIFI_SSID "YourNetworkName" #define WIFI_PASS "YourPassword" #define MACHINIST_AGENT_ID "YourMachinistAgentID" #define MACHINIST_API_KEY "YourMachinistAPIKey" #define MACHINIST_HOST "gw.machinist.iij.jp" #define MACHINIST_PORT 443 #define MACHINIST_PATH "/endpoint" #define VCC_PIN 13 // Sensor VCC Pin // Global Objects Adafruit_BME280 bme; // I2C String temperature; String humidity; String pressure; // JSON Data Template char json[] = "{" "\"agent_id\":\"" MACHINIST_AGENT_ID "\"," "\"metrics\": [" "{" "\"name\": \"Temperature\"," "\"namespace\": \"BME280\"," "\"data_point\": {" "\"value\": 25.0" // dummy "}" "}," "{" "\"name\": \"Humidity\"," "\"namespace\": \"BME280\"," "\"data_point\": {" "\"value\": 50.0" // dummy "}" "}," "{" "\"name\": \"Pressure\"," "\"namespace\": \"BME280\"," "\"data_point\": {" "\"value\": 1000" // dummy "}" "}]" "}"; /* * Get Sensor Data */ void GetSensorData() { // Sensor Power ON pinMode(VCC_PIN, OUTPUT); digitalWrite(VCC_PIN, HIGH); // Initialize BME280 delay(10); bme.begin(0x76); // SDO GND(Open):0x76 VDD:0x77 // Sensor Measurement temperature = String(bme.readTemperature(), 1); // xx.xC humidity = String(bme.readHumidity(), 1); // xx.x% pressure = String(bme.readPressure()/100.0, 0); // xxxxhPa Serial.println("Temperature:" + temperature); Serial.println("Humidity :" + humidity); Serial.println("Pressure :" + pressure); // Sensor Power OFF digitalWrite(VCC_PIN, LOW); } /* * Send Sensor Data */ void SendSensorData() { // Make JSON data StaticJsonBuffer<1000> jsonBuffer; JsonObject& root = jsonBuffer.parseObject(json); String JSBuffer; root["metrics"][0]["data_point"]["value"] = temperature.toFloat(); root["metrics"][1]["data_point"]["value"] = humidity.toFloat(); root["metrics"][2]["data_point"]["value"] = pressure.toInt(); root.prettyPrintTo(JSBuffer); Serial.println(JSBuffer); JSBuffer = ""; root.printTo(JSBuffer); // The ciphers used to set up the SSL connection can be configured to // only support faster but less secure ciphers. BearSSL::WiFiClientSecure client; client.setInsecure(); client.setCiphersLessSecure(); Serial.print("Trying: " MACHINIST_HOST ":" + String(MACHINIST_PORT) + "... "); client.connect(MACHINIST_HOST, MACHINIST_PORT); if (!client.connected()) { Serial.println("Can't connect to Machinist"); return; } Serial.println("Connected"); client.println("POST " MACHINIST_PATH " HTTP/1.0"); client.println("Host: " MACHINIST_HOST); client.println("Authorization: Bearer " MACHINIST_API_KEY); client.println("Content-Type: application/json"); client.println(String("Content-Length: ") + JSBuffer.length()); client.println(); client.println(JSBuffer); while (!client.available()) { delay(10); } Serial.print("Sent Data to Machinist\nThe response code is "); Serial.println(client.readStringUntil('\r')); client.stop(); } void setup() { Serial.begin(74880); // Connect to WiFi Serial.print("Connecting to " WIFI_SSID); WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASS); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\nWiFi connected"); GetSensorData(); SendSensorData(); // Go to deep sleep delay(1000); Serial.println(String("Go to sleep for ") + DS_INTERVAL + " minutes"); ESP.deepSleep(DS_INTERVAL*60*1000*1000, WAKE_RF_DEFAULT); } void loop() { }
Machinistは制限がありますが無料枠でも十分に楽しめるので、ESP8266でいろいろと遊んでください!
おしまい