ESP32 網路時鐘:用 NodeMCU-32S 打造分秒不差的 NTP 原子鐘
告別手動對時!用 NodeMCU-32S 打造分秒不差的 WiFi 原子鐘。免 RTC模組、自動網路校正,搭配精美 OLED 介面。程式碼複製貼上即刻啟用,百元內享受永久精準的 IoT 工藝!
以前用 Arduino 做時鐘,總是要買一個額外的 RTC 模組 (如 DS3231) 還要裝水銀電池,時間久了還是會跑掉。
但既然我們用的是 NodeMCU-32S (ESP32),它天生就能連上網路!我們可以直接利用 NTP (Network Time Protocol) 技術,讓它自動連上網路時間伺服器校正時間。這意味著:它永遠不需要調整時間,而且分秒不差。
這篇教學將帶你製作一個精緻的桌面網路時鐘,並深入講解它背後的運作原理。
準備材料
- NodeMCU-32S 開發板
- 0.96 吋 OLED 顯示螢幕 (I2C 介面)
- 杜邦線
NodeMCU-32S 相容版本 ESP32開發板 WiFi 藍牙 可用Arduino IDE
安可信原廠貨! 全腳位引出,還保持迷你的身型,插上麵包後還能插杜邦線,真的是太棒了! 和NodeMCU V2幾乎一樣尺寸! 有5V供電輸出,非常方便! 有了ESP32開發板,真的可以忘記原來的那些Arduino板子了! 可以用Arduino IDE開發,但效能更強大,還內建WiFi 傑森實測記錄,大家可以到 F 粉 絲 團 B 看貼文哦! ESP32-D0WDQ6 內置兩個低功耗 Xtensa® 32-bit LX6 MCU。片上存儲包括: • 448 KB 的 ROM,用於程序啟動和內核功能

ESP32S擴展板 適用於Nodemcu-32s 38Pin全引出
※ 不含ESP32S開發板,需另購! ESP32S專用擴展板 適用於Nodemcu-32s,其它型號都不相容哦!請留意。 38Pin全引出,無敵方便!

0.96吋 OLED 128x64 低耗電 高解析 可顯示點陣圖 大勝LCD
尺寸:0.96吋 解析度:128x64 使用電源:3.3v~5v (實測3.3v即可) 顏色:上方1/4為黃色,以下為藍色 傑森實際,效果非常好! 送完整範例程式,一看就會。 大家可以利用LCDAssistant等程式,將點陣圖轉為Byte Array,即可顯示在螢幕上!

第一步:硬體接線
NodeMCU-32S 的 I2C 腳位是固定的,請依照下表連接:
| OLED 引腳 | NodeMCU-32S | 說明 |
| GND | GND | 接地 |
| VCC | 3V3或5V | 3.3或5V 電源 |
| SDA | GPIO 21 | I2C SDA |
| SCL | GPIO 22 | I2C SCL |
第二步:程式碼解析 (燒錄前必看)
在燒錄程式之前,有兩個關於 NTP 的運作機制,是新手最常感到困惑的,這裡特別說明一下:
1. 它是跟哪台主機對時的?
程式碼中設定的伺服器是 pool.ntp.org。
這不是「單一台」電腦,而是一個由全球志願者組成的伺服器叢集 (Pool)。當你的 ESP32 連線時,系統會自動將你導向到離你最近(例如台灣或亞洲區)且目前有空的主機。這保證了連線速度快且不會因為某台主機掛掉而失效。
2. 多久會校正一次時間?
這支程式設定每 1 秒 刷新一次螢幕,但這不代表它每秒都在連網。
ESP32 的運作邏輯是:
- 每 1 小時 (3600秒):ESP32 會在背景自動連上 NTP 伺服器,修正內部的時間誤差。
- 每 1 秒:程式讀取 ESP32 內部計時器顯示在螢幕上。這樣設計既省電,又能保證時間準確度(一小時內的誤差通常僅有幾毫秒,肉眼無法察覺)。
第三步:燒錄程式碼
請將以下程式碼複製到 Arduino IDE。
注意: 這次我們不需要安裝額外的時間函式庫,因為 ESP32 核心已經內建強大的 time.h。
👇 請修改這兩行即可:
ssid: 你的 WiFi 名稱password: 你的 WiFi 密碼
/*
* NodeMCU-32S NTP 網路時鐘 (防當機 & 介面優化版)
* 硬體:NodeMCU-32S (ESP32) + 0.96 OLED
* 接線:SDA -> GPIO 21, SCL -> GPIO 22
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <WiFi.h>
#include "time.h" // ESP32 內建強大的時間函式庫
// ================= 使用者設定區 =================
const char* ssid = "你的WiFi名稱";
const char* password = "你的WiFi密碼";
// NTP 伺服器設定 (使用全球叢集 pool.ntp.org)
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 28800; // 台灣時區 UTC+8 (8 * 3600 = 28800)
const int daylightOffset_sec = 0; // 台灣無日光節約時間
// ==============================================
// OLED 設定
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define SDA_PIN 21 // NodeMCU 標準 SDA
#define SCL_PIN 22 // NodeMCU 標準 SCL
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// 變數
unsigned long lastTime = 0;
unsigned long timerDelay = 1000; // 每秒更新螢幕
void setup() {
Serial.begin(115200);
delay(1000);
// 1. 初始化 OLED
Wire.begin(SDA_PIN, SCL_PIN);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("OLED 啟動失敗"));
for(;;);
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0, 10);
display.println(F("System Booting..."));
display.display();
// 2. WiFi 設定 (加入防當機參數)
WiFi.mode(WIFI_STA);
// [優化] 降低發射功率,避免 NodeMCU 因電流突波重啟
WiFi.setTxPower(WIFI_POWER_8_5dBm);
// [優化] 關閉省電模式,確保 NTP 校時順暢
WiFi.setSleep(false);
Serial.print("連線 WiFi 中");
WiFi.begin(ssid, password);
int limit = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
limit++;
if (limit > 60) ESP.restart(); // 超時重啟
}
Serial.println("\nWiFi 已連線!");
// 3. 啟動 NTP 校時 (關鍵步驟)
// 只要執行這行,ESP32 就會自動在背景每小時校正一次時間
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
// 等待第一次時間同步
struct tm timeinfo;
Serial.print("等待 NTP 同步");
while(!getLocalTime(&timeinfo)){
Serial.print(".");
delay(100);
}
Serial.println("\n時間同步完成!");
updateDisplay();
}
void loop() {
// 每秒更新一次畫面
if ((millis() - lastTime) > timerDelay) {
updateDisplay();
lastTime = millis();
}
}
// 介面繪圖函式
void updateDisplay() {
struct tm timeinfo;
// 再次確認時間是否有效
if(!getLocalTime(&timeinfo)){
return;
}
// 準備字串 (年-月-日, 時:分, :秒)
char dateStr[20];
char timeStr[10];
char secStr[5];
// 使用 strftime 格式化時間
strftime(dateStr, 20, "%Y-%m-%d", &timeinfo);
strftime(timeStr, 10, "%H:%M", &timeinfo);
strftime(secStr, 5, ":%S", &timeinfo);
display.clearDisplay();
// --- 區塊 1: 頂部日期列 (白底黑字) ---
display.fillRect(0, 0, 128, 16, SSD1306_WHITE);
display.setTextColor(SSD1306_BLACK, SSD1306_WHITE);
display.setTextSize(1);
// 自動置中演算法: (螢幕寬 - 文字寬) / 2
int dateLen = strlen(dateStr) * 6;
int dateX = (128 - dateLen) / 2;
display.setCursor(dateX, 4);
display.print(dateStr);
// --- 區塊 2: 主時間 (黑底白字) ---
display.setTextColor(SSD1306_WHITE);
// 顯示時:分 (大字體)
// 設定在最左邊 (x=0),為秒數騰出空間
display.setTextSize(3);
display.setCursor(0, 30);
display.print(timeStr);
// 顯示 :秒 (中字體)
// 緊接在時間後面 (x=90),避免換行爆版
display.setTextSize(2);
display.setCursor(90, 37);
display.print(secStr);
// --- 區塊 3: 底部星期 ---
char weekStr[10];
strftime(weekStr, 10, "%A", &timeinfo); // 取得星期全名
display.setTextSize(1);
int weekLen = strlen(weekStr) * 6;
int weekX = (128 - weekLen) / 2; // 一樣自動置中
display.setCursor(weekX, 56);
display.print(weekStr);
display.display();
}
成果展示與介面設計
為了在小小的 0.96 吋螢幕上塞入所有資訊且不顯得擁擠,我們在程式碼中做了精確的版面計算:

- 日期欄 (上方):採用「反白」設計(白底黑字),模仿手機的狀態列,視覺上有區隔感。
- 時間欄 (中間):
- 時與分:使用 Size 3 的超大字體,一目了然。
- 秒數:使用 Size 2 字體,並將座標精確設定在
(90, 37)。這是為了解決「爆版」問題,如果依照預設排列,秒數的個位數會被擠到下一行。
- 星期欄 (下方):自動置中顯示英文星期(如 Sunday),讓畫面平衡。
現在,只要把 NodeMCU-32S 接上 USB 電源,你就有一個永遠不需要對時、精準無比的桌面時鐘了!
NodeMCU-32S 相容版本 ESP32開發板 WiFi 藍牙 可用Arduino IDE
安可信原廠貨! 全腳位引出,還保持迷你的身型,插上麵包後還能插杜邦線,真的是太棒了! 和NodeMCU V2幾乎一樣尺寸! 有5V供電輸出,非常方便! 有了ESP32開發板,真的可以忘記原來的那些Arduino板子了! 可以用Arduino IDE開發,但效能更強大,還內建WiFi 傑森實測記錄,大家可以到 F 粉 絲 團 B 看貼文哦! ESP32-D0WDQ6 內置兩個低功耗 Xtensa® 32-bit LX6 MCU。片上存儲包括: • 448 KB 的 ROM,用於程序啟動和內核功能




