Self Learning Clock (SLC)
The Self-Learning Clock (SLC) is a DIY clock that learns its own internal drift to maintain high accuracy with minimal NTP synchronizations.
It uses an ultra-low power development board, paired with a 4.2-inch, high-contrast e-paper display.
The watch is battery-operated, and thanks to its extremely low power consumption (<0.75 mAh on average), a single charge should last months; I'll provide precise battery life data in due time.
The Concept:
Traditional NTP-based clocks frequently check in with time servers, which drains power and relies on a continuous network connection. Their accuracy drifts until the next sync.
The Self-Learning Clock (SLC) takes a different approach: it observes its own timing errors and builds a software correction model for its internal oscillator's drift.
By applying this adaptive correction, it achieves excellent short-term accuracy and minimal long-term drift (the last gets fully compensated at NTP syncs).
After an initial learning period, the clock maintains an accuracy of approximately ±2 seconds over a 12-hour period, with just two NTP syncs per day.
The initial learning takes about two days, but the learning process never stops, allowing the SLC to continuously refine its corrections.
A high-level flowchart is attached, which better explains how the system works.
Why This Matters:
Microcontrollers like the ESP32 contain two types of oscillators:
- High-power mode: Uses a stable and precise crystal oscillator.
- Low-power mode (light sleep): Uses energy-efficient but imprecise RC oscillators that can accumulate minutes of error per day.
While external RTC modules with backup batteries can solve this problem, the SLC achieves similar precision using only the microcontroller itself.
This enables true, long-lasting battery operation without any additional hardware.
Why "Self-Learning" ?
In this context, "learning" refers to a deterministic self-calibration process. The clock models and compensates for its own hardware characteristics. It's not artificial intelligence, but a straightforward mathematical analysis.
The system compares the internal clock deviation (in PPM) against a trusted source (NTP servers) and applies a filtered correction to the displayed time.
Is this approach totally new ?
After researching online, I could not find any prior project with this specific approach.
Key Features:
- Adaptive Drift Compensation:
- Learns and corrects the microcontroller's oscillator drift through periodic NTP synchronization, creating an increasingly accurate internal timing model.
- There is no need to manually calibrate the resonator; the code handles compensation automatically.
- Ultra-Low Power Consumption (<0.75 mAh average):
- Implements finely tuned light-sleep cycles with ultra-short wakeups (≈0.8 seconds active per minute).
- Smart Time Management:
- Automatically handles time zones and Daylight Saving Time (DST) with configurable rules for different regions (e.g., EU, US, Australia).
- High-Contrast 4.2" E-Paper Display:
- Features gigantic 110-point digits for excellent readability.
- It uses partial refreshes for efficiency and a full refresh every 60 updates to prevent ghosting.
- The display updates immediately after the minute changes (when seconds are between 0 and 5).
- The display also shows useful status information:
- Battery level (displayed in place of the colon between hours and minutes)
- Residual timing error (in PPM)
- Microprocessor temperature (°C/°F)
- Time of the next NTP sync
- Status of the last WiFi connection
- Status of the last NTP synchronization
- WiFi Connectivity:
- The clock requires a WiFi network for NTP synchronization. While this is a limiting factor, WiFi is common in most modern homes.
Additional Notes:
The learning and correction process continuously adapts to variations that might arise from temperature changes or component aging.
The very small duty cycle (<1.5%) drastically limits the microprocessor's self-heating, allowing it to be 'sealed' in an enclosure. This isolation protects the processor from sudden room temperature fluctuations, leading to a more stable drift characteristic.
Long-term accuracy validation is an ongoing process: The more precise the clock becomes, the longer the test period required to characterize its performance fully.
So far, the longest continuous test run has been one week; I will provide more detailed long-term data as it becomes available.
About this Instructables
This Instructable provides all the key information to understand how the Self-Learning Clock (SLC) works, and to build one yourself.
Whether your interest lies in exploring the concept and functionality of the clock, or in replicating the device, the following steps will guide you through both aspects.
Here’s what you’ll find:
- Supplies
- Step 1: Flash the ESP32-S3
- Step 2: Copy the SLC Files
- Step 3: Adjust the Settings
- Step 4: Prepare the Connections Board
- Step 5: Set the EPD to 3V3 (much Lower Consumption)
- Step 6: First Check
- Step 7: 3D Print the Box
- Step 8: Assembly of the SLC
- Step 9: Display Fields
- Step 10: Enabling and Disabling Autostart
- Step 11: Result Evaluation
- Step 12: Conclusions
- Step 13: Future Tasks
- Step 14: Credits
Supplies
- 1 x LiLyGO TTGO T8-S3 (v1.2) [https://tinyurl.com/3tyy9sea].
- 1 x WaveShare 4.2 inches epaper display [https://www.waveshare.com/wiki/Pico-ePaper-4.2]
- 1x Lithium Polymer Battery 3.7V 2200mAh (77mm x 35.5mm x 8.5mm) [https://tinyurl.com/2d88ehw4]
- 1x 4056 breakout board with protection circuit [https://tinyurl.com/m7vyyh32]
- 1 x USB C DIY socket [https://tinyurl.com/264trdkj]
- 1x Connections board to drastically reduce wiring, otherwise wires :-). The board can be made out of a perfoboard, otherwise the Gerber files are provided to order ita at a PCB manufacturer.
- 2.54mm female and male headers.
- some screws: See Assembly for the types.
Rationale for some of the chosen components:
I couldn’t get this project to run with the standard RAM (about 320 KB of DRAM) of a regular ESP32-S3 board.
The chosen board (LilyGO TTGO T8) offers several useful features for this project: 8 MB of PSRAM, a battery connector, and an IPEX connector as an alternative to the built-in antenna.
Although the T8 board includes charging circuitry for a LiPo battery, it doesn’t integrate a protection system. For this reason, I decided to add a TP4056 breakout board with a built-in protection circuit.
The Waveshare 4.2″ e-paper display is large and very neat. In addition, it offers flexibility — it can be connected either via wires or directly through the header, resulting in a cleaner assembly. Not to mention, I could buy the Pico version at a very reasonable price (about 24 € in September 2025).
Flash the ESP32-S3
https://docs.espressif.com/projects/esptool/en/latest/esp32/
- Install esptool in your PC [instructions at https://micropython.org/download/ESP32_GENERIC_S3/]
- Download the Micropython v1.26.1 bin image [https://micropython.org/download/ESP32_GENERIC_S3/]
- Move the binary file to the folder where you run the esptool.
- Connect the board to a PC via the usb-C port.
- Check on which COM port the device is detected (i.e. via Device manager in Windows).
- Erase the flash memory (replace <COM> with the COM port your device is detected, i.e. --port COM6):
- Flash the OS (note this board has 8Mb PSRAM):
Copy the SLC Files
The code is stored at https://github.com/AndreaFavero71/Self_Learning_Clock
Copy all the files, and the lib folder, to the root of ESP32-S3; The needed libraries are included.
An easy way to copy the file is :
- Download the SLC files to a known folder on your PC.
- Connect the LiLyGO T8 board to your PC and launch Thonny → [https://thonny.org/].
- In the bottom-right corner, check if the board is detected.
- If not, click that corner and select the device/COM port associated with your ESP32.
- Press the STOP button to let Thonny connect to the T8 board.
- Enable the Files view: go to View → Files.
- In the top-left panel, browse to the local folder on your PC.
- In the bottom-left panel, you’ll see the files on your ESP32 device (it should be empty).
- Select all files from the local source (click the first file, then Shift + Click the last one).
- Right-click on one of the selected files and choose “Upload to /”.
- Confirm the overwriting of the boot.py file.
Files will be coopied on the root of the T8 board
Adjust the Settings
All settings are located at /lib/config folder.
There is one python file (config.py) and two json files (secrets.json and dst_rules.json).
The content of these files is self-explanatory, anyhow below is provided some more information.
Regional related settings:
The SLC works with UTC (Universal Time Coordinated) but it doesn't know in which part of the word is located.
Settings must be applied at config.py, and include:
- Time Zone
- If the Day Saving Time (DST) is used
- If the DST is used, which rule follow
- DST rules as structured in the dst_rules.json file.; It includes those for EU, US and AU. Adding new rules is possible and straight forward.
- Temperature in Celsius or Fahrenheit
- Date format (DMY, MDY and YMD)
- the NTP servers:
Notes about NTP servers:
- default NTP servers in config.py: ['pool.ntp.org', 'nl.pool.ntp.org', 'europe.pool.ntp.org', 'time.nist.gov', 'time.google.com', 'time.windows.com']
- The second and third servers are respectively the National and the Continetal one where I live. These icould could be conveniently exchanged with servers located in your region.
- The server ''pool.ntp.org' is internation, and tipically the fastest one the SLC can reach via my home WiFi network.
WiFi settings:
The SLC requires a WiFi network to operate.
You can enter your SSID and password (or more than one) in the secrets.json file.
In case of multiple SSIDs, the set priority in the json file are used by the clock to fall back to the next network if the previous one does not respond.
Optional: In case of no WiFi settings, or all the set networks are failing, the clock will then scan for open networks: In case it finds one or more, it will use the one with the strongest signal.
System settings:
There are a number of system settings, mainly for learning and debugging purposes, like:
- DEBUG --> enable/disable verbose printing to the Shell.
- OPEN_NETWORKS --> enable/disable the usage of free networks.
- BATTERY --> enable/disable the battery operation.
- PUSH_FILE_ENABLED --> enable/disable data sharing toward a local server.
- QUICK_CHECK --> enable/disable a fast test to test the overall code (inaccurate time).
- WDT_ENABLED --> enable/disable the watch dog timer.
Prepare the Connections Board
The Connections board can be built on a prototyping board (fairly simple), or you can have a PCB manufactured.
If you choose the second option, the Gerber files are available at: https://github.com/AndreaFavero71/Self_Learning_Clock
Note: The 6-pin header is reserved for future use (not needed for the current build).
Downloads
Set the EPD to 3V3 (much Lower Consumption)
The Connections board provides both the 3V3 and VSYS power rails to the EPD.
The EPD includes a 0 Ω resistor at J2 between the pads “VCC_INT” and “VSYS”, which defines its default power supply.
When VSYS is used, the EPD circuitry employs a level shifter that continuously draws about 1.5 mA, even when the display is in deep sleep.
To reduce power consumption, it is possible to move the 0 Ω resistor to bridge “3V3” with “VCC_INT” at J2 (or remove the resistor and apply a small droplet of solder between these two pads).
With this modification, the EPD draws less than 10 µA when in deep sleep — that is, for more than 99.1% of the time.
Attached is the schematic of the Waveshare 4.2" Pico e-Paper display.
Downloads
First Check
The SLC can be powered directly from your PC through the USB-C port on the LiLyGO T8 board.
Connect to the T8 via Thonny or another IDE.
Run the slc.py file — after about 20 seconds, the display should show the SLC logo.
If Wi-Fi has been configured, the clock will continue operating normally; otherwise, it will likely show a Wi-Fi error.
Once the first test is successful, you can proceed with the 3D printing and assembly.
3D Print the Box
There are a total of 5 parts
Assembly of the SLC
Screw list:
- 1× M3×8 mm (or M3×10 mm) → Clip holding the battery and TP4056 breakout board
- 4× M2.5×10 mm → Frame to EPD (e-paper display)
- 4× M3×18 mm → Back cover to Frame
Assembly notes:
- When preparing the wiring, remove the battery connector by cutting one wire at a time to prevent short circuits.
- The attached pictures are ordered according to the suggested assembly sequence.
- The Front is connected to the Frame via snap hooks; it can be assembled as the final step.
Display Fields
Enabling and Disabling Autostart
Edit the file boot.py (for example, using Thonny) and uncomment the commands from lines 5 to 13.
Save the file.
From now on, when the board boots, the SLC will start automatically.
To reboot the board, simply switch it OFF and then ON using the small slider switch.
Important Notes – Stopping the Code After Auto-Start
When the SLC is running, it spends over 99% of the time in light-sleep mode.
While the ESP32-S3 microcontroller is in light sleep, it cannot handle serial communication, so your PC will not recognize it as a connected USB device.
If you need to modify a setting or re-edit the code, follow these steps:
- Open Thonny on your PC.
- Connect the T8 board to your PC.
- Press and release the small push button located behind the slider switch — this is the one soldered on the back side of the board, facing upward.
- Press the STOP button in Thonny.
- In the bottom-right corner, select the board/COM port corresponding to your ESP32.
- Press Ctrl + C to interrupt the SLC code running in the background.
- Double-click boot.py in the lower-left file list.
- Select all the code with Ctrl + A.
- Comment out all lines with Alt + 3.
- Save the file with Ctrl + S.
- Press the STOP button again.
- From step 3 onward, you have about 10 seconds to prevent the code from restarting automatically.
After commenting out boot.py, the SLC will no longer start automatically, allowing you to modify or upload new files.
Result Evaluation
Improvement factor
A simple way to quantify the result is through an improvement factor, defined as the absolute ratio between the original error and the residual error.
In the first chart, the latest nightly tests are shown, where the improvement factor is plotted against the test time. It essentially illustrates how the clock improves over time.
You’ll notice two main groups:
- One showing faster but less stable improvement, originally based on shorter periods.
- Another showing slower but more stable improvement, obtained with longer periods.
The second chart shows the longest test I’ve performed so far. This one resulted from a mistake — I accidentally typed an extra zero in the milliseconds value, extending a 6-hour interval to 60 hours.
Anyway, since I was busy modeling the box and monitoring power consumption, I decided to let the test run.
Residual Error Evaluation
Another way to evaluate the result is by observing the residual error shown on the display as “Error (ppm)”.
For this you can refer to the third chart: An error of 100 ppm corresponds to a potential drift of 8.64 seconds per day.
After the initial two days of learning, the SLC automatically compensates for any remaining error every 12 hours as part of the NTP synchronization process.
A residual error of 200 ppm is normally reached within the first day.
More detailed long-term data will be provided as it becomes available.
Conclusions
This approach is for sure not the most precise, when the ojective is time accuracy.
Differently, it might be a real option for an indoor clock, or as timekeeping in battery operated systems that can tollerate a few seconds of inacuracy.
While there are many accurate timekeeping solutions available, I wanted to see how far I could push the accuracy of a microcontroller's own hardware, in a battery-operated device, and I'm quite pleased with the results.
Future Tasks
- Collect more data from long-term tests to evaluate whether a better model is possible.
- Measure the SLC’s power consumption when powered by the battery (I currently don’t have a proper instrument). This will improve the autonomy estimation.
- Investigate the feasibility of a micro solar panel for achieving full energy autonomy.
- Since there is some unused space on the display, adding a weather forecast would be a nice enhancement.
Credits
Credits to:
- Hackaday One Hertz Contest → [https://hackaday.io/contest/203248-one-hertz-challenge] for hosting so many inspiring projects. I particularly liked the one based on a tuning fork:[https://hackaday.io/project/202971-a-more-precise-tuning-fork-clock].
- Volodymyr Shymanskyy → for his excellent lightweight asynchronous DNS client for MicroPython:[https://github.com/vshymanskyy/aiodns/tree/main]