昔作って頒布していた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



