A clock that sets itself
An ESP32, a clock panel and four wires. The time comes from the internet, the colon blinks once a second, and the display shows four dashes until the time is real — which matters more than it sounds.
What the sketch is doing
Three things, and only one of them is about the display.
The ESP32 has no battery-backed clock, so it wakes up in 1970 every time. It joins your network, asks a time server what time it is, and the C library keeps count from there.
configTzTime takes a POSIX timezone string rather than an offset in seconds.
The string carries the daylight-saving rules, so the clock changes itself in the
right week without you reflashing anything in October.
Then the display: hours × 100 + minutes is the number, and the colon is one bit.
The face
0b01000000 lights the colon between them. Drag the slider through midnight with leading zeros off to see why they are on.Drag through midnight with leading zeros off and you can watch both failures
happen: 00:05 arrives as the number 5 and prints as a bare 5 with a stray colon,
and 00:00 loses the colon entirely. With leading zeros on, neither can happen —
which is why the sketch passes true.
Twelve-hour time is the opposite: the hour is never 0, and leading zeros off gives you 9:05 rather than 09:05.
int h = t.tm_hour % 12; if (h == 0) h = 12;
display.showNumberDecEx(h * 100 + t.tm_min, COLON, false);Why the colon blinks
Partly because clocks do. Mostly because it is the only thing on a finished clock that says the sketch is still running.
A display holds its four bytes with no help from your board. If the ESP32 crashes, loses Wi-Fi in a way that blocks, or sits in a watchdog reset loop, the time on the panel simply stops changing — and a clock showing 14:32 looks exactly as convincing at half past three. A colon that blinks once a second is a heartbeat you can see from the doorway.
The dashes do the same job at the other end. Until the year is past 2020 the sketch refuses to show a time at all, because a confidently displayed 00:00 that happens to be 1970 is worse than an obviously blank display.
Making it live somewhere
Two changes turn this from a sketch into a clock on a shelf.
Drop the brightness at night — read t.tm_hour and call setBrightness(0)
after nine and setBrightness(5) in the morning. Remember that it is sent with
the next write, which here is 200 ms away.
And power it from a USB charger rather than a computer. Nothing in the sketch needs the serial port after the IP address is printed, and an ESP32 with one panel draws well under what a phone charger supplies.
The code
A 24-hour clock on an ESP32. Put your network name and password at the top, and your own timezone string — the one here is Melbourne, and it carries the daylight-saving dates so the clock changes itself in the right week.
/*
TM1637 4-Digit Display - a clock that sets itself TK51 / /p/tk51
An ESP32 and a clock panel. The time comes over Wi-Fi from NTP.
Wiring. No pad on this board is square, so go by the names
printed on the back: GND, VCC, clock, data. Seen from the
display side with the header on the left, GND is the top pin.
GND -> GND
VCC -> 3V3. Never 5V beside a 3.3 V board: the pull-ups
would put 5 V on your pins.
CLOCK -> GPIO 18
DATA -> GPIO 19
Arduino IDE
Tools > Board ESP32 Dev Module
Tools > Port the one that appears when you plug in
Library: "TM1637" by Avishay Orpaz, from Tools > Manage
Libraries. WiFi and time.h come with the ESP32 core.
Serial Monitor at 115200.
*/
#include <WiFi.h>
#include <time.h>
#include <TM1637Display.h>
#define CLOCK_PIN 18
#define DATA_PIN 19
#define COLON 0b01000000 // bit 7 of the second digit
const char *SSID = "your-ssid";
const char *PASSWORD = "your-password";
// POSIX timezone: the offset and the two days daylight saving
// changes on, so the board makes the switch itself. This one is
// Melbourne. Replace it with your own.
const char *TZ = "AEST-10AEDT,M10.1.0,M4.1.0/3";
TM1637Display display(CLOCK_PIN, DATA_PIN);
// Shown until the time is real. A wrong time looks exactly like a
// right one; four dashes do not.
const uint8_t DASHES[] = { 0x40, 0x40, 0x40, 0x40 };
void setup() {
Serial.begin(115200);
display.setBrightness(4);
display.setSegments(DASHES);
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) delay(250);
Serial.println(WiFi.localIP());
configTzTime(TZ, "pool.ntp.org", "time.nist.gov");
}
void loop() {
struct tm t;
if (!getLocalTime(&t, 100) || t.tm_year + 1900 < 2020) {
display.setSegments(DASHES); // no time yet
delay(500);
return;
}
// Leading zeros on. Without them, midnight sends the number 0,
// and the released library drops the colon with the blanks.
display.showNumberDecEx(t.tm_hour * 100 + t.tm_min,
(t.tm_sec % 2) ? COLON : 0,
true);
delay(200);
}The year check is the real test for whether the time has arrived: getLocalTime returns true as soon as the clock has any time at all, and on a cold boot that time is 1970. Until the year looks plausible the display shows four dashes, which is honest in a way that a wrong time is not.
View on GitHub · blocks/tk51-tm1637-display/arduino/clock/clock.ino @ v1.13The same clock in MicroPython on an ESP32, with tm1637.py from the same folder saved to the board first. It joins Wi-Fi, asks NTP for the time until an answer comes, and shows dashes until then.
"""
TM1637 4-Digit Display - a clock that sets itself TK51 / /p/tk51
An ESP32 and a clock panel. The time comes over Wi-Fi from NTP.
Wiring. No pad on this board is square, so go by the names
printed on the back: GND, VCC, clock, data. Seen from the
display side with the header on the left, GND is the top pin.
GND -> GND
VCC -> 3V3. Never 5V: the pull-ups would put 5 V on your
pins.
CLOCK -> GPIO 18
DATA -> GPIO 19
Thonny
Run > Configure interpreter MicroPython (ESP32)
Save tm1637.py from this folder to the board first.
MicroPython keeps UTC and knows no timezones, so OFFSET_H is a
fixed number of hours: change it by hand when daylight saving
starts and ends. The Arduino clock.ino makes that switch itself.
"""
import time
import network
import ntptime
from tm1637 import TM1637, COLON, MINUS
SSID = "your-ssid"
PASSWORD = "your-password"
OFFSET_H = 10 # Melbourne in winter; 11 in summer
display = TM1637(18, 19)
display.brightness(4)
display.segments([MINUS] * 4) # dashes until the time is real
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(SSID, PASSWORD)
while not wlan.isconnected():
time.sleep_ms(250)
print(wlan.ifconfig()[0])
while True:
try:
ntptime.settime() # sets the board's clock to UTC
break
except OSError:
time.sleep(5) # no answer yet: keep the dashes
while True:
t = time.localtime(time.time() + OFFSET_H * 3600)
colon = COLON if t[5] % 2 else 0
# Leading zeros on, so midnight shows 00:00.
display.number(t[3] * 100 + t[4], colon, True)
time.sleep_ms(200)MicroPython keeps the time in UTC and has no timezone rules, so OFFSET_H is a fixed number of hours that you change by hand when daylight saving starts and ends. The Arduino sketch's TZ string makes that switch itself, which is the reason to prefer it for a clock that lives on a shelf.
View on GitHub · blocks/tk51-tm1637-display/micropython/clock.py @ v1.13When it does not work
Either Wi-Fi never connected or NTP never answered. Check the serial monitor: the sketch prints the board's IP address once it is on the network. If the IP appears and the dashes stay, NTP is being blocked — it is UDP port 123 outbound, which plenty of guest and corporate networks drop. Most routers run a time server on the gateway address instead.
That is a fixed offset instead of a timezone. A POSIX TZ string like the one in the sketch carries the two switch dates, so the board changes itself. gmtOffset plus daylightOffset is correct for one half of the year and wrong for the other.
Check the panel first — a digit panel has no colon, only points. If it is a clock panel, check the mask is 0b01000000 and not 0b10000000, and that leading zeros are on.
The display holds whatever it was last sent, so a crashed sketch leaves a perfectly readable frozen clock. That is what the blinking colon is really for: a clock whose colon has stopped blinking is a clock whose sketch has stopped running.
3.3 V is not enough for blue or white. Use a red, orange, yellow or green panel on an ESP32, or feed the display 5 V with a level converter in the two signal wires.
The same display with a point instead of a colon, and the arithmetic that puts the point in the right place.
A voltmeter readout →Edit this page — content/books/tm1637-display/a-clock-that-sets-itself.mdx
Questions about this product
See what other owners have asked, and read their solutions.
This page covers several products. Choose yours to see the right discussions.
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.