Quinary LED Art Clock 
Sunday, October 11, 2020, 17:30
Posted by Administrator


This quinary ( base 5 ) LED clock project has taken me many more months than I thought it would. I spent a great deal of time considering and ruminating over exactly how I wanted it to look and operate. It's gone through many, many, many iterations but is finally in a 'done' enough state that I feel quite happy with it and ready to write about it and possibly move on to other things in my life.

It began first with the concept of utilizing addressable ws2812b LED strips to indicate the time in an alternate base. I've worked on binary clocks a few times in the past but have grown a little tired of the concept, and I wanted to create something that would make more efficient use of fewer LEDs so that I could power them directly from an esp8266 without worrying about drawing too much power from the board itself and burning it out. I like the simplicity of not providing additional current, and I wanted to see how much utility I could sneak out of these power limitations.

I also spent at least a few weeks determining what base to use. I was thinking about base 3, and base 8 for most of that time. What finally drew me to base 5 was when I started calculating out how many LEDs it would take to convey the number 24 ( For max hours in military time ). It takes exactly 2 LEDs to do this which I found to be perfectly succinct. Then, after realizing that 3 additional LEDs would easily allow me to show every minute value up to 59 it seemed strikingly elegant that the number of LEDs required to display both the hours and minutes ( 5 total ) was the same as the base I was using. This part of the project strangely seemed to design itself. It just felt right for the clock to be configured in this way. The more I thought about it, the more I realized that it was something I had to bring into existence as a functional art piece.

The next step was determining what colors should indicate the digits 4 through 0. What made the most sense to me was to use at least Red Green and Blue in that order. With Red being the most significant value of 4, Green being 3, and Blue as 2. I still needed to choose a value for 1, and since I like the color Purple I decided to go with that. The biggest challenge of choosing the digit colors was what to do with the digit 0. One option is to leave the LED off to indicate 0's, but in my experience with my binary clocks, I've found that having at least some amount of light on this digit can be very useful for reading these sorts of displays in the dark. I originally went with the color Orange, because I liked that the first letter of the color looked like the digit it represents. Although, in practice with the particular LED strips I have, I found it more easily discernible to use a dim Yellow for 0's. So the way I think about the final colorset is as RGBPY, or Red, Green, Blue, Purple, Yellow. The clock made a lot more sense to me after I committed these color/digit pairs to memory and I can now readily associate the colors I chose with the digit values that they represent.

The coding of this project is the part that took the least amount of time. I was able to re-use much of the code that I have used for previous esp8266 projects, although I did spend a couple of nights after work to make the code cleaner by moving certain parts of it to self-contained helper functions. The time setting portion is handled automatically using Network Time Protocol, so the esp8266 hops onto my local WIFI router and retrieves the current local time from the internet. It also syncs up with the NTP server regularly so that there isn't much drift. I wrote a helper function to account for daylight savings time, so that unlike with my other clock projects, I won't have to reflash the code twice a year for it to remain accurate. The settings for timezone, daylight savings time, the colorset, and everything else are defined in adjustable values up near the top of the code so that they can be adjusted easily by someone else ( Or more likely, me years from now ).

The final nicety with the code is that at 8 pm every night it turns the brightness down quite a bit so that the LEDs don't garishly illuminate a dark room. After 8 am the clock sets its brightness back to daytime mode. The full code can be downloaded here, and the meat of it follows below:
// Quinary RGBPY LED Clock
// Spike Snell 2020
//
// Add ESP8266 board from https://arduino.esp8266.com/stable/package_esp8266com_index.json
// ( File > Preferences, Tools > Board > Board Manager )

#include <NTPClient.h> // https://github.com/taranais/NTPClient
#include <FastLED.h> // https://github.com/FastLED/FastLED
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

// Define the number of leds
#define NUM_LEDS 5

// Define the data pin
#define DATA_PIN 4

// Set up the character arrays for the Wifi ssid and password
const char *ssid = "********";
const char *password = "********";

// Define the UTC Time offsets, these are for Central time in North America
const long winterOffset = -21600;
const long summerOffset = -18000;

// Define the DST Start and end Months and Days
const int dstMonthStart = 3;
const int dstDayStart = 8;
const int dstMonthEnd = 11;
const int dstDayEnd = 1;

// Define the bright and dim light intensity 0 - 255
const int bright = 88;
const int dim = 15;

// Define the hour that daylight roughly starts and ends
const int hourDayStarts = 8;
const int hourDayEnds = 20;

// Define the led digit colors
uint32_t color[] = {
0x151000, // 0, Dim Yellow
0xAA00AA, // 1, Purple
0x0000FF, // 2, Blue
0x005500, // 3, Green
0xFF0000 // 4, Red
};

// Define NTP Client to get the time
WiFiUDP ntpUDP;

// Set up the Network Time Protocol Client, update with fresh time every 10 minutes
NTPClient timeClient(ntpUDP, "85.21.78.23", winterOffset, 600000);

// Set up the leds array
CRGB leds[NUM_LEDS];

// Setup our sketch
void setup() {

// Connect to the wifi point
WiFi.begin(ssid, password);

// Wait for the wifi to be connected
while ( WiFi.status() != WL_CONNECTED ) {
delay(500);
}

// Start the time client
timeClient.begin();

// Add the leds array to the Fast Led Definition
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);

}

// Loop through this part every second
void loop() {

// Update the time client to get the current time
timeClient.update();

// Adjust for daylight savings time
adjustDst();

// Parse the current time and prepare all the led colors
parseTime();

// Show all the leds
FastLED.show();

// Wait for 1 second
delay(1000);

}

// Adjust for daylight savings time considerations
void adjustDst() {

// Extract date
String formattedDate = timeClient.getFormattedDate();

// Get the current month string
String month = formattedDate.substring(5, 7);

// Get the current day string
String day = formattedDate.substring(8,10);

// If we are within the defined daylight savings time period
if ( (month.toInt() > dstMonthStart && month.toInt() < dstMonthEnd) ||
(month.toInt() == dstMonthStart && day.toInt() > dstDayStart - 1) ||
(month.toInt() == dstMonthEnd && day.toInt() < dstDayEnd) ){

// Set summer time
timeClient.setTimeOffset(summerOffset);

}

// Else we must not be in daylight savings time
else {

// Set winter time
timeClient.setTimeOffset(winterOffset);

}

}

// Parse the current time and prepare all the led colors
void parseTime() {

// Create our character buffers
char timeBuffer[5];
char minuteBuffer[3];
char tempHourBuffer[2];
char tempMinuteBuffer[3];

// Set the leds bright if during the daytime
if ( timeClient.getHours() > hourDayStarts - 1 && timeClient.getHours() < hourDayEnds ) {

// Set leds to bright mode
FastLED.setBrightness(bright);

}

// Else set the led's to be dim because it is nighttime
else {

// Set leds to dim mode
FastLED.setBrightness(dim);

}

// Convert the current hours to base 5
itoa(timeClient.getHours(),tempHourBuffer,5);

// Convert the current minutes to base 5
itoa(timeClient.getMinutes(),tempMinuteBuffer,5);

// Pad 0's to the hour buffer and place it on the timeBuffer
sprintf( timeBuffer, "%02s", tempHourBuffer );

// Pad 0's onto the tempMinuteBuffer and place it on minuteBuffer
sprintf( minuteBuffer, "%03s", tempMinuteBuffer );

// Concatenate the timeBuffer and minuteBuffer
strcat(timeBuffer, minuteBuffer);

// Iterate over the timeBuffer and set all the leds
for (int i = 0; i < strlen(timeBuffer); i++) {

// Switch on the2 current timeBuffer digit and set each led's color
switch(timeBuffer[ i ]) {
case '0' :
leds[ i ] = color[0];
break;
case '1' :
leds[ i ] = color[1];
break;
case '2' :
leds[ i ]= color[2];
break;
case '3' :
leds[ i ] = color[3];
break;
default :
leds[ i ] = color[4];
}

}

}
Another part of this project that took quite a bit of time was determining how I wanted to arrange and display the LEDs physically. I've had a lot of prototypes, from just the LED strip laid out loose, or on a strip of cardboard, to putting them on popsicle sticks. My current favorite version is where I have eliminated the LED strip and wired individual nanopixels directly into a strip of pegboard that I painted black. With a little bit of hot glue smushed into the pegboard holes ( and then roughed up a bit with a nail ) very pleasing diffused colors can be generated.

I've had one of these quinary clocks in place prominently in my living room for so long now that it is starting to feel less like a novelty and has instead become a permanent function of the wall itself. It is one of the main clocks that I look to throughout the day to determine what time it is. I've had people see it many times without realizing that it was a clock who were subsequently astonished when inquiring and finding out its secret use. The clock comes across much like an amorphous colorful art installation if you don't know what it is. But, when you start explaining that it is a timepiece and how to read it, that is when they start looking at you like you're a little crazy. Luckily, my girlfriend is also interested in programming and computer science, so she was able to start reading the new clock pretty naturally within a couple of days.

I strongly like this quinary clock. It turned out a lot more elegant than I first imagined it would, and it has become a visually intriguing yet useful part of my living environment that I would very much miss if absent. Unlike most of the other projects that I set up, have fun with for a few weeks, and then set aside, I feel confident that this one will be staying up indefinitely.
add comment   |  permalink   |  $star_image$star_image$star_image$star_image$star_image ( 0 / 0 )

| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Next> Last>>