2022年2月6日日曜日

ESP32のRTCをオフラインでブラウザから設定する。

 ESP32をSoftAPとかでオフラインで使用する場合に内蔵のRTCの時間を簡単に設定できないか試してみた。IoT系マイコンなのでネットに接続されていてNTPから時刻を取得するのは簡単なんだけど、オフラインの場合は外付けRTCを接続して電池で保持するのが定番なのかな。

その昔WebページにJavascriptでリアルタイム時刻を表示していた時代も有ったなーというところからの発想を得て、パソコンやスマホからESP32にブラウザを使ってアクセスしてJavascriptから時刻をESP32に送ってやれないかなと。

ということで早速作ってみた。ページを開くたびに問答無用で時間セットするサンプル。

#include <WiFi.h>
#include <WebServer.h>
#include "time.h"
#include "esp_mac.h"  //MACアドレス取得用

WebServer server(80);

const char INDEX_HTML[] =
  "<!DOCTYPE HTML>"
  "<html>"
  "<head>"
  "<link rel=\"icon\" href=\"data:,\">"
  "<title>Timeset</title>"
  "</head>"
  "<script type=\"text/javascript\">\n"
  "var xhr = new XMLHttpRequest();\n"
  "xhr.open('POST', './settime', true);\n"
  "xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');\n"
  "const dtime = new Date().getTime() / 1000.0;\n"
  "xhr.send(\"time=\" + dtime);\n"
  "window.onload = function(){\n"
  "document.getElementById(\"textbox\").value = dtime;\n"
  "}\n"
  "</script>\n"
  "<body>"
  "<p>Unix Time</p><br>"
  "<input type=\"text\" id=\"textbox\">"
  "</body>"
  "</html>";

void setup() {
  uint8_t macaddr[6];
  esp_read_mac(macaddr, ESP_MAC_WIFI_SOFTAP);  //WiFi Macアドレス読み込み
  char SSID[14];
  sprintf(SSID, "ESP32-%02X%02X", macaddr[4], macaddr[5]);
  WiFi.softAP(SSID);  //SoftAPを起動
  delay(100);

  server.enableCORS();  //CORS対策 (JavaScriptデバッグ用)
  server.on("/", []() {
    server.send(200, "text/html", INDEX_HTML);
  });
  server.on("/settime", []() {
    if (server.args() > 0) {
      timeval epoch = { server.arg(0).toInt(), 0 };//構造体に格納
      settimeofday(&epoch, NULL);//RTCに時間をセット
    }
    server.send(204);
  });
  server.begin();
  setenv("TZ", "JST-9", 1);  //日本標準時に設定

  xTaskCreatePinnedToCore(task, "task", 4 * 1024, NULL, 5, NULL, 0);  //別タスク起動
}

void loop() {
  server.handleClient();
}

void task(void *pvParameters) {
  Serial.begin(115200);
  while (1) {
    static unsigned long lastUpdate = 0;
    if (lastUpdate + 1000 <= millis()) {
      lastUpdate = millis();  //次の実行のために時間更新//ms周期
      struct tm timeinfo;
      if (!getLocalTime(&timeinfo)) {
        Serial.println("Failed to obtain time");
      } else {
        Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
      }
    }
    delay(1);  //WDT対策
  }
  vTaskDelete(NULL);
}

ESP32でSoftAPでアクセスポイントを作ったらWebサーバーを立てて、それにスマホやPCのブラウザからアクセスするとシステム時刻をJavaScriptで取得してESP32にPOSTで投げるという荒業。もちろん2038年問題対策はされていない。

getLocalTimeがfalseの場合、処理に時間がかかるのでWebサーバーをブロッキングしないように無駄にマルチタスクでシリアルモニタを実装。

ページを読み込んだ瞬間にJavaScriptから時間を取得してPOSTで投げるのでロードにかかった分とかは時間がずれる。でもシリアルコンソールの時間表示と比べてみる限り1秒もずれていなかった。PC版ChromeとiOSでは動作確認できた。

外付けRTCで電池の心配しなくても良いので(ESP32の内蔵RTCがどのぐらいでずれていくのかわからないけど)簡易的な用途だったらこれで十分かも。ちなみに内蔵RTCはそこそこ制度が悪いので日をまたいだりするとかなりずれる。そんなときは外付けクリスタルにも対応しているらしいので精度が必要であれば32kHzのクリスタルを外付けすれば良さそう。

0 件のコメント:

コメントを投稿