📝

Arduino(4) POST通信で時刻合わせ

2021/07/23に公開

はじめに

せっかくArduinoにてRTCを使用して時刻表示が出来たが、時刻合わせをする手段が無いので、時計として役立たずなものとなっているので、外部から時刻合わせ出来る様作り込んでみる。

とりあえず確認

POSTで日時データを投げたら、Arduino側でRTCに設定したいので、まずパソコンから送信した電文を正しく受けれるかを確認。

//-----------------------------------------------
//  受信した内容をそのまま表示する関数
//-----------------------------------------------
void lanRx()
{
  byte c;
  String line = "" ;
  
  rxBufferIndex = 0;
  EthernetClient client = server.available();
  if (client) {
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        line = client.readStringUntil('\n');
        int indexTime = line.indexOf("time");
        if (indexTime != -1) {
          Serial.println(line.substring(indexTime + 7,indexTime + 7 + 8));
        }
        int indexDate = line.indexOf("date");
        if (indexDate != -1) {
          Serial.println(line.substring(indexDate + 7,indexDate + 7 + 8));
        }
      }
    }
  }
  delay(1);
  // close the connection:
  client.stop();  
}

受信した電文をの中から「time」「date」といった文字列を検索して、その後の文字を取得する関数。
パソコンからは以下でPOSTを送信してみた。
(wsl2環境で実施)

 curl -X POST -H "Content-Type: application/json" -d '{"time":"18:50:00", "date":"21/07/20"}' 192.168.1.177:80

シリアルモニタの結果

start
RTC has set the system time
18:50:00
21/07/20

かなり力づくですが、パソコンから送られた時間を取得する事が出来ました。
後は、この値をRTCに設定すれば外部からの時刻合わせ完了の予定。

実装説明

ここで気づいたが、スケッチ例に「SetSerial」なるサンプルがある。これはシリアル通信で時刻合わせ出来るみたい。これの時刻合わせを行っているloop関数を丸コピーして、LANで取得した値を代入するよう変更した。
上記のlanRx関数を書き換え。

実装

//-----------------------------------------------
//  受信した値をRTCにセットする
//-----------------------------------------------
void lanRx()
{
  byte c;
  String line = "" ;
  time_t t;
  tmElements_t tm;
  int getTimeCount = 0;
    
  EthernetClient client = server.available();
  if (client) {
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        line = client.readStringUntil('\n');
        int indexTime = line.indexOf("date");
        if (indexTime != -1) {
          Serial.println(line.substring(indexTime + 7,indexTime + 7 + 8));
         
          tm.Year  = y2kYearToTm((line.substring(indexTime + 7, indexTime + 7 + 2)).toInt());
          tm.Month = (line.substring(indexTime + 7 + 3, indexTime + 7 + 3 + 2)).toInt();
          tm.Day   = (line.substring(indexTime + 7 + 6, indexTime + 7 + 6 + 2)).toInt();
          getTimeCount++;
        }
        int indexDate = line.indexOf("time");
        if (indexDate != -1) {
          Serial.println(line.substring(indexDate + 7,indexDate + 7 + 8));
          tm.Hour = (line.substring(indexTime + 7, indexTime + 7 + 2)).toInt();
          tm.Minute = (line.substring(indexTime + 7 + 3, indexTime + 7 + 3 + 2)).toInt();
          tm.Second = (line.substring(indexTime + 7 + 6, indexTime + 7 + 6 + 2)).toInt();
          getTimeCount++;
        }
      }
    }
    if(getTimeCount == 2)
    {
        t = makeTime(tm);
        RTC.set(t);
        setTime(t);
    }
  }
  delay(1);
  // close the connection:
  client.stop();  
}

LANのコネクションを切った瞬間にRTC設定が変わって、表示も更新された。
完成だけど、丸コピーしているので、意味不明な関数が幾つかある。
取得した文字列から位置で時分秒の文字を切り出しているので、せっかくJSON形式で受信しているのに、生かせていない。次にJSON形式にて受信する実装を行ってみる。

JSON形式で受信

JSONに対応するにはライブラリを入れる必要がある。
「ツール」→「ライブラリを管理」より「JSON」といった文言で検索するとArduinoJSONというそれっぽいものが見つかる。

こいつをインストールして、
ソースの最初にインクルードを追加する

#include <ArduinoJson.h>

実際のソースは上記のlanRxを書き直して、

//-----------------------------------------------
//  受信した値をRTCにセットする
//-----------------------------------------------
void lanRx()
{
  byte c;
  String line = "" ;
  time_t t;
  tmElements_t tm;
  int getTimeCount = 0;

  const char* sensor;
  char* time;
  char* date;
  double latitude;
  double longitude;
  
  EthernetClient client = server.available();
  if (client) {
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        line = client.readStringUntil('\n');

        StaticJsonDocument<200> doc;
        DeserializationError err = deserializeJson(doc, line);
        time = doc["time"];
        date = doc["date"];
        
        if(strlen(date) == 8)
        {
          String Data = date;
          Serial.println(Data);
          tm.Year  = y2kYearToTm((Data.substring(0, 2)).toInt());
          tm.Month = (Data.substring(3, 3 + 2)).toInt();
          tm.Day   = (Data.substring(6, 6 + 2)).toInt();
          getTimeCount++;
        }
        if(strlen( time) == 8)
        {
          String Data = time;
          Serial.println(Data);
          tm.Hour = (Data.substring(0, 2)).toInt();
          tm.Minute = (Data.substring(3, 3 + 2)).toInt();
          tm.Second = (Data.substring(6, 6 + 2)).toInt();
          getTimeCount++;
        }
      }
    }
    if(getTimeCount == 2)
    {
        t = makeTime(tm);
        RTC.set(t);
        setTime(t);
    }
  }
  delay(1);
  // close the connection:
  client.stop();  
}

結局、文字列からの切り出しは文字位置で行っているので変わらない気もするが…。
将来的に時刻合わせだけでなく、他のコマンドで情報が投げやすくなったと思うので、このまま進める。

Discussion