趣味の開発ノート

ITの学習やプログラミング・ノーコードアプリ開発のことなど。

【M5Stack】センサデータを時刻に合わせて一定間隔でSpreadsheetへ送信する

M5Stackでの電子工作の学習中。

こちらの続きで、一定間隔でセンサ値をデータベース(Spreadsheet)に送るような仕組みを作っていく。

nouka-it.hatenablog.com

以前のこちらの記事に載せたスケッチと組み合わせて、まずは毎分データをSpreadsheetへ送信するようにしてみた。

nouka-it.hatenablog.com

実装

Spreadsheetに送られたデータを確認

スケッチを書いて、1分ごとにセンサー値をSpreadsheetに送信してみた。
(最終的なスケッチは最後に記載している。)

Spreadsheetには、日時とセンサーデータが入るようにしている。

今回のスケッチは、まずloop()関数の中でdelay(1000)で1秒間待機させて毎分実行されるようにしてみた。
するとこんな感じでデータの取得には成功してるのだけど、毎回7秒ずつずれてデータが入ってきている。
11:53台は飛んでしまっている。

f:id:massa_potato:20220222115439p:plain

案の定、送信処理のたびに少しずつラグがあるようだ。
1秒間の待機の他の処理に時間がかかるので、1回のループで1分以上かかってくるのだろう。

時間のズレを調整

なので毎回のループをdelay(883)に修正してみる。 (60秒間で7秒のずれを解消したいので、7000ミリ秒/60 ≒ 116.66を1000ミリ秒から減算している。)

スケッチ書き込み後、改めてSpreadsheetを確認してみると、大きなラグはないけどやっぱり少しずつズレは生じているようだ。

f:id:massa_potato:20220222121443p:plain

このようにdelay()を調整するだけだと、ズレが出てきてしまうんだな。

ひとまず今回はここまで。

ここまでのスケッチ

▼毎分センサーデータをSpreadsheetに送信する(delay修正後)

#include <M5Stack.h>
#include <DHT.h>
#include <HTTPClient.h>
#include <WiFi.h>
#include <ArduinoJson.h>

#define WIFI_SSID "483FE9EB00D9-2G"
#define WIFI_PASSWORD "00000001"

// 関数のプロトタイプ宣言
static uint8_t conv2d(const char* p);
void sendValues();

// スプレッドシートへデータを飛ばすWebhook URL
const char *host = "https://script.google.com/macros/s/AKfycbyAcTUmJI_pDovJgI1Ol3-7MKFBGD7G2qqHofZrCVe0xk64U1pQIpTJvKMqTIldHKDQtA/exec";

// JSON設定
StaticJsonDocument<255> json_request;
char buffer[255];

// dht設定
const int PIN_DHT = 5;
DHT dht(PIN_DHT, DHT11);

// センサー値
float humidity;                // 湿度
float temperature;             // 温度
uint16_t moisture = 0;         // 土壌水分

// 時刻合わせ用変数
uint8_t hh = conv2d(__TIME__), mm = conv2d(__TIME__ + 3), ss = conv2d(__TIME__ + 6); // Get H, M, S from compile time
uint8_t omm = mm;


/* setup関数 */
void setup() {
  // M5の初期化
  M5.begin();
  dht.begin();
  
  // Wi-Fi接続
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  WiFi.setSleep(false); // btnA連打バグへの対応
  Serial.print("connectiong");
  while(WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println();

  // 接続に成功
  Serial.println("¥nWiFi Connected.");
  Serial.println(WiFi.localIP());

  M5.Lcd.fillScreen(TFT_BLACK);
  M5.Lcd.setTextSize(3);
  M5.Lcd.println("WiFi Connected:");
  M5.Lcd.println(WiFi.localIP());
  M5.Lcd.println("");
  
}


/* loop関数 */
void loop(){

  // 1秒進めて時間合わせ
  ss++; // 
  if(ss == 60){
    ss = 0;
    mm++;
    if(mm > 59){
      mm = 0;
      hh++;
      if(hh > 23){
        hh = 0;
      }  
    }        
  }
  
//  if(mm == 0 || mm == 30) {  // (※)毎時00分、30分の時に実行する場合はこのコメントアウトを外す
    if(mm != omm){  // 分の切り替わりを検知して一回だけこの中身が実行される

      omm = mm;     // 現在の分を保管

      // センサーの値を送る
      sendValues();
    }
//  }   // (※)毎時00分、30分の時に実行する場合はこのコメントアウトを外す


  humidity = dht.readHumidity();
  temperature = dht.readTemperature();  
  moisture = analogRead(36);

  M5.Lcd.clear(BLACK);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setTextSize(1);
  M5.Lcd.drawString("M5STACK SENSORS:", 0, 0, 4);

  M5.Lcd.setTextColor(YELLOW);
  M5.Lcd.drawString("Humidity:", 0, 30, 4);
  M5.Lcd.drawFloat(humidity, 0, 0, 60, 4);
  M5.Lcd.drawString("%", 50, 60, 4);
  
  M5.Lcd.setTextColor(GREEN);
  M5.Lcd.drawString("Temperature:", 0, 90, 4);
  M5.Lcd.drawFloat(temperature, 0, 0, 120, 4);
  M5.Lcd.drawString("C", 50, 120, 4);
 
  M5.Lcd.setTextColor(BLUE);
  M5.Lcd.drawString("Moisture:", 0, 150, 4);
  M5.Lcd.drawFloat(moisture, 0, 0, 180, 4);

  // 1秒間待機
  delay(883);

}


/* センサーの値送信 */
void sendValues(){
  json_request["humidity"] = humidity;
  json_request["temperature"] = temperature;
  json_request["moisture"] = moisture;
  serializeJson(json_request, buffer, sizeof(buffer));

  HTTPClient http;
  http.begin(host);
  http.addHeader("Content-Type", "application/json");
  int status_code = http.POST((uint8_t*)buffer, strlen(buffer));
  Serial.println(status_code);
  if(status_code > 0){
    if(status_code == HTTP_CODE_FOUND){
      String payload = http.getString();
      Serial.println(payload);

      M5.Lcd.setCursor(10, 100);
      M5.Lcd.fillScreen(BLACK);
      M5.Lcd.setTextColor(WHITE);
      M5.Lcd.setTextSize(3);
      M5.Lcd.println("Send Done!");
    }  
  } else {
    Serial.printf("[HTTP] GET... failed, error: %s¥n", http.errorToString(status_code).c_str());
  }
  http.end();

}


/* 時刻をミリ秒から時・分・秒へ変換 */
static uint8_t conv2d(const char* p) {
  uint8_t v = 0;
  if ('0' <= *p && *p <= '9')
    v = *p - '0';
  return 10 * v + *++p - '0';
}

今回のまとめ

定期的にSpreadsheetへの送信する処理はできたけど、delay()で時間管理するとどうしてもラグは発生する。

そもそも正確にやるならNTPサーバで時刻合わせしたりすべきだし、大した用途じゃなければこれでも良さそうだな、と考えている。

今後の改善点としては、

  1. 連続起動時間の検証 電源を繋いでいないと4時間くらいでデータが飛んで来なくなっている。
    画面が消えているので、スリープモードに入るのかバッテリーがもたないのか。この辺りは検証したい。

  2. 省電力な仕様に loop()関数をもうちょっと工夫したい。deepsleepというのも活用できるのかな? モバイルバッテリーの利用も検討しなければいけないかもしれない。