LED Light Clock

Two 1-meter strings of APA104 LEDs, one ESP32

Similar projects worth following
I wanted something to spruce up dreary 45-degree-latitude winter mornings, and this project is my answer. An ESP32 board drives two 1-meter APA104 strips based on NTP time and configuration set through an mDNS-advertised HTTP interface.

At the time appointed through the HTTP interface, two meters of fully programmable LEDs will cheerfully augment the growing dawn light with a color of your choice. It even sports a snooze button!

This little bar of joy is even useful in the evening, when it will take a temperature and brightness of your choosing, convert it into an LED color, lighten up the room, and slowly, peacefully fade to black. It can even stay on for a selectable number of minutes before fading over a selectable number of minutes.

This must-have build is pretty easy if you have the materials and tools. The software might be considered non-trivial by Arduino standards, but hey, it's already done! Not only that, but it's commented copiously to help you dip your toes into the world of embedded development should you so desire.

If you're feeling particularly adventurous, there are several patterns available that reach beyond the plainness of solid colors. Rainbows, fades, Unix epoch time, and even a binary-coded-decimal display are included free of charge*!

This project made possible by Alibaba, SparkFun, AdaFruit, Amazon, Vetco Electronics, Ace Hardware, the local Microsoft Garage chapter, my brother-in-law, and our dear, old friend SARS-COV-2 whom we still wish to banish into the mists of time as soon as reasonably possible.

* Note that staring too long into the bright, LED-lit expanse of time may cause irreparable harm to one's perspective on the shortness of life, including but not limited to multigenerational planning and horror at the meaninglessness of social media (cat videos excluded, of course).

View all 8 components

  • Software Architecture

    Jon Kunkee07/20/2020 at 07:16 0 comments

    Most makers working with the ESP32 can easily accomplish their goals by using the ESP-IDF integration with the Arduino IDE. With my background in hardware and low-level software, I wanted to learn a bit more about the ESP32 and went with the ESP IDF (coupled with Visual Studio Code. (The blog post linked from the Project page discusses some of the work I had to do to get it working in the somewhat exotic environment I work in on a daily basis.)

    This meant I was exposed to FreeRTOS resource management and multitasking, and I had some fun.

    The IDF sample project starts with a 'main' function that runs in its own FreeRTOS task (thread). As peripherals are initialized and started, relevant tasks, events, and ISRs are set up. The programmer is then left to build on top of this.

    Overall, the code is split into:

    • Color Space APIs
    • LED patterns and colors
    • Configuration management
    • HTTP handlers
    • Alarm+Sleep state machine

    I ended up with two event loops of my own: a vestigial one in the 'main' task that keeps the WiFi up and an alarm loop that tracks time and listens for configuration events.

    initialization outputs
    The initialization done in main() has a number of subsystems it sets up.

    Here's a look at how these parts talk to each other, whether by function calls or events, during normal operation:
    call/event flow under normal operation
    Call/event flow under normal operation. Note that the alarm event loop is the star of the show with a wide variety of supporting characters. One important exception to this is that LED commands can come directly through HTTP.

    The ESP-IDF services all expose either a direct call interface--where a function is called to perform a given task--or a callback-based asynchronous interface. For my own services (the LEDs and the configuration engine), I crafted rough approximations of the same. The HTTP handler calls into the config engine which then calls a static callback to inform interested subsystems that they should re-read their configuration. The LED service has a single exposed function for running LED patterns, which internally uses a mutex to ensure only one command is processed at a time. The underlying code makes extensive use of my color space APIs to convert various conceptual concepts, like 'green' and '3100K', into linear RGB which then gets gamma-corrected by the LED driver itself.

    The color APIs all rely on only function arguments and locals, making them state-free and so lock-free.

    The alarm event loop is not direct-call or callback-based. Instead, it uses events and event wait timeouts to receive things like HTTP events (snooze, stop) and time changes.

    Time is tracked by the onboard RTC and periodically updated via NTP. As an alarm clock on mains power, no attempt was made at power management.

    Nothing makes code easier to maintain than good comments (except maybe good docs in addition to good comments :). Knowing I would probably take quite some time away from this project and seeing that I was learning a lot as I went, I put as much documentation as I could in the code.

  • Electronics Architecture

    Jon Kunkee07/20/2020 at 06:51 0 comments

    Wiring up the project got a little weird because of some aesthetic choices I made.

    The LED strips came with two plain wires for ground and +5V and a 3-wire cable for ground, +5V, and data. Instead of wiring the power in a classic star topology (ESP32 and LED strips all connected to the barrel jack), I connected the two plain wires to the barrel jack and connected the ESP32 to power through one of the LED strips. It came out something like this:

  • Build

    Jon Kunkee07/20/2020 at 06:41 0 comments

    Over the next several weeks, I spent my spare time setting up the build environment, brainstorming desired features, and writing code. There were a number of little problems, described primarily in the Git commit history, code, and in a blog post linked from the Project page. Probably the most interesting issue was that LEDs past about number 30 would sometimes get random colors. It took a lot of forum crawling, some trial-and-error, and a little oscilloscope work to establish that one ESP32 CPU core isn't powerful enough to handle both WiFi and IR transceiver (RMT) interrupts on the same CPU if the RMT load is high enough--like when doing tight-loop animations or writing to long strands. The fix was to move the RMT ISR to the second CPU.

    A second interesting problem was programming colors. The ESP32 is capable of using floating-point representations for colors at all phases (barring its terrible floating point performance), but eventually they needed to be converted to the 24-bit RGB values the LEDs would accept. I ended up with a set of layered abstractions and conversion functions moving from the most abstract color systems, like CIE x,y values and color temperatures, down past HSV to linear RGB through to gamma-corrected RGB PWM values to be sent straight to the hardware. This felt like the part where I learned the most.

    The remainder of the physical build was actually straightforward: drill some holes in plastic and aluminum, bolt things together, and run some wires. It sounds simple, but I ended up only getting three of the four board-mounting holes to line up well enough to use. (I also learned that a common battery-powered drill with wood bits is, with some effort, quite capable of working 1/16" aluminum.

  • Origins

    Jon Kunkee07/20/2020 at 06:23 0 comments

    A long time ago (2018), I had a hare-brained idea to make getting up in the morning easier: add light to the room on a timer. This led to a part-buying binge with quite a bit of back-and-forth on which 5V power supply to use and which LED strips would minimize the work I had to do, with a mix of SparkFun and AdaFruit parts sneaking off with my wallet. A local hardware store, Ace Hardware, provided a meter of angle aluminum.

    This sat on my shelf for two years, right next to the ESP32 modules a family member got me for Christmas.

    Well, technically I immediately used the adhesive backing on the LED strips to paste them to the angle aluminum, and that subassembly sat on my shelf forever. As is often the case with my projects, enclosure design stymied me--until I discovered Hammond Manufacturing project boxes at Vetco Electronics Store.

    Around the start of the COVID-19 response in my country, I got the bug to make something and, with all the parts sitting around, this project was the clear winner.

View all 4 project logs

Enjoy this project?



Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates