Help with LED strip clock code

I am trying to make a simple clock made from LED strips. I have hardly any experience with coding but have looked at a lot of examples online for LED clocks. This clock does not need to be very accurate meaning it does not need to have an RTC connected to it. This project is for a high school marching band prop and they want 6 of these clocks on the field during their show. Each clock will run at different speeds meaning there could be more or less than 60 seconds between minute changes. Some clocks will run fast and some will run slow. The clocks will only have hours and minutes. I'll eventually want to have all 6 clocks run off of a single arduino if possible but we can tackle that at some other time. Right now I just want to prototype a single LED strip clock using 5V WS2812b LEDs and get the code working.

To start this project I made this clock Prototype Clock to use as the prototype . I did not need the Bluetooth, RTC, or temp/humidity sensor. I just used the arduino and leds. I wired up the LEDs and dots just like his project. His code was to hard for me to decouple so I had to research another way. I ran into this guys clock and tried using his code Clock and Code. I am having a hard time getting his code to work. Maybe I should start from scratch but I'm not really sharp enough to know where to start.

24 17 8 1 <----- Arduino connects to LED 1.
29 25 22 18 15 13 9 6 2 LED connections follow in numerical order
30 23 14 7
28 26 21 19 16 12 10 5 3
27 20 11 4

Hour Hour dots minute minute

This is the code I started with:

#include <FastLED.h> 
#define NUM_LEDS 28
#define DATA_PIN 6
 
CRGB leds[NUM_LEDS];
 
int currentTimeHours = 16;
int currentTimeMinutes = 17 ;
 

 
 boolean if_in_array(int array[], int element); 
 boolean if_in_array(int array[], int element) {
 for (int i = 0; i < 7; i++) {
      if (array[i] == element) {
          return true;
      }
    }
  return false;
 }
 
int turnon(int t[7], int digit){       //turn on leds that are required for that digit and turn other leds off
  for (int i = 1; i < 8; i ++){
      if (if_in_array(t,i)){ 
        
        leds[i-1 + digit*7]= CRGB::Green;
        }
      else {
        leds[i-1+ digit*7] = CRGB::Black;
        }
    }
  }
//  array with the saved led positions for each digit from 0 to 9
int ref[10][7] = {{1,2,3,4,5,6},{2,3},{1,2,4,5,7},{1,2,7,3,4},{2,3,6,7},{1,3,4,6,7},{1,3,4,5,6,7},{1,2,3},{1,2,3,4,5,6,7},{1,2,3,4,6,7}};

 
void setup() {
  FastLED.addLeds<WS2812, DATA_PIN>(leds, NUM_LEDS); }
 
void loop(){  
  
 
 //int minutes = millis() / 60000 ; // get number of minutes since the arduino is turned on
 int minutes = millis() / 1000 ; // get number of minutes since the arduino is turned on
 minutes = minutes + currentTimeHours*60 + currentTimeMinutes; // this is used to set the current time
 int hours = minutes/60;
 minutes = minutes - hours*60;
 hours = hours - (hours / 24)*24;
 
 turnon(ref[minutes-(minutes/10)*10],0);
 turnon(ref[minutes/10],1);
 turnon(ref[hours-(hours/10)*10],2); 
 turnon(ref[hours/10],3); 
 FastLED.show();
 
}

I can get the minutes to work but the dots are messing me up since they are in the middle of the array. I don't know how to code around the dots. If there is an easier way to code this whole clock please help me out. I was just trying to use something that was already out there that was similar to what I need the LED clock to do. Thanks in advance!

Sorry I haven't actually looked at your code but frankly, the code is the easy bit!

May I ask if you have checked as to whether these LEDs are individually and in a group, sufficiently bright to be seen in the way you require in daylight? How big do you expect the display to be, from how far away must it be viewed and importantly, in what direction? How do you plan to power it?

NUM_LEDS = 28 ?
how is that possible?
It makes no sense.
All together there must be at least 21 LEDs per digit and there are 4 digits.
What am I missing?

4 digits with just 1 LED/segment to start?

Paul__B, Yes, you raise all good questions that I have thought about also. That prototype that I am making is only 7" tall which is probably not big enough to see clearly from spectator stands. I am going to make some cheap digits probably 14" tall to start with. We will also need to build some type of black painted hood around the clocks to keep out as much light as possible but still be viewable from the stands. Its going to be powered from 20V Dewalt tool batteries. Same setup that I used on that drumline project you helped me with a month or so ago.

ieee488 and CrossRoads, that NUM_LED variable is wrong and should be 30. You are correct CrossRoads, 1 LED/segment for 4 digits and the dots also have 1 LED a piece. I am using this just to get the code working and to understand it. The real clocks will be bigger and have multiple LEDs/segment.

I am still not sure where 30 comes from.
Each segment is 3 LEDs. 7 segments makes up 1 digit.
There are 4 digits in the clock.

ieee488, my prototype only has 1 LED/segment, 7 segments per digit, 4 digits, 2 dots = 30 LEDs

Ah, Got it.

I have some LED strips, so I was thinking about doing this.

Except, I am starting with the most significant digit on the left.
It is easier for my brain. Not sure why.

To make the programming easier, I have the the 2 dots at the end of the leds array that is in the code.

    0  1  2             21 22 23
17           3       28
16           4       27
15           5       26
   18 19 20
14           6
13           7
12           8
   11 10  9

Is how I am thinking about doing it.

Cool, I just found this YouTube video and started with this guys idea as a prototype. He has a RTC, humidity/temp sensor, and bluetooth module. Trying to decouple his code from all those devices was not working out for me.

I am going to put two LEDs for the ':' and call it a day.

I don't know how to code around the dots.

Why not just make an array to hold the number of the first led in each segment. According to your diagram, but assuming the first digit starts at led 0, not led 1 as shown, that would be:

int digitStart[4] = {0, 7, 16, 23};

Then calculate the led position like this:

leds[i-1 + digitStart[digit]]= CRGB::Green;

Thanks PaulRB! Let me see if I can get your idea to work and I will get back with you.

PaulRB, your code changes worked perfectly. The next thing 2 things I noticed on this code is when the hours are in the single digits the far left digit displays a zero. I would like to know how to make it not display anything until it is needed to display a significant digit. For instance for hours 1-9 it displays it as 01, 02, 03, 04, etc, etc.

The other thing it does is display the hour of 00 after hour 11. It should display 12 after 11 and then display a 1 in the hour column after 12. Not sure how to code that either.

Updated code:

#include <FastLED.h> 
#define NUM_LEDS 30
#define DATA_PIN 6
 
CRGB leds[NUM_LEDS];
 
int currentTimeHours = 11;
int currentTimeMinutes = 57 ;
 
int digitStart[4] = {0, 7, 16, 23};
 
 boolean if_in_array(int array[], int element); 
 boolean if_in_array(int array[], int element) {
 for (int i = 0; i < 7; i++) {
      if (array[i] == element) {
          return true;
      }
    }
  return false;
 }
 
int turnon(int t[7], int digit){       //turn on leds that are required for that digit and turn other leds off
  
  for (int i = 1; i < 8; i ++){
      if (if_in_array(t,i)){ 
        
        leds[i-1 + digitStart[digit]]= CRGB::Red;
        }
      else {
        leds[i-1+ digitStart[digit]] = CRGB::Black;
        }
    }
  }

//  array with the saved led positions for each digit from 0 to 9
int ref[10][7] = {{1,2,3,4,5,6},{2,3},{1,2,4,5,7},{1,2,7,3,4},{2,3,6,7},{1,3,4,6,7},{1,3,4,5,6,7},{1,2,3},{1,2,3,4,5,6,7},{1,2,3,4,6,7}};

 
void setup() {
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS); }
 
void loop(){  
  
 
 //int minutes = millis() / 60000 ; // get number of minutes since the arduino is turned on
 int minutes = millis() / 1000 ; // get number of minutes since the arduino is turned on
 minutes = minutes + currentTimeHours*60 + currentTimeMinutes; // this is used to set the current time
 int hours = minutes/60;
 minutes = minutes - hours*60;
 hours = hours - (hours / 12)*12;
 
 turnon(ref[minutes-(minutes/10)*10],0);
 turnon(ref[minutes/10],1);
 turnon(ref[hours-(hours/10)*10],2); 
 turnon(ref[hours/10],3); 

 FastLED.show();
 
}

may be I'm to late to that story, but if you need a 7 segment library made from WS2812b you could check my library: Arduino Noiasca Neopixel Display Library (7 segment with WS2811)

The example 30_clock_basic should give you a quick start.

The answer to both those questions is that before you execute your "turnon" function for digits 2 and 3, you decide what you want to display. Either modify "turnon" to include a blank digit, perhaps with an index of -1 (adding 1 before you actually index the array) or 10, or make a second function "turnoff" to blank a nominated digit.

So if the hours number is less than 10, you turn off digit 3 and display only digit 2, or if the hours are zero, you dis[play 12 if you insist on 12 hour time. You do this in the section of code where you call "turnon" for the hours.

noiasca - thanks for showing me this. I'll take a look and mess around with it
Paul__B - thanks for the reply. I'll try to code it but where I get stumped is syntax. I am not a programmer

Forum, I am NOT a coder by trade and trying to understand this copied code to make work for my project. I do not understand these statements.

boolean if_in_array(int array[], int element); 
 boolean if_in_array(int array[], int element) {
 for (int i = 0; i < 7; i++) {
      if (array[i] == element) {
          return true;
      }
    }
  return false;
 }

You can go back and look at the last code snippet I posted to get the full code listing. I understand arrays and counters but I am stumped at "int element". It is not defined from what I can tell anywhere in the code so when I see the statement:

if (array[i] == element)

what is "array " being compared to here?

It's part of this array called "ref":

int ref[10][7] = {{1,2,3,4,5,6}, ....};

"ref" is an array of arrays of ints, or a 2D array of ints.

In loop(), one of the elements of ref, which is itself an array of ints, is passed to function turnon():

turnon(ref[minutes-(minutes/10)*10],0);

In turnon(), the array of ints, now called "t", is passed to function if_in_array();

if (if_in_array(t,i)){

In if_in_array(), each of the elements of the array, now called "array", the elements of which are ints, are compared to a particular value:

if (array[i] == element) {

I'm not surprused you can't understand what's going on, not being an experienced coder. This code is over-complex, obscure and inefficient! It feels like it may have been translated from another language, maybe Python, by an inexperienced C coder, where the arrays might have been lists. It's inefficient because the function if_in_array() is needed to search through an array to find a particular value. It would me more sensible to have designed the array ref as a 2D array of booleans, so to know if a particular led should be on, it would be neccessary only to check one boolean element in a known position.

Even more efficient would be to encode the booleans as bits packed into bytes, and use bitRead() to check them.

Thanks for the explanation PaulRB. It's hard to wrap my brain around array of arrays, etc, etc. Maybe I should investigate noiasca's library he wrote. It looks more like what you are talking about. Let me try to implement his code and see where I can get with it. I posted his basic clock example below which is really all I am doing. Stay tuned

/* Noiasca Neopixel Display
   30 clock two displays
   handling HH MM SS as three separate displays

   by noiasca
   2020-05-04
*/

const byte ledPin = 12;                // Which pin on the Arduino is connected to the NeoPixels?
const byte numDigits = 2;              // How many digits (numbers) are available on each display
const byte startPixelHH = 0;           // the display starts with HH with the first pixel
const byte startPixelMM = 32;          // the MM are in the middle (start at 0 + digits for 10H + digits for H + 2 for the colon)
const byte startPixelSS = 64;          // seconds are on the far right, therefore we need the highest pixel offset
const byte pixelPerDigit = 15;         // all pixels, including decimal point pixels if available at each digit
const byte addPixels = 4;              // unregular additional pixels to be added to the strip (e.g. a double point for a clock 12:34:56)

const uint16_t ledCount(pixelPerDigit * numDigits * 3 + addPixels);
/*
   Segments are named and orded like this

          SEG_A
   SEG_F         SEG_B
          SEG_G
   SEG_E         SEG_C
          SEG_D          SEG_DP

  in the following constant array you have to define
  which pixels belong to which segment
*/

typedef uint16_t segsize_t;                               // fit variable size to your needed pixels. uint16_t --> max 16 Pixel per digit
const segsize_t segment[8] {
  0b0000000000000011,  // SEG_A
  0b0000000000001100,  // SEG_B
  0b0000000000110000,  // SEG_C
  0b0000000011000000,  // SEG_D
  0b0000001100000000,  // SEG_E
  0b0000110000000000,  // SEG_F
  0b0011000000000000,  // SEG_G
  0b1100000000000000   // SEG_DP if you don't have a decimal point, just leave it zero
};

#include <Adafruit_NeoPixel.h>                                      // install Adafruit library from library manager
Adafruit_NeoPixel strip(ledCount, ledPin, NEO_GRB + NEO_KHZ800);    // create neopixel object like you commonly used with Adafruit

#include <Noiasca_NeopixelDisplay.h>                                     // download library from: http://werner.rothschopf.net/202005_arduino_neopixel_display.htm
// in this sketch we handle HH and MM as two individual displays:
Noiasca_NeopixelDisplay displayHH(strip, segment, numDigits, pixelPerDigit, startPixelHH);  // create display object, handover the name of your strip as first parameter!
Noiasca_NeopixelDisplay displayMM(strip, segment, numDigits, pixelPerDigit, startPixelMM);  // create display object, handover the name of your strip as first parameter!
Noiasca_NeopixelDisplay displaySS(strip, segment, numDigits, pixelPerDigit, startPixelSS);  // create display object, handover the name of your strip as first parameter!

void blinkColon()
{
  static uint32_t previousTimestamp = 0;
  static bool state = 0;
  uint32_t actualTimestamp = millis() / 500;
  if (actualTimestamp != previousTimestamp)
  {
    previousTimestamp = actualTimestamp;
    strip.setPixelColor(pixelPerDigit * 2 + 0, 0x0000FF * state);  
    strip.setPixelColor(pixelPerDigit * 2 + 1, 0x0000FF * state);  
    strip.setPixelColor(pixelPerDigit * 4 + 2, 0x0000FF * state);  
    strip.setPixelColor(pixelPerDigit * 4 + 3, 0x0000FF * state);
    strip.show();
    state = !state;
  }
}

void updateSecond()
{
  static uint32_t previousSecond = -1;
  uint32_t actualSecond = (millis() / 1000UL) % 60 ;
  if ( actualSecond != previousSecond)
  {
    previousSecond = actualSecond;
    Serial.println(actualSecond);
    displaySS.setCursor(0);
    if (actualSecond < 10) displaySS.print("0");
    displaySS.print(actualSecond);
  }
}

void updateMinute()
{
  static uint32_t previousMinute = -1;
  uint32_t actualMinute = (millis() / 1000UL) / 60 % 60 ;
  if ( actualMinute != previousMinute)
  {
    previousMinute = actualMinute;
    Serial.println(actualMinute);
    displayMM.setCursor(0);
    if (actualMinute < 10) displayMM.print("0");
    displayMM.print(actualMinute);
  }
}

void updateHour()
{
  static uint32_t previousHour = -1;
  uint32_t actualHour = (millis() / 1000UL) / 3600 % 24 ;
  if ( actualHour != previousHour)
  {
    previousHour = actualHour;
    Serial.println(actualHour);
    displayHH.setCursor(0);
    if (actualHour < 10) displayHH.print(" ");
    displayHH.print(actualHour);
  }
}

void setup()
{
  Serial.begin(115200);
  Serial.println(F("\nNoiascaNeopixelDisplay\n30 clock two displays"));

  strip.begin();                   // INITIALIZE NeoPixel strip object (REQUIRED)
  strip.show();                    // Turn OFF all pixels ASAP
  strip.setBrightness(50);         // Set BRIGHTNESS to about 1/5 (max = 255)
  strip.clear();                   // clears the full strip (all displays assigned to this strip!)

  displayHH.setColorFont(0xAAAA00);
  displayMM.setColorFont(0x880044);

  Serial.println(F("Print 1234 on your display"));
  displayHH.print("88");
  displayMM.print("88");
  displaySS.print("88");
  delay(1000);
}

void loop()
{
  blinkColon();
  updateSecond();
  updateMinute();
  updateHour();
  // put here other code which needs to run:
}

Let me try to implement his code and see where I can get with it.

... so keep us updated: how is your progress with that method?