Machinistの気温グラフです。所々データが抜けているのは、データがうまくアップできなかったようです。21:00前後に抜けているのは、電源をオフ/オンしていたからです。
ThingSpeakの方は、昨日から今日にかけてごっそりとデータが抜けています。処理的には、WiFiをオンしてMachinistにアップしてWiFiをオフし、再度WiFiをオンしてThingSpeakにアップしていますが、WiFiオフからオンの時間を少し空けた方が安定するのかもしれません。
スケッチはバグがあるかもしれません、無保証です。著作権は留保しますが、改変などご自由にどうぞ。もしお気づきの点があればコメント頂けると幸いです。回路図は簡単なので省略します。 写真を参考にしてください。
今回試したIoTプラットフォームは、どちらも無料で試せます(それなりに制限あり)。
ThingSpeakは以下のページがわかりやすくて参考にさせて頂きました。
http://iwathi3.hatenablog.com/entry/Data-to-Graph-ThingSpeak
MachinistはIIJがやっていて日本語なので判りやすかったです。
https://machinist.iij.jp/getting-started/
//-------------------------------------------------------------------- // IoT気象計 2019.07.31 naka // 気圧・気温・湿度をクラウド(IoTプラットフォーム)にアップする // // Device // ・WiFiマイコン ESP32 // ・気圧・気温・湿度センサ BME280 (I2C) // ・小型LCD AQM1602XA-RN-GBW (I2C) // ・モニタ用LED (pin2) // // IoTプラットフォーム // ・ThingSpeak https://thingspeak.com/ // ・Machinist https://machinist.iij.jp/ // // 処理 // ・1分毎に測定しLCDに測定値を表示(更新間隔1分) // ・5分毎に過去5回の平均を出してIoTプラットフォームにアップ // //-------------------------------------------------------------------- #include <Wire.h> #include <FaBoLCDmini_AQM0802A.h> #include <WiFi.h> #include <HTTPClient.h> #include <SparkFunBME280.h> #include <ThingSpeak.h> // WiFiアクセスポイント情報 const char *ssid = "******"; const char *pass = "********"; // Machinist情報 const String MachinistApi_key = "****************"; const String MachinistURL = "https://gw.machinist.iij.jp/endpoint"; // ThingSpeak情報 unsigned long myChannelNumber = ******; const char* myWriteAPIKey = "****************"; int ThingSpeak_DelayCount; // ポスト間隔を15秒以上あける必要があり、そのためのカウンタ WiFiClient client; FaBoLCDmini_AQM0802A lcd; BME280 sensor; #define LED_PIN 2 int LedBlink = 0; // タイマー割り込み用 volatile int interruptCounter; hw_timer_t *timer = NULL; portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; int secCount = -1; int minCount = -1; int nCount; float temp, hum, pres; float avetemp,avehum,avepres; void IRAM_ATTR secEvent() { //1000ms=1s毎に呼び出される portENTER_CRITICAL_ISR(&timerMux); interruptCounter++; portEXIT_CRITICAL_ISR(&timerMux); } void setup() { char buff[17]; IPAddress IP; // Serial.begin(115200); // Serial.print("started\n"); // タイマー割り込み設定 timer = timerBegin(0, 80, true); timerAttachInterrupt(timer, &secEvent, true); timerAlarmWrite(timer, 1000000, true); // 1sec (us単位) // モニタLED設定 pinMode(LED_PIN, OUTPUT); // モニタLCD設定 SetupLCD(); lcd.clear(); lcd.print("hello!"); // 起動メッセージ // 温湿度センサ SetupSensor(); // WiFi接続(起動時に確認のために一旦繋いでみる) WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { delay(100); } lcd.clear(); lcd.print("WiFi connected"); IP = WiFi.localIP(); WiFi.disconnect(WIFI_OFF); // 省電力のために切断 sprintf(buff, "IP=%d:%d:%d:%d", IP[0], IP[1], IP[2], IP[3]); lcd.setCursor(0, 1); // Col,Raw lcd.print(buff); ThingSpeak_DelayCount = -1; delay(5000); // 5秒待ち(IPアドレスを目視確認のため) lcd.clear(); nCount = 0; // センサー値の平均用変数初期化 temp = hum = pres = 0.0; timerAlarmEnable(timer); // 割り込みタイマー開始 } void loop() { float ftemp,fhum,fpres; char buff[17]; if (interruptCounter > 0) { // タイマー割り込み処理(1秒毎に割り込み) portENTER_CRITICAL(&timerMux); // ここから3行は決まり文句らしい interruptCounter--; portEXIT_CRITICAL(&timerMux); // 以下、1秒ごとに実行する処理 secCount++; LedBlink ^= 1; // トグル操作 digitalWrite(LED_PIN, LedBlink); if (secCount == 60 || secCount==0) { minCount++; secCount = 0; // センサーから値を取り出す ftemp = sensor.readTempC(); fhum = sensor.readFloatHumidity(); fpres = sensor.readFloatPressure() / 100.0; DispLCD(ftemp,fhum,fpres); // LCDに表示 temp += ftemp; // 平均を出すためにΣ hum += fhum; pres += fpres; nCount++; if (minCount == 5) { // 5分毎に平均してIoTプラットフォームへポストする minCount = 0; avetemp = temp / nCount; avehum = hum / nCount; avepres = pres / nCount; temp = hum = pres = 0.0; nCount = 0; // IoTプラットフォームにポストする PostMachinist(avetemp,avehum,avepres); PostThingSpeak('T', avetemp); ThingSpeak_DelayCount = 0; // カウントスタート } } sprintf(buff, "%02d:%02d", minCount, secCount); lcd.setCursor(11, 0); // Col,Raw lcd.print(buff); if (ThingSpeak_DelayCount>=0) { // ThingSpeakはポスト間隔を15秒以上開ける必要があり、 ThingSpeak_DelayCount++; // 一応20秒経ったら湿度、さらに20秒経ったら気圧をポスト if (ThingSpeak_DelayCount==20) { PostThingSpeak('H',avehum); } else if (ThingSpeak_DelayCount==40) { PostThingSpeak('P',avepres); ThingSpeak_DelayCount = -1; // カウントストップ } } } } void SetupSensor() { sensor.setI2CAddress(0x76); // 0x77(default) or 0x76 sensor.beginI2C(); // 以降は省略可(室内の状況を測定するには以下がよいらしい) sensor.setFilter(4); //0 to 4 is valid. Filter coefficient. See 3.4.4 sensor.setStandbyTime(0); //0 to 7 valid. Time between readings. See table 27. sensor.setTempOverSample(2); //0 to 16 are valid. 0 disables temp sensing. See table 24. sensor.setPressureOverSample(5); //0 to 16 are valid. 0 disables pressure sensing. See table 23. sensor.setHumidityOverSample(1); //0 to 16 are valid. 0 disables humidity sensing. See table 19. sensor.setMode(MODE_NORMAL); //MODE_SLEEP, MODE_FORCED, MODE_NORMAL is valid. See 3.3 } void SetupLCD() { lcd.begin(); lcd.command(0x38); lcd.command(0x39); lcd.command(0x14); lcd.command(0x73); // lcd.command(0x51); lcd.command(0x56); // 3.3V delay(2); lcd.command(0x6c); delay(300); lcd.command(0x38); delay(1); lcd.command(0x01); delay(2); lcd.command(0x0c); delay(2); } void DispLCD(float temp, float hum, float pres) { // LCDに表示 char tempbuff[8], humbuff[8], presbuff[8], buff[17]; dtostrf(temp, 4, 1, tempbuff); dtostrf(hum, 4, 1, humbuff); dtostrf(pres, 6, 1, presbuff); sprintf(buff,"%shPa",presbuff); lcd.setCursor(0, 0); // Col,Raw lcd.print(buff); sprintf(buff,"%s%cC %s%%",tempbuff,0xDF,humbuff); lcd.setCursor(2, 1); // Col,Raw lcd.print(buff); } void PostThingSpeak(char kind, float val) { // WiFi接続 WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { delay(100); } ThingSpeak.begin(client); // ThingSpeak初期化 switch (kind) { case 'T': // 気温 ThingSpeak.writeField(myChannelNumber, 1, val, myWriteAPIKey); // 1 は気温用 ThingSpeak_DelayCount = 0; // 送信用カウンタ初期化 break; case 'H': // 湿度 ThingSpeak.writeField(myChannelNumber, 2, val, myWriteAPIKey); // 2 は湿度用 break; case 'P': // 気圧 ThingSpeak.writeField(myChannelNumber, 3, val, myWriteAPIKey); // 3 は気圧用 break; } WiFi.disconnect(WIFI_OFF); } void PostMachinist(float temp, float humid, float pres) { const String api_key = "Bearer " + MachinistApi_key; HTTPClient http; String Json; Json = MkJson(temp, humid, pres); // JSONフォーマット作成 // WiFi接続 WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { delay(100); } http.begin(MachinistURL); http.addHeader("Content-Type", "application/json"); http.addHeader("Authorization", api_key); int http_rc = http.POST(Json); if (http_rc > 0) { String response = http.getString(); // Serial.println(http_rc); // Serial.println(response); } else { // Serial.print("Error on sending POST Request: "); // Serial.println(http_rc); } http.end(); WiFi.disconnect(WIFI_OFF); } String MkJson(float temp, float humid, float pres) { char buff[8]; String strbuff; String Json; dtostrf(temp, 4, 1, buff); strbuff = buff; Json = "{"; Json += "\"agent\": \"Home\","; Json += "\"metrics\": ["; Json += "{ "; Json += "\"name\" : \"temperature\","; Json += "\"namespace\": \"Environment Sensor\","; Json += "\"data_point\": { "; Json += "\"value\":" + strbuff; Json += "}"; Json += "},"; dtostrf(humid, 4, 1, buff); strbuff = buff; Json += "{ "; Json += "\"name\" : \"humidity\","; Json += "\"namespace\": \"Environment Sensor\","; Json += "\"data_point\": { "; Json += "\"value\":" + strbuff; Json += "}"; Json += "},"; dtostrf(pres, 6, 1, buff); strbuff = buff; Json += "{ "; Json += "\"name\" : \"Pressure\","; Json += "\"namespace\": \"Environment Sensor\","; Json += "\"data_point\": { "; Json += "\"value\":" + strbuff; Json += "}"; Json += "}"; Json += "]"; Json += "}"; return Json; } //--------------------------------------------------------------------