NTP時刻同期時計再び -4-

 -3-はえらく短くなってしまったが、他にネタを思いついたので続く。

 時計で1日に一回しか同期しないので、無線関係は完全に切って、消費電力を下げておくと良さそう。

 Bluetoothは使っていないので完全にオフにするとして、WiFiは設定のときと、1日一回の時刻同期の時だけ動作させるようにしておきたい。とりあえず、Arduinoからの制御はある程度できそうだ。

 WiFiの停止はこんな感じ。

#include <WiFi.h>
void setup() {
  // もしつながっていたら切断
  WiFi.disconnect(true);      // true で保存済みの設定も消す
  // モードを NULL (OFF) に
  WiFi.mode(WIFI_OFF);        // または WiFi.mode(WIFI_MODE_NULL);
}

 Bluetoothの停止はこんな感じ。

#include <WiFi.h>
extern "C" {
  #include "esp_bt.h"
}

void setup() {
  esp_bluedroid_disable();
  esp_bt_controller_disable();
}

 以下の方針でAIにスケッチを書きなおしてもらう。

  • 共通関数 wifiOff() を用意(必ず WiFi を切って WIFI_OFF)
  • NTP 用の一時接続関数 wifiReconnectForNtp() を用意
  • autoConnect() / startConfigPortal() 失敗時も必ず wifiOff() 呼び出し
  • 15:48 の再同期でも成功 / 失敗に関係なく最後に WiFi を OFF

 まぁ、いきなりうまくはできないよね... 動かない。

 初回、WiFiセット直後に時刻同期をするときにうまく動作せずsync表示で停止している。

 修正させる。  が、明後日の提案をしてきたので、軌道を修正する。  WiFiのオン・オフを独立の関数化して、初回やリセット後にはこの関数でオンにして、時刻同期後にオフにするように指示する。また、日次の時刻同期は別の関数にして、その中でオン・時刻同期・オフを行うように指示する。

 こんな方針を出してきた。

  • wifiOn() / wifiOff() を用意
  • 初回/リセット後は wifiOn() → WiFiManager → synctime() → wifiOff()
  • 日次同期は dailySyncIfNeeded() という別関数内でwifiOn() → WiFi.begin() → synctime() → wifiOff()
#include <WiFi.h>
#define WM_STRINGS_FILE "wm_strings_ja.h"
#include <WiFiManager.h>     // tzapu WiFiManager
#include <TM1637Display.h>
#include "time.h"

/* ESP32-DevKitC
const int button = 4;
const int CLK = 32;
const int DIO = 33; */

// NodeMCU-32S
const int button = 27;
const int CLK    = 25;
const int DIO    = 26;

TM1637Display display(CLK, DIO);
WiFiManager wm;

// NTP
const char* ntpServer          = "ntp.jst.mfeed.ad.jp";
const long  gmtOffset_sec      = 9 * 3600L;
const int   daylightOffset_sec = 0;

// 7セグ用メッセージパターン
const uint8_t SEG_PUSH[] = {
  SEG_A | SEG_B | SEG_F | SEG_E | SEG_G,         // P
  SEG_B | SEG_C | SEG_D | SEG_E | SEG_F,         // U
  SEG_A | SEG_C | SEG_D | SEG_F | SEG_G,         // S
  SEG_B | SEG_C | SEG_F | SEG_E | SEG_G,         // H
};

const uint8_t SEG_CONN[] = {
  SEG_D | SEG_E | SEG_G,                         // c
  SEG_C | SEG_D | SEG_E | SEG_G,                 // o
  SEG_C | SEG_E | SEG_G,                         // n
  SEG_C | SEG_E | SEG_G,                         // n
};

const uint8_t SEG_BAR[] = {
  SEG_G, SEG_G, SEG_G, SEG_G                     // ----
};

const uint8_t SEG_SYNC[] = {
  SEG_A | SEG_C | SEG_D | SEG_F | SEG_G,         // S
  SEG_B | SEG_C | SEG_D | SEG_F | SEG_G,         // y
  SEG_C | SEG_E | SEG_G,                         // n
  SEG_D | SEG_E | SEG_G,                         // c
};

const uint8_t SEG_ERR[] = {
  SEG_D,                                         // _
  SEG_A | SEG_D | SEG_E | SEG_F | SEG_G,         // E
  SEG_E | SEG_G,                                 // r
  SEG_E | SEG_G,                                 // r
};

// 長押ししきい値(ループ内 delay(10) と合わせて約2秒)
const int BUTTON_LONGPRESS_COUNT = 200;
int btnCounter = BUTTON_LONGPRESS_COUNT;

// ===== WiFi ON/OFF 共通関数 =====
void wifiOn() {
  Serial.println("[wifiOn] WiFi.mode(WIFI_STA)");
  WiFi.mode(WIFI_STA);
}

void wifiOff() {
  Serial.println("[wifiOff] WiFi.disconnect + WIFI_OFF");
  WiFi.disconnect(true);     // 保存済み設定は保持したまま切断
  WiFi.mode(WIFI_OFF);
}

// NTP 同期(WiFi 接続済み前提・ON/OFFは触らない)
void synctime() {
  Serial.println("[synctime] start");
  display.setSegments(SEG_SYNC);

  Serial.print("[synctime] using NTP server: ");
  Serial.println(ntpServer);
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  delay(1000);

  struct tm timeinfo;
  if (getLocalTime(&timeinfo)) {
    Serial.printf("[synctime] synced: %04d-%02d-%02d %02d:%02d:%02d\n",
                  timeinfo.tm_year + 1900,
                  timeinfo.tm_mon + 1,
                  timeinfo.tm_mday,
                  timeinfo.tm_hour,
                  timeinfo.tm_min,
                  timeinfo.tm_sec);
  } else {
    Serial.println("[synctime] getLocalTime FAILED");
    display.setSegments(SEG_ERR);
  }
}

// 時刻表示(コロン点滅)
// → 日次の再同期判定はここから分離して別関数にする
void printTime() {
  static bool flashPhase = false;   // 2回に1回フラッシュするためのフラグ
  
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("[printTime] getLocalTime FAILED");
    display.setSegments(SEG_ERR);
    return;
  }

  char hourmin[5];
  sprintf(hourmin, "%02d%02d", timeinfo.tm_hour, timeinfo.tm_min);
  int hm = atoi(hourmin);

  // コロン位置を変えて点滅っぽく
  flashPhase = !flashPhase;
  
  if (flashPhase) {
    display.showNumberDecEx(hm, (0x80 >> 0), true);
    delay(100);
    display.showNumberDecEx(hm, (0x80 >> 1), true);
  } else {
    display.showNumberDecEx(hm, (0x80 >> 0), true);
    delay(100);
  }
}

// 日次の時刻同期(15:48:00)
// この中で WiFi オン・接続・同期・オフを完結させる
void dailySyncIfNeeded() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("[dailySyncIfNeeded] getLocalTime FAILED");
    return;
  }

  char hourminsec[7];
  sprintf(hourminsec, "%02d%02d%02d",
          timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);

  if (atoi(hourminsec) == 154800) {
    Serial.println("[dailySyncIfNeeded] 15:48:00 reached, resync NTP");
    display.setSegments(SEG_SYNC);

    // WiFi オン(STA モード)
    wifiOn();

    // 保存済みの設定で接続
    WiFi.begin();                 // WiFiManager が保存した設定を利用
    int retry = 0;
    while (WiFi.status() != WL_CONNECTED && retry < 60) {
      delay(500);
      retry++;
      Serial.printf("[dailySyncIfNeeded] reconnecting WiFi... retry=%d status=%d\n",
                    retry, WiFi.status());
    }

    if (WiFi.status() == WL_CONNECTED) {
      Serial.println("[dailySyncIfNeeded] WiFi reconnected, call synctime()");
      synctime();
    } else {
      Serial.println("[dailySyncIfNeeded] WiFi reconnect FAILED");
      display.setSegments(SEG_ERR);
    }

    // 同期の成否にかかわらずここでオフ
    wifiOff();
  }
}

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println();
  Serial.println("==== ESP32 Clock with WiFiManager / TM1637 ====");

  Serial.print("WM_LANGUAGE = ");
  Serial.println(FPSTR(WM_LANGUAGE));

  pinMode(button, INPUT_PULLUP);

  display.clear();
  display.setBrightness(0);
  display.showNumberDec(0, true);

  // WiFiManager のデバッグ出力もシリアルに出す(デフォルト true だが明示)
  wm.setDebugOutput(true);

  // ポータル待ち中の表示
  display.setSegments(SEG_BAR);

  Serial.println("[setup] WiFiManager autoConnect(\"ClockConfig\")");

  // 初回:WiFi ON → WiFiManager → NTP 同期 → OFF
  wifiOn();
  bool res = wm.autoConnect("ClockConfig");
  delay(100);

  if (!res) {
    Serial.println("[setup] WiFiManager autoConnect FAILED");
    display.setSegments(SEG_ERR);
    wifiOff();   // 失敗時も OFF に戻す
    return;
  }

  Serial.println("[setup] WiFiManager autoConnect OK");
  Serial.print("[setup] Connected SSID: ");
  Serial.println(WiFi.SSID());
  Serial.print("[setup] IP address: ");
  Serial.println(WiFi.localIP());

  // WiFi 接続成功
  display.setSegments(SEG_CONN);
  delay(1000);

  // WiFi はすでに繋がっているので、そのまま時刻同期だけ
  synctime();

  // 同期後は WiFi を OFF
  wifiOff();
}

void loop() {
  int flag = digitalRead(button);

  if (flag == HIGH) {
    // ボタンが押されていないとき → 普通の時計表示
    btnCounter = BUTTON_LONGPRESS_COUNT;   // カウンタをリセット
    delay(400);
    printTime();
    dailySyncIfNeeded();                  // 日次同期チェック
  } else {
    // ボタン押下中:カウントダウン表示
    if (btnCounter > 0) {
      btnCounter--;
      display.showNumberDec(btnCounter, true);
      delay(10);

      // カウントダウン完了 → 長押しと判定して WiFi 設定リセット&ポータル
      if (btnCounter == 0) {
        Serial.println("[loop] long press detected, reset WiFi settings");
        // WiFi情報リセット
        wm.resetSettings();

        // 長押し中表示
        display.setSegments(SEG_PUSH);
        delay(1000);
        display.setSegments(SEG_BAR);

        // リセット後も:WiFi ON → ConfigPortal → NTP → OFF
        wifiOn();
        Serial.println("[loop] WiFiManager startConfigPortal(\"ClockConfig\")");
        bool res = wm.startConfigPortal("ClockConfig");

        if (res) {
          Serial.println("[loop] WiFiManager startConfigPortal OK");
          Serial.print("[loop] Connected SSID: ");
          Serial.println(WiFi.SSID());
          Serial.print("[loop] IP address: ");
          Serial.println(WiFi.localIP());

          display.setSegments(SEG_CONN);
          synctime();
        } else {
          Serial.println("[loop] WiFiManager startConfigPortal FAILED");
          display.setSegments(SEG_ERR);
        }

        // 終了後は必ず OFF
        wifiOff();

        // 終了後は次にボタンを離したときにリセットされる
      }
    }
  }
}

 そういえば、この実装になってから、「conn」表示が一瞬で見えていないな... わりとconn好きなんだけど。手動で1秒待ち入れた。

 これで問題なさそうだけど、電力消費はどうかな?
 前に、簡単に測るやつ買っておいたような。100均で買ったmicroBの変換もあったな。

 測ってみたけど、あまりかかってない感じねw こんな安いシンプルなのじゃ計れないレベルの500mA以下だw もっと細かく測れる機材もあったのだけど、埋もれて出てこないw

 これで終わりかな。

 前の実装で、もう連続何年かわからないくらい動いている時計が2台あるのだけど、今年になって、たまにLEDの輝度ムラが出てきたり、なんか時刻同期に失敗したり、明らかにハングして停止していることが何回かあった。さすがにいろいろ経年劣化するのだろうな。そのうち、そっちもファームを焼いたり、7セグの交換とかしてみるかな。

 パーツは同時期のものなのだけど、今回焼いたものは、LEDもくっきりとしているのでいい感じだ。


オリジナル投稿: NTP時刻同期時計再び -4-|kinneko|pixivFANBOX
https://www.fanbox.cc/@kinneko/posts/10930601