Building custom firmware for the EleksWFD clock
Reverse engineering an LED desk clock, mapping its display, and building replacement firmware with an optional Rust companion app.
I built replacement firmware for the EleksMaker EleksWFD desk clock, along with a Windows companion app. The firmware gives it WiFi time synchronization, weather, a browser dashboard, and animations it can run on its own. The app adds PC audio visualization, system meters, and live messages.
The work started with reverse engineering the stock device and its Windows tool. It grew into a firmware project, a set of tools for mapping the display, a Python control app, and eventually a Rust app packaged for Windows.
Mapping the display
The EleksWFD has a distinctive panel: clock digits, a six-character message line, a 7×7 grid, audio columns, system meters, and small status icons. On the hardware this project targets, those are LEDs with fixed colors. Software controls which ones light up and the panel's overall brightness.
An ESP32 drives the panel through a TM1680 LED controller. Its display memory is only 42 bytes, but making use of those bytes requires knowing which bit controls each segment, dot, or icon.
I built a browser-based mapper that lights one bit at a time. Clicking the corresponding element on a drawing of the panel records the relationship. A second tool maps the individual segments of the digits and message characters. Those results became the panel map used by the firmware.
Once that map existed, the firmware could draw a complete display from its current state and send the resulting bytes to the controller. Time, messages, animation frames, and audio levels could each have their own rendering logic.
The hardware notes document the chips, wiring, and display layout.
Getting the first flash to work
The USB connection produced a misleading problem early on. The clock would reboot, but attempts to enter the ESP32's flashing mode kept failing on the USB 3 setup used during the investigation. Even the vendor's own flashing tool failed.
Moving the clock to a USB 2.0 port made the automatic reset sequence work. That finding became part of the installation guidance: if the tool cannot connect, check the USB connection before assuming the firmware or board is broken.
The Windows flasher also takes a full backup before writing anything. It checks that backup against the device's flash checksum and stops if the backup fails. Keeping a recovery path matters when replacing software on a physical device someone already owns.
Letting the clock work on its own
The firmware uses the board's DS3231 real-time clock and synchronizes it over WiFi using NTP. It fetches weather from Open-Meteo, runs stored messages and grid animations, and serves its own dashboard. Once configured, the clock can do those jobs while powered from a USB charger.
The Windows app supplies information that comes from the PC: audio output, microphone input, CPU/GPU/RAM readings, and the active window title. It connects over USB or WiFi. When its live commands stop, the firmware resumes standalone behavior after about two seconds; a normal app exit tells the clock to resume immediately.
That division also determines where settings live. Brightness, time format, location, and standalone behavior belong to the clock and persist in its flash. The app reads those settings and sends changes to the clock for validation. App-specific choices, such as PC audio gains and the live message source, stay with the app.
Changing brightness from a phone therefore survives the next PC connection. There is one saved brightness value, owned by the device that uses it.
The dashboard and grid editor are shared HTML files served by both the firmware and the companion app. The dashboard shows the PC controls when it is running through the app.
Making the microphone useful
The clock has its own microphone, so sound-reactive behavior can continue when the PC is off. The microphone amplifier and the ESP32's analog input needed more than a simple mapping from sample value to LED height.
The firmware processes sound relative to an adaptive noise floor. Loudness is measured on a decibel scale, with a fast rise and a smoother fall. Frequency analysis separates bass and treble for the two columns and supplies bands for grid animations.
A recorded trace from the clock became a test input. The tests replay quiet room noise, music, and claps through the same C++ processing code used on the device. Synthetic tones check that low and high frequencies reach the expected columns and bands.
That makes microphone tuning repeatable: changing a threshold can be checked against the recording before another hardware test.
Keeping behavior consistent across implementations
The Python control app remains a reference for the Rust port. Tests compare settings validation, display commands, message glyphs, and deterministic animations. The firmware's C++ animation engine can also be compiled on a PC and checked against expected frames.
Weather scenes have shared fixtures for the Rust app and firmware. A sun, rain cloud, or moon should draw the same way whether the PC is driving the grid or the clock is running independently.

The editor lets a drawing become part of that standalone behavior too. Frames can be previewed on the clock and saved to its flash for playback after the app closes.
Trying the firmware
The MIT-licensed repository includes the firmware, companion apps, mapping tools, and hardware notes. The Windows release includes a USB flasher for installation; subsequent firmware updates can run over WiFi from the dashboard or companion app.
This targets the EleksWFD hardware documented in the repository. Check the flashing guide and keep your own device backup before replacing its firmware.
The EleksWFD project page collects the capabilities, technology stack, and links.