A request was made for a word clock, which I knew nothing about, so I poked around and thought some... Showcasing a 12-hour, colloquial, grammatically accurate, RTC and FastLED driven, Word Clock... with test functions and an internal sketch to set up your own word-clock-words. I transposed a couple letters to keep word-searchers happy.
And, it fits on a Nano/Uno...
Sketch uses 8744 bytes (28%) of program storage space. Maximum is 30720 bytes.
Global variables use 1300 bytes (63%) of dynamic memory, leaving 748 bytes for local variables. Maximum is 2048 bytes.
sketch.ino
#include <FastLED.h> // http://fastled.io/docs/files.html
// https://forum.arduino.cc/t/time-yet-another-word-clock/1293884
#define ROWS 16 // matrix rows
#define COLS 16 // matrix columns
#define NUMPIX (ROWS * COLS) // the full matrix
#define PIXPIN 5 // Arduino data/digital pin
#define MAXBRIGHT 255 // pixels brightness, 0 - 255
CRGB pix[NUMPIX]; // create object to control [NUMPIX]
#include "words.h" // words in the clock
#include "showMin.h" // show minutes
#include "showHour.h" // show hours
// #include "tests.h" // allow testing all letter and word placement
#include <RTClib.h>
RTC_DS1307 rtc;
unsigned long timer, ms1000 = 1000; // one second
int oldMin; // clear old time
void setup() {
// Serial.begin(115200); // enable for testing
FastLED.addLeds<WS2812B, PIXPIN, GRB>(pix, NUMPIX);
FastLED.setBrightness(MAXBRIGHT);
FastLED.clear(); // empty pixel buffer
FastLED.show(); // display empty buffer
if (! rtc.begin()) {
Serial.println("ERROR reading RTC");
Serial.flush();
while (1);
}
// TEST and TIME FUNCTIONS
// sayitall(); while (1); // test all words then halt
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // run once after inserting new battery
// rtc.adjust(DateTime (2000, 1, 2, 3, 4, 56)); // adjustable time for testing
// rtc.adjust(DateTime(year, month, date, hour, minute, second));
}
void loop () {
if (millis() - timer > ms1000) { // update every second
timer = millis(); // reset timer
// gettimedate(); // print time and date text in Serial Monitor with external function
DateTime now = rtc.now(); // get local time and date
// UPDATE ONLY AT TOP OF MINUTE
if (now.minute() != oldMin) { // check for minute change
oldMin = now.minute(); // store new minute
FastLED.clear(); // clear previous clock reading
}
the(); tim(); is(); // these words will always be on
// CALCULATE MINUTES, UNTIL, PAST
if (now.minute() > 0) { // only show "minutes" after "zero"
if (now.minute() >= 20 && now.minute() < 30) { // 21 through 29 becomes "twenty" (min) "past"
twnty();
showMin(now.minute() - 20); // subtract 20 from 21..29 = minutes
}
if (now.minute() == 30) { // exactly 30
hlf(); pst(); // "half" "past"
}
if (now.minute() > 30 && now.minute() <= 40) { // 31 through 39 becomes "twenty" (min) "until"
twnty();
showMin(40 - now.minute()); // subtract 31..39 from 60 = minutes
}
if (now.minute() != 15 && now.minute() != 45 && now.minute() != 30) {
if (now.minute() == 1 || now.minute() == 59)
minx(); // minute. - singular
else
mins(); // minutes - plural
}
if (now.minute() < 30) pst(); // before half
if (now.minute() > 30) { // after half
if (now.minute() == 45) // quarter (hour)
til(); // shortened 'til
else
utl(); // until
}
if ((now.minute() != 15) && (now.minute() != 45)) { // exclude "quarter" hours
if (now.minute() > 40) showMin(60 - now.minute()); // minutes 41 to 59 "until"
if (now.minute() < 20) showMin(now.minute()); // minutes 1 to 19 "past"
} else if ((now.minute() == 15) || (now.minute()) == 45)
qrtr(); // 15 "quarter"
}
// MAKE 12-HOUR CLOCK - replace two checks on now.hour() == 11 and now.hour() == 23
// int tHour = (now.hour() < 13) ? now.hour() : now.hour() - 12;
// PAST (the hour) and UNTIL (the hour)
if (now.minute() > 30)
showHour(now.hour() + 1); // add an hour if "until the hour"
else
showHour(now.hour());
// MIDNIGHT (0 or 24) and NOON (12)
if (now.hour() == 0 && !(now.minute() > 30))
showHour(0); // "midnight"
else if (now.hour() == 12 && !(now.minute() > 30))
showHour(12); // "noon"
// O'CLOCK not after NOON and MIDNIGHT
if (!((now.minute() > 30 && (now.hour() == 11 || now.hour() == 23) || // 31 TO 59
(now.minute() < 30 && (now.hour() == 12 || now.hour() == 0))))) { // 1 TO 29
ocl(); // o'clock
}
// AM or PM (ANTE MERIDIAM / POST MERIDIAM) following O'CLOCK
if (now.hour() > 12 && !(now.minute() > 30 && now.hour() == 23)) // no PM at NOON
pm();
else if (now.hour() < 12 && !(now.minute() > 30 && now.hour() == 11)) {
am();
}
}
}
makejson.h
//*****************************************************
// This utility creates json text attributes for 16x16
// word clock grid simulation. Standalone or function
//*****************************************************
/*
char words[16][16] = {
1234567890123456
".the..time..is..", // 0 1
"TWENTY.ONE.TWO..", // 16 2
"THREE.FOUR.FIVE.", // 32 3
".SIX.SEVEN.EIGHT", // 48 4
"NINE.TEN.ELEVEN.", // 64 5
".TWELVE.THIRTEEN", // 80 6
"FOURTEEN.FIFTEEN", // 96 7
"SIXTEENSEVENTEEN", // 112 8
"EIGHTEENNINETEEN", // 128 9
".quater.minutes.", // 144 10
"half.until.past.", // 160 11
"ONETWOTHREEFOUR.", // 176 12
".FIVE.SIX.SEVEN.", // 192 13
".EIGHT.NINE.TEN.", // 208 14
".ELEVEN.midnight", // 224 15
"o'clock.pmAMnoon" // 240 16
};
void setup() {
Serial.begin(115200);
for (int k = 0; k < 16; k++) {
for (int j = 0 ; j < 4; j++) {
Serial.print("\"attrs\": { \"text\": \"");
for (int i = 0; i < 4; i++) {
Serial.print(words[i + j * 4][k]);
if (i < 3)
Serial.print("\\n");
}
Serial.print("\" }");
Serial.println();
}
Serial.println();
}
}
void loop() {}
*/
diagram.json
{
"version": 1,
"author": "Anonymous maker",
"editor": "wokwi",
"parts": [
{ "type": "wokwi-arduino-nano", "id": "nano", "top": 62.4, "left": -77.3, "attrs": {} },
{
"type": "wokwi-neopixel-matrix",
"id": "ring1",
"top": -324.48,
"left": -91,
"attrs": { "pixleate": "1", "rows": "16", "cols": "16" }
},
{ "type": "wokwi-vcc", "id": "vcc1", "top": 10.36, "left": 307.2, "attrs": {} },
{ "type": "wokwi-gnd", "id": "gnd1", "top": 105.6, "left": 306.6, "attrs": {} },
{ "type": "wokwi-ds1307", "id": "rtc1", "top": 119.4, "left": 105.7, "attrs": {} },
{
"type": "wokwi-text",
"id": "legendservo1",
"top": -326.4,
"left": -86.4,
"attrs": { "text": "X\nT\nT\nF" }
},
{
"type": "wokwi-text",
"id": "legendservo13",
"top": -240,
"left": -86.4,
"attrs": { "text": "N\nD\nF\nS" }
},
{
"type": "wokwi-text",
"id": "legendservo14",
"top": -144,
"left": -86.4,
"attrs": { "text": "E\nQ\nH\nO" }
},
{
"type": "wokwi-text",
"id": "legendservo15",
"top": -57.6,
"left": -86.4,
"attrs": { "text": "D\nD\nO\nO" }
},
{
"type": "wokwi-text",
"id": "legendservo2",
"top": -326.4,
"left": -57.6,
"attrs": { "text": "T\nW\nH\nS" }
},
{
"type": "wokwi-text",
"id": "legendservo3",
"top": -240,
"left": -57.6,
"attrs": { "text": "I\nT\nO\nI" }
},
{
"type": "wokwi-text",
"id": "legendservo4",
"top": -144,
"left": -57.6,
"attrs": { "text": "I\nU\nA\nN" }
},
{
"type": "wokwi-text",
"id": "legendservo5",
"top": -57.6,
"left": -57.6,
"attrs": { "text": "F\nE\nE\n'" }
},
{
"type": "wokwi-text",
"id": "legendservo6",
"top": -326.4,
"left": -38.4,
"attrs": { "text": "H\nE\nR\nI" }
},
{
"type": "wokwi-text",
"id": "legendservo7",
"top": -240,
"left": -38.4,
"attrs": { "text": "N\nW\nU\nX" }
},
{
"type": "wokwi-text",
"id": "legendservo8",
"top": -146.5,
"left": -35.9,
"attrs": { "text": "G\nA\nL\nE" }
},
{
"type": "wokwi-text",
"id": "legendservo9",
"top": -58.85,
"left": -34.02,
"attrs": { "text": "I\nI\nL\nC" }
},
{
"type": "wokwi-text",
"id": "legendservo10",
"top": -326.4,
"left": -9.6,
"attrs": { "text": "E\nN\nE\nX" }
},
{
"type": "wokwi-text",
"id": "legendservo11",
"top": -240,
"left": -9.6,
"attrs": { "text": "E\nE\nR\nT" }
},
{
"type": "wokwi-text",
"id": "legendservo12",
"top": -146.71,
"left": -9.6,
"attrs": { "text": "H\nR\nF\nT" }
},
{
"type": "wokwi-text",
"id": "legendservo16",
"top": -57.6,
"left": -9.6,
"attrs": { "text": "V\nG\nE\nL" }
},
{
"type": "wokwi-text",
"id": "legendservo17",
"top": -326.4,
"left": 19.2,
"attrs": { "text": "F\nT\nE\nP" }
},
{
"type": "wokwi-text",
"id": "legendservo18",
"top": -240,
"left": 19.2,
"attrs": { "text": "W\nL\nT\nE" }
},
{
"type": "wokwi-text",
"id": "legendservo19",
"top": -146.5,
"left": 14.82,
"attrs": { "text": "T\nT\nX\nW" }
},
{
"type": "wokwi-text",
"id": "legendservo20",
"top": -57.6,
"left": 19.2,
"attrs": { "text": "E\nH\nV\nO" }
},
{
"type": "wokwi-text",
"id": "legendservo21",
"top": -326.4,
"left": 38.4,
"attrs": { "text": "P\nY\nC\nS" }
},
{
"type": "wokwi-text",
"id": "legendservo22",
"top": -240,
"left": 38.4,
"attrs": { "text": "T\nV\nE\nE" }
},
{
"type": "wokwi-text",
"id": "legendservo23",
"top": -144,
"left": 38.4,
"attrs": { "text": "E\nE\nU\nO" }
},
{
"type": "wokwi-text",
"id": "legendservo24",
"top": -57.6,
"left": 38.4,
"attrs": { "text": "W\nT\nE\nC" }
},
{
"type": "wokwi-text",
"id": "legendservo25",
"top": -326.4,
"left": 67.2,
"attrs": { "text": "T\nD\nF\nE" }
},
{
"type": "wokwi-text",
"id": "legendservo26",
"top": -240,
"left": 67.2,
"attrs": { "text": "E\nE\nE\nN" }
},
{
"type": "wokwi-text",
"id": "legendservo27",
"top": -144,
"left": 67.2,
"attrs": { "text": "E\nR\nN\nT" }
},
{
"type": "wokwi-text",
"id": "legendservo28",
"top": -57.6,
"left": 67.2,
"attrs": { "text": "S\nC\nN\nK" }
},
{
"type": "wokwi-text",
"id": "legendservo29",
"top": -329.53,
"left": 91.62,
"attrs": { "text": "I\nO\nO\nV" }
},
{
"type": "wokwi-text",
"id": "legendservo30",
"top": -236.87,
"left": 89.74,
"attrs": { "text": "N\nC\nN\nS" }
},
{
"type": "wokwi-text",
"id": "legendservo31",
"top": -146.3,
"left": 92.87,
"attrs": { "text": "N\nO\nT\nH" }
},
{
"type": "wokwi-text",
"id": "legendservo32",
"top": -58.85,
"left": 93.29,
"attrs": { "text": "I\nN\nC\nK" }
},
{
"type": "wokwi-text",
"id": "legendservo33",
"top": -326.4,
"left": 115.2,
"attrs": { "text": "M\nN\nU\nE" }
},
{
"type": "wokwi-text",
"id": "legendservo34",
"top": -240,
"left": 115.2,
"attrs": { "text": "O\nT\nL\nE" }
},
{
"type": "wokwi-text",
"id": "legendservo35",
"top": -144,
"left": 115.2,
"attrs": { "text": "N\nM\nI\nR" }
},
{
"type": "wokwi-text",
"id": "legendservo36",
"top": -57.6,
"left": 115.2,
"attrs": { "text": "X\nI\nM\nP" }
},
{
"type": "wokwi-text",
"id": "legendservo37",
"top": -326.4,
"left": 144,
"attrs": { "text": "E\nE\nR\nN" }
},
{
"type": "wokwi-text",
"id": "legendservo38",
"top": -240,
"left": 144,
"attrs": { "text": "E\nH\nF\nV" }
},
{
"type": "wokwi-text",
"id": "legendservo39",
"top": -144,
"left": 144,
"attrs": { "text": "I\nI\nL\nE" }
},
{
"type": "wokwi-text",
"id": "legendservo40",
"top": -57.6,
"left": 144,
"attrs": { "text": "O\nN\nI\nM" }
},
{
"type": "wokwi-text",
"id": "legendservo41",
"top": -326.4,
"left": 163.2,
"attrs": { "text": "D\nC\nK\nD" }
},
{
"type": "wokwi-text",
"id": "legendservo42",
"top": -240,
"left": 163.2,
"attrs": { "text": "L\nI\nI\nE" }
},
{
"type": "wokwi-text",
"id": "legendservo43",
"top": -144,
"left": 163.2,
"attrs": { "text": "N\nN\nK\nE" }
},
{
"type": "wokwi-text",
"id": "legendservo44",
"top": -57.6,
"left": 163.2,
"attrs": { "text": "S\nE\nD\nA" }
},
{
"type": "wokwi-text",
"id": "legendservo45",
"top": -326.4,
"left": 192,
"attrs": { "text": "W\nT\nF\nE" }
},
{
"type": "wokwi-text",
"id": "legendservo46",
"top": -240,
"left": 192,
"attrs": { "text": "E\nR\nF\nN" }
},
{
"type": "wokwi-text",
"id": "legendservo47",
"top": -144,
"left": 192,
"attrs": { "text": "E\nU\nP\nF" }
},
{
"type": "wokwi-text",
"id": "legendservo48",
"top": -57.6,
"left": 192,
"attrs": { "text": "E\nC\nN\nM" }
},
{
"type": "wokwi-text",
"id": "legendservo49",
"top": -326.4,
"left": 220.8,
"attrs": { "text": "I\nW\nI\nI" }
},
{
"type": "wokwi-text",
"id": "legendservo50",
"top": -240,
"left": 220.8,
"attrs": { "text": "V\nT\nT\nT" }
},
{
"type": "wokwi-text",
"id": "legendservo51",
"top": -144,
"left": 220.8,
"attrs": { "text": "T\nT\nA\nO" }
},
{
"type": "wokwi-text",
"id": "legendservo52",
"top": -57.6,
"left": 220.8,
"attrs": { "text": "V\nT\nI\nN" }
},
{
"type": "wokwi-text",
"id": "legendservo53",
"top": -326.4,
"left": 240,
"attrs": { "text": "S\nO\nV\nG" }
},
{
"type": "wokwi-text",
"id": "legendservo54",
"top": -240,
"left": 240,
"attrs": { "text": "E\nE\nE\nE" }
},
{
"type": "wokwi-text",
"id": "legendservo55",
"top": -144,
"left": 240,
"attrs": { "text": "E\nE\nS\nU" }
},
{
"type": "wokwi-text",
"id": "legendservo56",
"top": -57.6,
"left": 240,
"attrs": { "text": "E\nE\nG\nO" }
},
{
"type": "wokwi-text",
"id": "legendservo57",
"top": -326.4,
"left": 268.8,
"attrs": { "text": "O\nL\nE\nH" }
},
{
"type": "wokwi-text",
"id": "legendservo58",
"top": -240,
"left": 268.8,
"attrs": { "text": "N\nE\nE\nE" }
},
{
"type": "wokwi-text",
"id": "legendservo59",
"top": -144,
"left": 268.8,
"attrs": { "text": "E\nS\nT\nR" }
},
{
"type": "wokwi-text",
"id": "legendservo60",
"top": -57.6,
"left": 268.8,
"attrs": { "text": "N\nN\nH\nO" }
},
{
"type": "wokwi-text",
"id": "legendservo61",
"top": -327.08,
"left": 290.71,
"attrs": { "text": "R\nO\nX\nT" }
},
{
"type": "wokwi-text",
"id": "legendservo62",
"top": -240,
"left": 290.71,
"attrs": { "text": "R\nN\nN\nN" }
},
{
"type": "wokwi-text",
"id": "legendservo63",
"top": -146.7,
"left": 293.01,
"attrs": { "text": "N\nC\nF\nP" }
},
{
"type": "wokwi-text",
"id": "legendservo64",
"top": -58.13,
"left": 295.47,
"attrs": { "text": "R\nL\nT\nN" }
},
{
"type": "wokwi-resistor",
"id": "r1",
"top": 42.35,
"left": 38.4,
"attrs": { "value": "330" }
},
{ "type": "chip-1000uf", "id": "chip1", "top": 68.22, "left": 196.8, "attrs": {} }
],
"connections": [
[ "gnd1:GND", "ring1:GND", "black", [ "h-220.8", "v-28.8" ] ],
[ "vcc1:VCC", "ring1:VCC", "red", [ "v19.2", "h-211.2" ] ],
[ "rtc1:GND", "nano:GND.1", "black", [ "h0" ] ],
[ "rtc1:5V", "nano:5V", "red", [ "h0" ] ],
[ "rtc1:SDA", "nano:A4", "green", [ "h0" ] ],
[ "rtc1:SCL", "nano:A5", "green", [ "h0" ] ],
[ "r1:2", "ring1:DIN", "green", [ "h18", "v-9.6" ] ],
[ "nano:5", "r1:1", "green", [ "v0" ] ],
[ "chip1:OUT", "vcc1:VCC", "green", [ "h21.01", "v-28.8" ] ],
[ "chip1:IN", "gnd1:GND", "green", [ "v0", "h21.01", "v28.8" ] ],
[ "gnd1:GND", "rtc1:GND", "black", [ "v0", "h-220.8", "v28.8" ] ]
],
"dependencies": {}
}