Time? Yet Another Word Clock

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": {}
}
2 Likes

Many beginners ask questions on the forum about why their ws2812 projects are not lighting up correctly. We always advise to use a resistor and capacitor, as shown on the famous Adafruit tutorial, which beginners often omit. The beginners also often defend themselves by saying that they saw someone else's project which works despite not having those components.

How does the painting of the farmer relate to your project?

Yes, duly noted and installed. The AI generated image is a nod to "time."

Another common beginner error is to forget to add a common ground between circuits powered from different sources.

Time? I don't get it.... Pigs and apples... Maybe it will come to me later... :thinking:

The farmer's fingers look strange...

Noted and corrected. It is always good to have a review after making changes.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.