NeoPixel LED Analog Clock – Part 2 – Software Implementation

In Part 1 I described the hardware components and the functionality of the LED clock. This this part, I’ll explore the software required to implement the functionality and seamlessly manage the different user interfaces.

Software Implementation

The software is available as a sketch called Chroniker from my code repository.

A block diagram for the control software components in this project is shown below. The user interface components are designed to be independent and communicate to the back end processes through a command queue, using the architecture described here.

Managing the Clock Face

If the LED demo is not running, the default is to update the clock every second. There are 3 types of clock faces that can be displayed:

The first is a straightforward animation of the time with the hour, minute and second hands displayed as individual LEDs in their respective analog positions. The hour LED moves in proportion to the minutes past the hour.

The second type of display is like the first but with the positions for 1, 2, 3, 4 etc marked by a permanently lit LED.

The third type of display has all the LEDS from the ‘zero’ position to the current hour, minute and second lit, so the display looks somewhat like an animated pie chart. The only complication with this display is that the colors need to be updated so that no colors are ‘masked’ by another.

LED Show

Control for the NeoPixel LED ring in the clock face is implemented through the FastLED library. The background processing animations are largely largely adapted from the FastLED library examples.

As the FastLED library turns of all interrupts while it is working, the examples use lower frame rates to allow the UI (Serial comms and IR receiver) to work.

Ambient Light Adjustment

The ambient light adjustment is designed to increase the intensity in daylight and decrease it when dark. It works by creating a ±intensity offset from the set point. It works, but overall it probably needs some improvement to be really effective.

User Interfaces

The user interfaces are implemented as derived classes from the basic interface class iChroniker, implementing a begin() method for initialisation and a getCommand() method returning true when a command is available for processing, stored in the cmdQ_t variable c of the class.

The command structure contains a uint8_t (1 byte) with a command code and a uint32_t (4 byte) with an optional command parameter. These structures are placed in the command queue.

The tact switch provides user functions modelled on the Micro Word Clock project and works in a similar manner. There is is the least capable user interface and is a simple implementation of interpreting different kinds of switch presses detected by the MD_KeySwitch digital switch library.

The Bluetooth (BT) interface receives commands from an Android Application. The commands are embedded in a serial communication protocol described in Reliable Bluetooth Comms Between Arduino and MIT App Inventor (AI2). The Android app (screen shots below) is a fully featured interface with dialogs, on-screen buttons, and sliders to simplify managing the clock.

The opening screen allows the user to connect by selecting the BT paired connection from the list displayed in second screen. If the connection is successful, the main display is shown, allowing access to the full set of functions available in the clock. To aid in debugging, the application also shows the serial protocol response messages, or error messages, in the area directly underneath the ‘Disconnect’ button (the “1: Ok [*0~]” in the third screen shot).

The Infrared (IR) remote control provides a user interface with functionality sitting between the tact switches and the BT interface. The Remote control’s codes were mapped out using the AnalysIR software, resulting in the table on the right (click to enlarge). getCommand() is then simply a function to map the different codes to commands. This is a straightforward exercise, the most difficult part being mapping MP3 related switches to commands in a somewhat logical manner.

Scheduling Function

The scheduling function in Croniker is used to invoke the user interfaces in turn and enqueue all the available commands. It is called every time through the loop() function.

As different user interfaces are implemented, they are added into the scheduling loop. While in this example these are hard coded in or out, the design could also allow the user input section to adapt the hardware environment, configured from via run-time configuration parameters (in EEPROM, for example).

Command Processor

The command processor reads the next command from the queue and executes it. It is part of loop() and essentially consists of a switch statement to execute the right actions for each command. The starting code fragment of the queue processor is shown below.

Conclusion

The aim of this exercise was to test the ability integrate multiple simultaneous user interfaces whilst maintaining uninterrupted back end processing rates.

The system designed was successfully able to integrate the different UIs with no significant loss of responsiveness when the processor intensive background tasks were running. As a bonus it also provides flexibility and scalability that makes it adaptable to many other situations.