Imagine never having to set your clock again, even after a power outage or a daylight saving time change. In this project, we are going to build exactly that: a fully automated, internet-synced digital clock using an ESP32 and a TM1637 4-digit 7-segment display. By syncing with online time servers, it will keep the time accurate down to the second.
Let’s get started!
Things You Will Need
For this project, you will need the following items:
- ESP32 development board
- TM1637 4-digit display module
- Jumper wires
- USB cable compatible with your ESP32 board
- Computer for uploading the program
- Breadboard (optional, for easier testing and wiring)
Project Overview
We start by connecting the ESP32 to your local Wi-Fi network. Once it is online, it contacts a highly accurate NTP server over the User Datagram Protocol (UDP) on port 123 to fetch the current time. It sends a request packet to the server, which then responds with a timestamp packet. The ESP32 reads the current time from that response and applies your regional time zone and daylight saving rules so the raw global time is converted into the correct local time.

After establishing the correct local time, the ESP32 stores this information and begins tracking the time internally. It does not keep asking the NTP server for the time every second. Instead, it uses its own internal timer to keep time once the initial synchronization is complete.
Finally, it updates the TM1637 display every second to show the current time, while blinking the center colon to indicate that the clock is actively running.
Suggested Reading
Before you move forward with this project, it’s a good idea to check out the following tutorials. They’ll help you understand the basics of how to use TM1637 4-digit display with ESP32 and how to get time from the internet using an NTP server.
Wiring the TM1637 Module to an ESP32
Now it’s time to connect the TM1637 Module to the ESP32.
Start by connecting the VCC pin of the TM1637 module to the VIN pin of the ESP32. Then connect the GND pin of the module to a GND pin on the ESP32. After that, connect the CLK pin of the module to GPIO 26 on the ESP32, and connect the DIO pin of the module to GPIO 25.
Here’s a quick reference table for the pin connections:
| TM1637 Module | ESP32 | |
| VCC | VIN | |
| GND | GND | |
| DIO | 25 | |
| CLK | 26 |
This diagram shows you exactly how to connect everything:

Setting Up the Arduino IDE
We will be using the Arduino IDE to program the ESP32, so please ensure you have the ESP32 add-on installed before you proceed:
Library Installation
To communicate with the TM1637 chip, you will need to use a library. A great option is the TM1637Display library created by Avishay Orpaz. This library has many built-in functions that make controlling the display simple and straightforward.
To install the library,
- First open your Arduino IDE program. Then click on the Library Manager icon on the left sidebar.
- Type “tm1637” in the search box to filter your results.
- Look for the TM1637 library by Avishay Orpaz.
- Click the Install button to add it to your Arduino IDE.

Once the library is installed, you’re all set and ready for programming!
Uploading the Code
Copy the code below to your Arduino IDE. But before you upload the code to your ESP32, there are a few important changes you need to make to ensure everything works correctly for your setup.
- First, update the following two variables with your network credentials so that the ESP32 can connect to your network.
// Replace with your network credentials const char* ssid = "YOUR_SSID"; const char* password = "YOUR_PASS"; - Next, you need to change the timezone string to match your region. You can find a list of timezone strings on this GitHub resource.
// Time zone string // Example below is for US Eastern Time with automatic daylight saving // Change this for your region const char* tzInfo = "EST5EDT,M3.2.0,M11.1.0";
After making these changes, go ahead and upload the code.
#include <WiFi.h>
#include <time.h>
#include <TM1637Display.h>
// Replace with your network credentials
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASS";
// NTP servers used to get the current time from the internet
const char* ntpServer1 = "pool.ntp.org";
const char* ntpServer2 = "time.nist.gov";
// Time zone string
// Example below is for US Eastern Time with automatic daylight saving
// Change this for your region
// See list of timezone strings https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
const char* tzInfo = "EST5EDT,M3.2.0,M11.1.0";
// ESP32 pins connected to the TM1637 module
#define CLK 26
#define DIO 25
// Create the display object
TM1637Display display(CLK, DIO);
void connectWiFi() {
// Put Wi-Fi in station mode so ESP32 connects to a router
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
// Wait until Wi-Fi connection is established
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
}
void syncTime() {
// Start NTP using the two servers above
configTime(0, 0, ntpServer1, ntpServer2);
// Set the timezone for your region
setenv("TZ", tzInfo, 1);
tzset();
// Wait until a valid time is received from the NTP server
// 1577836800 is the Unix time for Jan 1, 2020
time_t now = 0;
while (time(&now) < 1577836800) {
delay(500);
}
}
void displayTime() {
static bool colonOn = false; // Keeps track of colon state between calls
colonOn = !colonOn; // Toggle colon each second for blinking effect
struct tm timeinfo;
if (!getLocalTime(&timeinfo, 2000)) {
display.clear(); // Clear display if time is not available
return;
}
int hours = timeinfo.tm_hour; // Current hour (0 to 23)
int minutes = timeinfo.tm_min; // Current minute (0 to 59)
// Combine hours and minutes into a 4-digit number: HHMM
// Example: 9:05 becomes 905, and leading zeros will show it as 0905
int value = hours * 100 + minutes;
// Bit mask to control the center colon on the TM1637 display
uint8_t colonMask = colonOn ? 0b01000000 : 0;
display.showNumberDecEx(
value,
colonMask, // Show or hide colon
true // Keep leading zeros, so 06:04 displays correctly
);
}
void setup() {
// Set display brightness from 0 to 7
display.setBrightness(5);
display.clear();
// Connect to Wi-Fi and get the current time
connectWiFi();
syncTime();
}
void loop() {
// Update the clock display every second
displayTime();
delay(1000);
}Demonstration
After uploading the sketch, press the EN (reset) button on your ESP32. If everything is set up correctly, you should see the current time displayed on the TM1637 display every second.

For verification, you can compare the output with the current EST time from a Google search. In the screenshot below, the time on the TM1637 display matches the online time result, confirming that the ESP32 has successfully synchronized with the NTP server and is keeping accurate local time.

Code Explanation
The code starts by including three important libraries. The WiFi.h library helps the ESP32 connect to Wi-Fi. The time.h library allows the ESP32 to communicate with the NTP server and format the time into a readable format. The TM1637Display.h library helps control the TM1637 4-digit display module.
#include <WiFi.h>
#include <time.h>
#include <TM1637Display.h>Next, we define two variables for your Wi-Fi connection: one for the network name (SSID) and one for the password. This step is very important because the ESP32 needs internet access to reach the NTP server and get the correct time. That’s why you had to replace the placeholder text with your actual network credentials earlier.
// Replace with your network credentials
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASS";Then we set up two different NTP servers, pool.ntp.org and time.nist.gov. These tell our ESP32 exactly where on the internet it should look to find the correct time.
// NTP servers used to get the current time from the internet
const char* ntpServer1 = "pool.ntp.org";
const char* ntpServer2 = "time.nist.gov";We use “pool.ntp.org”, which is actually a massive global cluster of time servers, as our primary timekeeper. We’ve also added “time.nist.gov” alongside it as a highly reliable backup. We provide two distinct servers so that if one happens to go offline or becomes temporarily unreachable, our clock can seamlessly ask the second one and keep right on ticking.
If you find that a particular server is blocked on your network or you simply prefer a regional one, you can easily swap these web addresses out for local ones that work better for your specific setup.
Once our servers are defined, we then need to tell the ESP32 how to adjust that raw global internet time to match our local time. We do this by setting up a timezone information string in tzInfo.
In this example, the timezone is configured for US Eastern Time with automatic daylight saving adjustment. If you want the clock to show the correct time for your location, you can change this timezone string to match your region. You can find a list of timezone strings on this GitHub resource.
// Time zone string
// Example below is for US Eastern Time with automatic daylight saving
// Change this for your region
const char* tzInfo = "EST5EDT,M3.2.0,M11.1.0";Then we define the ESP32 pins connected to the TM1637 display: CLK is set to GPIO 26 and DIO is set to GPIO 25.
// ESP32 pins connected to the TM1637 module
#define CLK 26
#define DIO 25Next, we create a display object from the TM1637Display class. This object lets us send commands to the TM1637. When we create the object, we have to tell it which two pins we are using.
// Create the display object
TM1637Display display(CLK, DIO);The connectWiFi() function is responsible for connecting the ESP32 to the Wi-Fi network. We first put the ESP32 into station mode using WiFi.mode(WIFI_STA). That tells the ESP32 to connect to an existing Wi-Fi network instead of creating its own. Then we start the connection process using WiFi.begin() function. After that, we keep checking the Wi-Fi status inside a loop. As long as the board is not connected yet, we wait for half a second and check again.
void connectWiFi() {
// Put Wi-Fi in station mode so ESP32 connects to a router
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
// Wait until Wi-Fi connection is established
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
}The syncTime() function takes care of getting the correct current time. We first call configTime(0, 0, ntpServer1, ntpServer2) to start NTP time synchronization using the two servers we defined earlier. The two zeros mean we are not applying a fixed UTC offset here, because timezone handling is done separately. Then we set the timezone environment variable with setenv("TZ", tzInfo, 1) and apply it using tzset(). That makes sure the ESP32 knows how to interpret local time properly.
To make sure we actually have a valid time downloaded before moving forward, we check the current internal time against a known past date; in this case, the Unix timestamp for January 1st, 2020. If our internal time is older than that, we know the internet sync hasn’t finished yet, so we wait a moment and keep checking until it updates.
void syncTime() {
// Start NTP using the two servers above
configTime(0, 0, ntpServer1, ntpServer2);
// Set the timezone for your region
setenv("TZ", tzInfo, 1);
tzset();
// Wait until a valid time is received from the NTP server
// 1577836800 is the Unix time for Jan 1, 2020
time_t now = 0;
while (time(&now) < 1577836800) {
delay(500);
}
}The core of our clock logic lives in our displayTime() function. We first create a static bool variable called colonOn to keep track of our colon’s state; every time this function runs, we toggle its state, so we get a nice blinking effect every second.
We then ask the ESP32 to fill our time structure with the current local time. If for some reason we cannot get it, we just clear the display and exit the function so we do not show garbage data.
Assuming we get the time successfully, we pull out the current hour and minute. To show this on our screen, we combine these two numbers into a single four-digit value by multiplying the hours by one hundred and adding the minutes. For example, if it is 9:05, this math turns it into 905. We then create a specific bitmask that turns the physical center colon on the display on or off based on our toggle state.
Finally, we push this combined value and the colon state to the display. We make sure we tell the display to keep leading zeros so that times like 6:04 display completely across all four digits.
void displayTime() {
static bool colonOn = false; // Keeps track of colon state between calls
colonOn = !colonOn; // Toggle colon each second for blinking effect
struct tm timeinfo;
if (!getLocalTime(&timeinfo, 2000)) {
display.clear(); // Clear display if time is not available
return;
}
int hours = timeinfo.tm_hour; // Current hour (0 to 23)
int minutes = timeinfo.tm_min; // Current minute (0 to 59)
// Combine hours and minutes into a 4-digit number: HHMM
// Example: 9:05 becomes 905, and leading zeros will show it as 0905
int value = hours * 100 + minutes;
// Bit mask to control the center colon on the TM1637 display
uint8_t colonMask = colonOn ? 0b01000000 : 0;
display.showNumberDecEx(
value,
colonMask, // Show or hide colon
true // Keep leading zeros, so 06:04 displays correctly
);
}In the setup section, we prepare everything once when the board starts. We first set the display brightness to 5 using the setBrightness() function. Since the valid range is 0 to 7, this gives a medium brightness level. Then we clear the display so it starts blank. After that, we call connectWiFi() to join the network, and then we call syncTime() to fetch the current time from the internet.
void setup() {
// Set display brightness from 0 to 7
display.setBrightness(5);
display.clear();
// Connect to Wi-Fi and get the current time
connectWiFi();
syncTime();
}Finally, in the loop section, we simply update the time on the display every second.
void loop() {
// Update the clock display every second
displayTime();
delay(1000);
}



