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

 昔作って頒布していたNTP時刻同期時計。まだ在庫あるのよね。展示出しても並べてもいないけどw

NTPで時刻同期する時計作ってみた #マッハ新書 - kinnekoの薄い本屋 - BOOTH
https://kinneko.booth.pm/items/1258594

 でも、無線関係のセキュリティが結構厳しくなって、WiFiの設定流し込みに使っていたSmartConfigが使えなくなっている。というわけで、在庫はみんな設定ができない。

 使っているESP32モジュールも、microBのものなので、最近のものとしてはイマイチすぎるよね。Type-Cに変換するような小さいモジュールも売っているみたいだけど、そこまではやらないにしても、WiFiの同期くらいはできるようにしておきたい。

 というわけで、何年かぶりでファームウエアを再構成することにした。

 さて、最近は、APモードで起動して、そこに一時的にスマホから接続して、Webを開いて設定して、設定終わるとAP切るみたいな方法が一般的のようだ。

 当時と違って、AIがあるので、あんまり考えないで前のコードを投げて新しいスケッチを書いてもらう。すんなり動くといいけど。
 と思ったら、当時のスケッチが残ってないw 薄い本から完全でないスケッチをコピーしてみたものの、使っていたTM1637Display.hはもうライブラリにないようだ。

 これだったかな?

 一回普通にベタ実装してみたのだけど、APに接続してパスワードを入れて、さらにブラウザを開いてIPアドレドを入力して、さらにそこからSSIDとPASSを入れるような感じがめんどくさい。
 よくみるようなやつで、AP接続されるとブラウザで設定画面が開いて、接続できると自動的にAPを終わるような簡単接続なやつがよさげ。

 それには、WiFiManagerやAutoConnectを使う必要があるのね。
 どちらのライブラリでも、DNSを全部自分(ESP32)に向けて、HTTPでどのURLに来ても自分の設定ページを返す感じになる。いずれにしても、ブラウザが開くかどうかは、OSのキャプティブポータルへの対応頼みか。

 今回は細かい設定や画面の変更はいらなさそうだし、シンプルなWiFiManagerにしておくか。

 ライブラリマネージャで検索すると、なんか、他にも使えそうなのがいっぱいあるけど、これかな? バージョンは2.0.17。入れる。

 スケッチはだいぶ簡素になった。けど、AIに書かせたスケッチは、いろいろ不具合があるので手動で修正。

 無線接続に失敗するので、ログも吐かせる。ログをみながら修正。

==== ESP32 Clock with WiFiManager / TM1637 ====
*wm:v2.0.17  D:2
[setup] WiFiManager autoConnect("ClockConfig")
*wm:AutoConnect 
*wm:No wifi saved, skipping 
*wm:AutoConnect: FAILED for  0 ms
*wm:StartAP with SSID:  ClockConfig
*wm:AP IP address: 192.168.4.1
*wm:Starting Web Portal 
*wm:6 networks found
*wm:Connecting to NEW AP: MyPlace
*wm:connectTimeout not set, ESP waitForConnectResult... 
*wm:Connect to new AP [SUCCESS] 
*wm:Got IP Address: 
*wm:192.168.0.18 
*wm:config portal exiting 
[setup] WiFiManager autoConnect OK
[setup] Connected SSID: MyPlace
[setup] IP address: 192.168.0.18
[synctime] start
[synctime] using NTP server: ntp.jst.mfeed.ad.jp
[synctime] synced: 2025-11-18 17:58:30
[synctime] disconnect WiFi

 なんとかうまく動いた。

 とりあえず、現状のスケッチ。

#include <WiFi.h>
#include <WiFiManager.h>     // tzapu WiFiManager
#include <TM1637Display.h>
#include "time.h"

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;

// NTP 同期(WiFi 接続済み前提)
void synctime() {
  Serial.println("[synctime] start");
  display.setSegments(SEG_SYNC);

  // すでに WiFi は接続済みという前提
  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");
  }

  Serial.println("[synctime] disconnect WiFi");
  WiFi.disconnect(true);   // 同期後は WiFi 切断
  WiFi.mode(WIFI_OFF);
}

// 時刻表示(コロン点滅&15:48:00で再同期)
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) {
    // ★ フラッシュする回:今まで通り2回描画してチカッとさせる
    display.showNumberDecEx(hm, (0x80 >> 0), true);
    delay(100);
    display.showNumberDecEx(hm, (0x80 >> 1), true);
  } else {
    // ★ フラッシュしない回:1回だけ普通に表示(コロン固定)
    display.showNumberDecEx(hm, (0x80 >> 0), true);
    delay(100);
  }

  // 毎日 15:48:00 に再同期(元ロジック)
  char hourminsec[7];
  sprintf(hourminsec, "%02d%02d%02d",
          timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
  if (atoi(hourminsec) == 154800) {
    Serial.println("[printTime] 15:48:00 reached, resync NTP");
    display.setSegments(SEG_SYNC);

    // 再同期時は一時的に WiFi を繋ぎ直す
    WiFi.mode(WIFI_STA);
    WiFi.begin();                 // 保存済み設定で接続
    int retry = 0;
    while (WiFi.status() != WL_CONNECTED && retry < 60) {
      delay(500);
      retry++;
      Serial.printf("[printTime] reconnecting WiFi... retry=%d status=%d\n",
                    retry, WiFi.status());
    }
    if (WiFi.status() == WL_CONNECTED) {
      Serial.println("[printTime] WiFi reconnected, call synctime()");
      synctime();
    } else {
      Serial.println("[printTime] WiFi reconnect FAILED");
      display.setSegments(SEG_ERR);
    }
  }
}

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

  pinMode(button, INPUT_PULLUP);

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

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

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

  Serial.println("[setup] WiFiManager autoConnect(\"ClockConfig\")");
  // 既存設定で接続を試し、ダメならClockConfigAPを立ち上げてポータルを開く
  bool res = wm.autoConnect("ClockConfig");
  delay(100);

  if (!res) {
    Serial.println("[setup] WiFiManager autoConnect FAILED");
    display.setSegments(SEG_ERR);
    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);
  synctime();           // 一度 NTP 同期して WiFi 切断
}

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

  if (flag == HIGH) {
    // ボタンが押されていないとき → 普通の時計表示
    btnCounter = BUTTON_LONGPRESS_COUNT;   // カウンタをリセット
    delay(400);
    printTime();
  } 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.mode(WIFI_STA);
        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);
        }

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

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