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;
}
//--------------------------------------------------------------------










