Word Clock

I am seeking some help for a word clock build. I am using SuperMakeSomething's great tutorial (Arduino + Neopixel Word Clock - Super Make Something Episode 6 - YouTube) but would like to expand on the project by:

  1. Displaying the time increments every minute, not every five
  2. Utilise a DS3231 RTC to set and maintain the time via some tactile buttons

Point 1. above I have under control but I am struggling with Point 2.

I need to replace the Time settings in SuperMakeSomething's code with some DS3231 code.

See attached my two sets of code - I need to migrate/combine the "DS3231_M_Adjust.ino" into "worldClockCode.ino" - but I am struggling at the moment.

Can anybody help?

wordClockCode.ino (7.4 KB)

DS3231_M_Adjust.ino (314 Bytes)

Can we see your struggle please?

Attach code using code tags as described in the topics "How to use forum" etc.
My phone has no IDE so no help from me now.

Latest attempt below. The RTC has been set when deployed via rtc.adjust(DateTime(2020, 8, 12, 16, 32, 0)); in the setup & the serial monitor correctly returns the time. However, the two tactile buttons do not alter the time.

Once I have this sorted the next hurdle will be to alter which LED's fire based on the time.

Any comments/advise would be greatly receieved.

Thank you.

#include <Adafruit_NeoPixel.h>
#include <TimeLib.h>
#include "RTClib.h"

RTC_DS3231 rtc;

// Which pin on the Arduino is connected to the NeoPixels?
#define PIN           6

// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS     32

// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
// Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest
// example for more information on possible values.
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

// Declare integer array with size corresponding to number of Neopixels in chain
// int individualPixels[NUMPIXELS];

//Declare pins for decrementing/incrementing current time by 5 minutes
#define PLUSONEMIN 4
#define PLUSONEHOUR  5

// Current and previous states for button pins -- in setup initialize all to HIGH
int minusPrevState=HIGH;
int minusCurrState=HIGH;
int plusPrevState=HIGH;
int plusCurrState=HIGH;

// Time variables
int h;
int m;
int s;

// RGB color variables
int red=25;
int green=25;
int blue=25;

void setup()
{

    if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    abort();
    rtc.adjust(DateTime(2020, 8, 12, 16, 32, 0));
  }

  pinMode(PLUSONEMIN, INPUT_PULLUP); //Define pin as input, enable pull-up resistor
  pinMode(PLUSONEHOUR, INPUT_PULLUP); //Define pin as input, enable pull-up resistor 
  
  setTime(8,14,0,4,8,2020); //Initialize current time as Midnight/noon 08/31/2015
  pixels.begin(); //Begin Neopixel string
  Serial.begin(9600); //Begin Serial for debugging purposes
  
}

void loop()
{
    DateTime now = rtc.now();
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.println();
    
  //Declare integer array with size corresponding to number of Neopixels in chain
  int individualPixels[NUMPIXELS]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
  
  /* Check for button presses & reset time if necessary */
  minusCurrState=digitalRead(PLUSONEMIN); //Get current state of PLUSONEMIN button
  /* If current state is different from previous state and value is now LOW, add one minute from current time */
  if ((minusCurrState!=minusPrevState) && (minusCurrState==HIGH)){
    adjustTime(1*60); //Shift time one minute forwards
    minusPrevState=minusCurrState;
  }
  else{
   minusPrevState=minusCurrState; 
  }
  
  plusCurrState=digitalRead(PLUSONEHOUR); //Get current state of PLUSONEHOUR button
  /* If current state is different from previous state and value is now LOW, add one hour from current time */
  if ((plusCurrState!=plusPrevState) && (plusCurrState==HIGH)){
    adjustTime(60*60); //Shift time on hour forward
    plusPrevState=plusCurrState;
  }
    else{
   plusPrevState=plusCurrState; 
  }
    
  /* Get current time */
  h=hourFormat12();    // Returns the hour of current time between 1-12
  m=minute();          // Returns the minute of current time
  s=second();          // Returns the second of current time (not used, included for completeness)
  
  //Serial.print(h);
  //Serial.print(":");
  //Serial.print(m);
  //Serial.print(":");
  //Serial.println(s);

  /* Parse time values to light corresponding pixels */
  individualPixels[0]=1; //Light "IT"
  individualPixels[1]=1; //Light "IS" 
  
  /* Minutes between 0-5 - Light "O CLOCK" */
  if ((m>=0 && m<5)){
    individualPixels[28]=1;
    individualPixels[29]=1;
  }
  
  /* Minutes between 5-10 or 55-60 - Light "FIVE," "MINUTES" */
  if ((m>=5 && m<10) || (m>=55 && m<60)){
    individualPixels[8]=1;
    individualPixels[9]=1;
    individualPixels[10]=1;
  }
  
  /* Minutes between 10-15 or 50-55 - Light "TEN," "MINUTES" */
  if ((m>=10 && m<15) || (m>=50 && m<55)){
    individualPixels[2]=1;
    individualPixels[9]=1;
    individualPixels[10]=1;
  }
  
  /* Minutes between 15-20 or 45-50 - Light "QUARTER" */
  if ((m>=15 && m<20) || (m>=45 && m<50)){
    individualPixels[6]=1;
    individualPixels[7]=1;
  }
  
  /* Minutes between 20-25 or 40-45 - Light "TWENTY," "MINUTES" */
  if ((m>=20 && m<25) || (m>=40 && m<45)){
    individualPixels[4]=1;
    individualPixels[5]=1;
    individualPixels[9]=1;
    individualPixels[10]=1;
  }  

  /* Minutes between 25-30 or 35-40 - Light "TWENTY," "FIVE," "MINUTES" */
  if ((m>=25 && m<30) || (m>=35 && m<40)){
    individualPixels[4]=1;
    individualPixels[5]=1;
    individualPixels[8]=1;
    individualPixels[9]=1;
    individualPixels[10]=1;
  }

  /* Minutes between 30-35 - Light "HALF" */
  if ((m>=30 && m<35)){
    individualPixels[3]=1;
  }
  
  /* Minutes between 5-35 - Light "PAST" */
  if ((m>=5) && (m<35)){
    individualPixels[14]=1;
  }
  
  /* Minutes between 35-60 - Light "TO" & MODIFY CURRENT HOUR VALUE */
  if (m>=35){
    individualPixels[13]=1;
    h++; //Add 1 from current hour
    /*Set time to twelve for hour around midnight, noon */
    if (h==0){
      h=12; 
    }
    /*Corner case for 12:35-12:59 */
    if (h==13){
      h=1;
    }
  }

  /* Hour=1 - Light "ONE" */
  if (h==1){
    individualPixels[12]=1;
  }
  
  /* Hour=2 - Light "TWO" */
  if (h==2){
    individualPixels[11]=1;
  }
  
  /* Hour=3 - Light "THREE" */
  if (h==3){
    individualPixels[15]=1;
    individualPixels[16]=1;    
  }
  
  /* Hour=4 - Light "FOUR" */
  if (h==4){
    individualPixels[17]=1;
  }
  
  /* Hour=5 - Light "FIVE" */
  if (h==5){
    individualPixels[18]=1;
  }
  
  /* Hour=6 - Light "SIX" */
  if (h==6){
    individualPixels[23]=1;
  }
  
  /* Hour=7 - Light "SEVEN" */
  if (h==7){
    individualPixels[21]=1;
    individualPixels[22]=1;
  }
  
  /* Hour=8 - Light "EIGHT" */
  if (h==8){
    individualPixels[19]=1;
    individualPixels[20]=1;
  }
  
  /* Hour=9 - Light "NINE" */
  if (h==9){
    individualPixels[24]=1;
  }
  
  /* Hour=10 - Light "TEN" */
  if (h==10){
    individualPixels[25]=1;
  }
  
  /* Hour=11 - Light "ELEVEN" */
  if (h==11){
    individualPixels[26]=1;
    individualPixels[27]=1;
  }
  
  /* Hour=12 - Light "TWELVE" */
  if (h==12){
    individualPixels[30]=1;
    individualPixels[31]=1;
  }
  
  /* Light pixels corresponding to current time */
  for (int i=0; i<sizeof(individualPixels); i++){
    if (individualPixels[i]==1){
      pixels.setPixelColor(i, pixels.Color(red,green,blue)); //Set Neopixel color
    }
    else{
      pixels.setPixelColor(i,pixels.Color(0,0,0));
    }
  }
  
  pixels.show(); //Display Neopixel color
  
//  /* Clear pixel values for re-assignment during next iteration */
//  for (int j=0; j<sizeof(individualPixels); j++){
//      individualPixels[j]=0; //Set array values to 0
//      pixels.setPixelColor(j, pixels.Color(0,0,0)); //Set Neopixel color to 0 brightness, i.e. off
//  }
  
}

Have you tested the button switches separately, with a simple button test only sketch?

The buttons work correctly with the original sketch from SuperMakeSomething's but since adding the RTC code and commenting out the time lines from SuperMakeSomething's code they are unresponsive

You seem to be setting the time on both TimeLib and RTCLIB. I suspect you are then getting the time from RTCLIB and the buttons are adjusting the time in TimeLib.

Look for examples of how to use TimeLib with an RTC. Or remove TimeLib and just use the RTCLIB.

@johnwasser - your right! Soley using RTCLib now and got the buttons working with rtc.adjust:

 rtc.adjust(rtc.now()+60); //Shift time one minute forwards
rtc.adjust(rtc.now()+3600); //Shift time on hour forward

Now - the final hurdle I have is when the minutes past the hour are greater than or equal to 35minutes I want the LED's to light the next hour. in example 8:35 would light twenty five minutes to 9. The original code details:

/* Minutes between 35-60 - Light "TO" & MODIFY CURRENT HOUR VALUE */
if (now.minute()>=35){
individualPixels[2]=1;
h++; //Add 1 from current hour

h is specified as an int:

int h;

and I have set h in the void loop() to now.hour():

/* Get current time */
h=now.hour();

But when testing on the LED's the LED indicating hour 9 does not light.

Thoughts?

Ok - so I have managed to get to the bottom of my issue. I re-established the RTC time values to the variables h, m & s and the h++ now correctly lights the next hour when >35 minutes past the hour.

One final like I would like to incorporate into the sketch would be to have a button cycle through some different colour options. At the moment the RGB values are stored as variables:

int red=5;
int green=5;
int blue=5;

And then they are lit accordingly as per the time at the end of the void loop():

  /* Light pixels corresponding to current time */
  for (int i=0; i<sizeof(individualPixels); i++){
    if (individualPixels[i]==1){
      pixels.setPixelColor(i, pixels.Color(red,green,blue)); //Set Neopixel color
    }
    else{
      pixels.setPixelColor(i,pixels.Color(0,0,0));    
    }
  }

I have looked at the buttoncycler sketch for inspiration/guidance but I cannot see how I can implement this into my sketch to cycle through different colours.

Is it possible to cycle through a set of variables via a button push?

nytram6:
Is it possible to cycle through a set of variables via a button push?

Button push increments counter*. Use the count value in a switch/case, if/else or, as an index into an array.

  • Study statechangedetection in IDE -> file/examples/digital.

Another thing you may want to look at is controlling "IT IS" vs leaving it on all the time.
The reason I mention this is that in most word clocks they are on all the time.
I built a word clock and after a few years the LEDs controlling "IT IS" started to be a bit dimmer than the others.
I had to replace the LEDs.

I updated my code to only turn on "IT IS" when it was *exactly that minute.
i.e. if it is 10 minutes past 1 then "IT IS" is on. But if it is 7,8,9,11,12 the "IT IS" is off.

I also modified how "MINUTES" is lit up since it also showed some dimming effects after several years.
The code "MINUTES" if off if the minute is rounded up
i.e.if the time hasn't happened yet due to rounding up "MINUTES" is off.
examples:
if the time is 02:05 it says "it is five minutes past two"
if the time is 02:06 it says "five minutes past two"
if the time is 02:07 it says "five minutes past two"
if the time is 02:08 it says "ten past two"
if the time is 02:09 it says "ten past two"
if the time is 02:10 it says "it is ten minutes past two"
if the time is 02:11 it says "ten minutes past two"
if the time is 02:12 it says "ten minutes past two"
if the time is 02:13 it says "quarter past two"
if the time is 02:14 it says "quarter past two"
if the time is 02:15 it says "it is a quarter past two"

I also created a the word "A" from the "A" in "HALF".
That is used for when it is minute to or a minute after the hour.
i.e.
"it is a minute to one"
"it is a minute past one"

The other thing I did is add a light sensor to adjust the brightness based on ambient light.
And a RCWL-0516 motion detector to turn off the display if there hasn't been any motion for while.
The motion module works great, it can see right through my plywood base and wire matrix and glass front.
It can "see" person from around 15-20 ft away even if very small movements like a hand or a few fingers.
It can also see backwards through the wall a few feet.

And since this was for my elderly mother with dementia,
I used an ESP8266 module and I sync the time by getting the time from a network server
http://worldtimeapi.org/api/ip
It returns the local time based on your ip address.
This is really cool as it handles the local time conversion including DST conversions without having to configure anything.
I simply parse JSON fields to set the RTC.
I do this when the device powers up and every morning at 02:00:10 (10 seconds after 2am)
This will automatically correct the time on DST switches.

This ensures that she doesn't ever have to set the time manually.
Its pretty cool, If you use the manual buttons to set a time (or mess it up) then unplug it and plug it back in,
it will automatically set the time to correct time.

I use wifimanager to ensure that the clock does not have any hard coded wifi credentials and the user can set certain parameters like the motion timeout period, min brightness, max brightness.
The min brightness if set to 0 will turn off the display in the dark/low light even if there is motion.

One other thing to keep in mind is the time_t value.
Some systems & libraries still use 32 bit values.
If they treat the value as a signed 32 bit int, the local time conversion will fail in Jan 2038.

That is less than 18 years away.
The TimeLib library uses an unsigned 32 bit int so it won't fail until 2106

--- bill

@dougp - thank you for this pointer, I have had a quick look and it looks like this is someinthg I can use.

@bperrybap - thank you ever so much for your comprehensive summary. The dimming effects of LED's that will be on more than others is not something that I have considered, maybe I could build some more "IT IS" into the first couple of lines to ease the burden on just one set.

I love the sound of the light sensor and the motion detector - what light sensor and library did you use? I was thinking of cycling different brightnesses via the button, but an intiuitive method via ambient light would be much smarter. Did you use Neopixels in your build? Woud you be open to sharing your sketch with me? I would love to see how your created all of the bits you have described.

The ESP8266 time set is an interesting idea - this would be fine for me, but I intend on making a few of these as presents for friends so the SSID & pswd code would have to be changed via the code for each unit - unless there would be a way of changing it remotely via bluetooth say? Utilising a Nano 33 could be an option maybe?

Anyway, thanks again for your insight. If you are happy to share your sketch that would be greatly appreciated.

nytram6:
@bperrybap - thank you ever so much for your comprehensive summary. The dimming effects of LED's that will be on more than others is not something that I have considered, maybe I could build some more "IT IS" into the first couple of lines to ease the burden on just one set.

If I understand you, more LEDs doesn't solve the potential issue as they would still be on all the time. It is on time that causes the LEDs to fade as LEDs slowly wear out over time.

I love the sound of the light sensor and the motion detector - what light sensor and library did you use?

I used just a simple, cadmium LDR hooked to an analog input. No library needed.

I was thinking of cycling different brightnesses via the button, but an intiuitive method via ambient light would be much smarter. Did you use Neopixels in your build?

Neopixels or WS2812 and WS2812B LEDs were not really that popular and available when I built my clock.
I used a HT16K33 on a module that Adafruit sold (looks like they may not make it anymore)
It was i2c based and I had to wire up all the individual white leds in a 8x13 matrix.
If I were doing it again I'd definitely use WS2812 leds.
Doesn't matter what type of leds are used or how you interface to them.
If they support dimming you can use the analog reading from the light sensor to the LED brightness.

The ESP8266 time set is an interesting idea - this would be fine for me, but I intend on making a few of these as presents for friends so the SSID & pswd code would have to be changed via the code for each unit - unless there would be a way of changing it remotely.

Like I said I use WiFimanager to not have to hard code anything.
The esp8266 in the clock creates a WiFi access point and you can connect to it with any WiFi device like a cell phone.
You set the SSID and password for the network using that. It is similar to the way many IoT devices do it, including WiFi thermostats, sprinkers, water heaters etc.
Using WiFimanager you can also provide your own configuration variables.
I use that capability to allow configuring other word clock configuration variables.

--- bill

Hi @bperrybap, just wanted to touch base on this again if you can spare me a few moments.

I have my project all set up as intended, overcoming the hurdles details above. But you had me intrigued with the RCWL-0516 motion detector & ESP8266 wifi module - so I have jumped in and trying to configure both devices with my project. The ESP8266 would be a nice to have, but the RCWL-0516 is a must, to minimise the fading of those "IT IS" LED's

  1. I am strugling with the ESP8266 configuation and wifimanager sketch - I am trying to hook this up to my Arduino Nano but cant find a good tutorial on how to upload the sketch to it and I think it might be a bit above my skillset. Do you have a good tutorial or suggest how I might get this talking with the serial monitor?

  2. I can read HIGH LOW readings from the sensor, but need some guidance on how to read this every "x" amount of minutes and store the values so my code can check if it has read HIGH in the past "x" amounts of minutes then keep the LEDS on, if not, turn LEDS off.

If you are able to share this portion of your sketch it would be greatly appreciated, please fee lfree to DM me.

Thank you in advnace.