Multitasking example clock

Instead of using the delay() function we use the millis() to find the right point in time to trigger the next steps.

The idea is to have four thing to display:

  • a display for the 1/100s of a second
    we use a filling circle, which alternates between light and dark mode
  • seconds
    we use a green dot
  • minutes
    we use a red dot
  • hours
    we use a blue dot

If the dots are overlaying each other we use different colors, eg. red + green = yellow. As the arduino does not have an onboard real time clock, we always start at 00h 00m 00s 000hs.

The code:

/*
    Clock with a 16 LED NeoPixel ring
    (C) 2016 -mat- filid brandy, brandy@linuxpinguin.de
*/

// Settings for the NeoPixel
#include <Adafruit_NeoPixel.h>
#define stripSize 16
Adafruit_NeoPixel strip = Adafruit_NeoPixel( stripSize, 6, NEO_GRB + NEO_KHZ800 );

// Time keeping
#define hs 0
#define s 1
#define m 2
#define h 3

unsigned long Diff[4];
unsigned long Max[4];
unsigned long Tick[4];
unsigned long Uhr[4];
unsigned int Pixel[4];
float Ratio[4];
bool reverse = LOW;
bool toggle = LOW;

// Colors
uint32_t colors[9];
#define black 0
#define white 1
#define red 2
#define green 3
#define blue 4
#define purple 5
#define cyan 6
#define yellow 7
#define all 8
#define light 8



void setup() {
  // Initialize the strip
  strip.begin();
  strip.show();

  Max[hs]   = 100;
  Max[s]    = 60;
  Max[m]    = 60;
  Max[h]    = 12;

  Diff[hs]  = 10;
  Diff[s]   = 1000;
  Diff[m]   = 60000;
  Diff[h]   = 3600000;

  for (int i = 0; i < 4; i++) {
    Tick[i] = 0;
    Uhr[i] = 0;
    Pixel[i] = 0;
    Ratio[i] = (float)Max[i] / (float)stripSize;
  }

  // fill in the wanted colors
  colors[black]  = strip.Color(0, 0, 0);
  colors[white]  = strip.Color(light / 4, light / 4, light / 4);
  colors[red]    = strip.Color(light, 0, 0);
  colors[green]  = strip.Color(0, light, 0);
  colors[blue]   = strip.Color(0, 0, light);
  colors[purple] = strip.Color(light, 0, light);
  colors[cyan]   = strip.Color(0, light, light);
  colors[yellow] = strip.Color(light, light, 0);
  colors[all]    = strip.Color(light * 2, light * 2, light * 2);
}

void loop() {
  // This is based on the internal Ticker
  unsigned long cTick = millis();

  // Find the trigger points in Tick
  for (int i = 0; i < 4; i++) {
    if ( (cTick - Tick[i]) >= Diff[i] ) {
      Tick[i] = cTick;
      Uhr[i] = ( ++Uhr[i] < Max[i] ) ? Uhr[i] : 0;
    }
  }

  if ( 0 == Uhr[hs] && !toggle) {
    reverse = !reverse;
    toggle = HIGH;
  }
  if ( 1 == Uhr[hs] ) {
    toggle = LOW;
  }

  // Calculate the display of
  for (int16_t p = 0; p < stripSize; p++) {
    strip.setPixelColor(p, colors[black]);
    // Find the points for the hs, s, m, h
    for (int j = 0; j < 4; j++) {
      if ( ( (float)(p) * Ratio[j] <= Uhr[j]  ) && ( Uhr[j] < (float)(p + 1) * Ratio[j] ) ) {
        Pixel[j] = stripSize - p - 1;
      }
    }
  }
  // 1/100s animation
  for (int16_t p = 0; p < stripSize; p++ ) {
    if (Pixel[hs] < p) {
      if (reverse)
        strip.setPixelColor(p, colors[white]);
      else
        strip.setPixelColor(p, colors[black]);
    } else {
      if (reverse)
        strip.setPixelColor(p, colors[black]);
      else
        strip.setPixelColor(p, colors[white]);
    }
  }

  // Paint the points
  strip.setPixelColor(Pixel[hs], colors[white]);
  strip.setPixelColor(Pixel[s], colors[green]);
  strip.setPixelColor(Pixel[m], colors[red]);
  strip.setPixelColor(Pixel[h], colors[blue]);

  // Handle overlaying points
  // double
  if ( Pixel[s] == Pixel[m] ) strip.setPixelColor(Pixel[m], colors[yellow]);
  if ( Pixel[s] == Pixel[h] ) strip.setPixelColor(Pixel[h], colors[cyan]);
  if ( Pixel[m] == Pixel[h] ) strip.setPixelColor(Pixel[h], colors[purple]);
  // triple
  if ( ( Pixel[m] == Pixel[h] ) && ( Pixel[s] == Pixel[m]) ) strip.setPixelColor(Pixel[h], colors[all]);

  // Tada!
  strip.show();
}