Mirko Pavleski
Published © GPL3+

DIY Unusual Linear Clock

This is another one of my series of unusual clocks. The time is displayed on the WS2812 LED strip containing 34 LEDs

BeginnerFull instructions provided106
DIY Unusual Linear Clock

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
DS3231M - ±5ppm, I2C Real-Time Clock
Maxim Integrated DS3231M - ±5ppm, I2C Real-Time Clock
×1
WS2812 Addressable LED Strip
Digilent WS2812 Addressable LED Strip
×1
Pushbutton Switch, Momentary
Pushbutton Switch, Momentary
×3
Rotary potentiometer (generic)
Rotary potentiometer (generic)
×1

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free

Story

Read more

Schematics

Schematic diagram

Code

Arduino Code

C/C++
/* Umprogrammierte Version 2.0 mit folgenden Verbesserungen:
   - Im Programmirmodus blinkt die Zeiteinheit, die grade programmiert wird
   - KlickButton-Library zum Einlesen der Buttons
   - Sollte die Backup-Batterie leer sein, wird Zeit voreingestellt und in SET gesprungen
   - Sollte RTC nicht erkannt werden wir in Fehlermodus gesprungen
   - Umstieg auf RTCLib von Adafruit

   Zuletzt modifiziert: 01.11.17
   Zuletzt hochgeladen: 05.11.2018

   Status: Funktioniert
   
   Modified by Mirko Pavleski: 27.03.2021
*/

#include <Wire.h>
#include "RTClib.h"   // RTClib in Version 1.2.0

// RTC-Instanz initialisieren
RTC_DS3231 realTimeClock;

#include <FastLED.h>  // FastLED in Version 3.1.6
#include "ClickButton.h"  // ClickButton in Version 1.0.0
#include <elapsedMillis.h>   // elapsedMillis in Version 1.0.4

// Definitionen fuer den RGB Streifen (WS2813 RGB Strip, 60 LEDs/m. Gekauft bei led-stuebchen.de
#define LED_PIN     5
#define NUM_LEDS    34
#define LED_TYPE    WS2813
#define COLOR_ORDER GRB

#define TASTER_PLUS 4
#define TASTER_MENU 3
#define TASTER_MINUS 2

#define MAX_BRIGHTNESS 100      // Thats full on, watch the power!
#define MIN_BRIGHTNESS 0      // set to a minimum of 25%
const int brightnessInPin = A0;


CRGB leds[NUM_LEDS];
#define UPDATES_PER_SECOND 100

#define FARBE_STUNDEN Crimson
#define FARBE_ZEHNERMINUTEN LimeGreen
#define FARBE_MINUTEN Yellow

// Taster einbinden
ClickButton TasterMenu (TASTER_MENU, LOW, CLICKBTN_PULLUP);
ClickButton TasterPlus (TASTER_PLUS, LOW, CLICKBTN_PULLUP);
ClickButton TasterMinus (TASTER_MINUS, LOW, CLICKBTN_PULLUP);

#define EINZELKLICK 1
#define DOPPELKLICK 2
#define LANGERKLICK -1

elapsedMillis IntervallSensorLesen;
elapsedMillis ZeitAnzeigenIntervall;    //
elapsedMillis regularClear; // Strip loschen, damit Ueberlaufe richtig angezeigt werden
elapsedMillis blinkenStunden;  // Funktion, um LEDs im SET-Modus zu blinken
elapsedMillis blinkenZehnerminuten;  // Funktion, um LEDs im SET-Modus zu blinken
elapsedMillis blinkenEinerminuten;  // Funktion, um LEDs im SET-Modus zu blinken


bool ERRORFLAG = false;


// State-Machine Funktions-Namen
typedef enum {
  HOCHFAHREN,
  ANZEIGE_ZEIT,
  EINSTELLEN_ZEIT,
  FEHLER
} modus;
modus Funktion = HOCHFAHREN;   // Startfunktion der State-Machine

// State-Machine RTC-Stellmodus
typedef enum {
  STUNDEN,
  ZEHNERMINUTEN,
  EINERMINUTEN
} set_modus;

set_modus Funktion_Set = STUNDEN;   // Startfunktion der SET Funktion

void setup() {
  //Serial.begin(9600);

  // Initialisierung der LEDs
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  FastLED.setBrightness(MAX_BRIGHTNESS);

  //realTimeClock.adjust(DateTime(2014, 1, 21, 11, 40, 55));
}

void loop() {
int mappedValue = map(analogRead(brightnessInPin), 0, 1023, 0, 100);
FastLED.setBrightness(constrain(mappedValue, MIN_BRIGHTNESS, MAX_BRIGHTNESS));
  if (ERRORFLAG == true) {
    Funktion = FEHLER;
  }

  // Taser lesen und Wert abspeichern
  TasterMinus.Update();
  int taster_minus = TasterMinus.clicks;
  TasterMenu.Update();
  int taster_menu = TasterMenu.clicks;
  TasterPlus.Update();
  int taster_plus = TasterPlus.clicks;

  

  switch (Funktion) {

    case HOCHFAHREN: {

        // Hier werden Initialisierungen durchgefuhrt

        // Wenn RTC nicht erkannt wird: I2C-Problem. In den Fehlermodus springen
        if (!realTimeClock.begin()) {
          Funktion = FEHLER;
        }

        // Wenn RTC-Backup-Batterie gewechselt wurde, Zeit voreinstellen und in SET springen. 01.01.2017 07:08:09Uhr
        else if (realTimeClock.lostPower())
        {
          realTimeClock.adjust(DateTime(2000, 1, 1, 12, 55, 0));
          Funktion = EINSTELLEN_ZEIT;
        }

        else {
          ClearStrip();
          Funktion = ANZEIGE_ZEIT;
        }
      } // Ende HOCHFAHREN
      break;

    case ANZEIGE_ZEIT: {

        ZeigeZeit();

        if (taster_menu == LANGERKLICK) {
          ClearStrip();
          Funktion = EINSTELLEN_ZEIT;
        }
      } // Ende ANZEIGE_ZEIT
      break;

    case EINSTELLEN_ZEIT: {

        static bool set_fertig = false;

        static int jahr_fest = 2017;
        static int monat_fest = 1;
        static int tag_fest = 1;

        static int stundenuebergabe = 0;
        static int zehnerminutenuebergabe = 0;
        static int einerminutenuebergabe = 0;

        if (set_fertig == false) // Wenn "frisch" in SET gesprungen wird, wird die aktuelle Zeit eingelesen.
        {
          DateTime now = realTimeClock.now();
          stundenuebergabe = now.hour();
          zehnerminutenuebergabe = (now.minute() / 10) % 10;  // Zehnerminuten berechnen
          einerminutenuebergabe = (now.minute() % 10);  // Einerminuten berechnen
          set_fertig = true;
        }

        switch (Funktion_Set) {

          case STUNDEN: {

              if (taster_minus == EINZELKLICK)
              {
                stundenuebergabe--;
                if (stundenuebergabe > 23) stundenuebergabe = 0;
                else if (stundenuebergabe < 0) stundenuebergabe = 23;
              }

              if (taster_plus == EINZELKLICK)
              {
                stundenuebergabe++;
                if (stundenuebergabe > 23) stundenuebergabe = 0;
                else if (stundenuebergabe < 0) stundenuebergabe = 23;
              }

              if (taster_menu == EINZELKLICK)
              {
                Funktion_Set = ZEHNERMINUTEN;
              }

              AnzeigeStundenBlinkend(stundenuebergabe);
              //AnzeigeZehnerminuten(zehnerminutenuebergabe);
              //AnzeigeEinerminuten(einerminutenuebergabe);
            } // Ende STUNDEN
            break;

          case ZEHNERMINUTEN: {

              if (taster_minus == EINZELKLICK)
              {
                zehnerminutenuebergabe--;
                if (zehnerminutenuebergabe > 5) zehnerminutenuebergabe = 0;
                else if (zehnerminutenuebergabe < 0) zehnerminutenuebergabe = 5;
              }

              if (taster_plus == EINZELKLICK)
              {
                zehnerminutenuebergabe++;
                if (zehnerminutenuebergabe > 5) zehnerminutenuebergabe = 0;
                else if (zehnerminutenuebergabe < 0) zehnerminutenuebergabe = 5;
              }

              if (taster_menu == EINZELKLICK)
              {
                Funktion_Set = EINERMINUTEN;
              }
              AnzeigeZehnerminutenBlinkend(zehnerminutenuebergabe);


            } // Ende ZEHNERMINUTEN
            break;

          case EINERMINUTEN: {

              if (taster_minus == EINZELKLICK)
              {
                einerminutenuebergabe--;
                if (einerminutenuebergabe > 9) einerminutenuebergabe = 0;
                else if (einerminutenuebergabe < 0) einerminutenuebergabe = 9;
              }

              if (taster_plus == EINZELKLICK)
              {
                einerminutenuebergabe++;
                if (einerminutenuebergabe > 9) einerminutenuebergabe = 0;
                else if (einerminutenuebergabe < 0) einerminutenuebergabe = 9;
              }

              if (taster_menu == EINZELKLICK)
              {
                Funktion_Set = STUNDEN;
              }

              AnzeigeEinerminutenBlinkend(einerminutenuebergabe);

            } // Ende EINERMINUTEN
            break;

        }

        if (taster_menu == LANGERKLICK)
        {
          realTimeClock.adjust(DateTime(jahr_fest, monat_fest, tag_fest, stundenuebergabe, (einerminutenuebergabe + (zehnerminutenuebergabe * 10)), 0));
          set_fertig = false;
          Funktion_Set = STUNDEN;

          ClearStrip();
          ZeigeZeit();
          Funktion = ANZEIGE_ZEIT;
        }


      } // Ende EINSTELLEN_ZEIT
      break;

    case FEHLER: {  // bei Fehlern gesamten LED Strip rot blinken

        leds[0] = CRGB::Red;
        leds[NUM_LEDS - 1] = CRGB::Red;
        FastLED.show();

        while (1);

      } // Ende FEHLER
      break;
  }
}


int ZeigeZeit() {

  DateTime now = realTimeClock.now(); //Aktuelle Zeit aus der RTC holen und in "now" zwischenspeichern
  AnzeigeStunden(now.hour());
  AnzeigeZehnerminuten ((now.minute() / 10) % 10);
  AnzeigeEinerminuten (now.minute() % 10);
  ZeitAnzeigenIntervall = 0;
}


void ClearStrip() { // Alle LEDs in einer fliessenden Bewegung von rechts nach links loeschen (Black)
  for (int i = 0; i <= NUM_LEDS - 1; i++) {
    leds[i] = CRGB::Black;
    FastLED.show();
  }
}

int AnzeigeStunden (int wert) { // Funktion, um die Stunden anzuzeigen. 24h wird in 12h "runtergebrochen"

  if (wert > 12) wert = (wert - 12);
  else if (wert > 23) wert = 0;

  int werte_led_tabelle[12][2] = {  // Umrechnungstabelle. {1, 19} heisst = alle LEDs bis 19 leuchten, {5, 24} alle LEDs bis 24 leuchten
    //(STUNDE, LED-NR im Strip)
    {1, 19},
    {2, 20},
    {3, 21},
    //Sprung, eine LED zwischendrin frei (Nr 22)
    {4, 23},
    {5, 24},
    {6, 25},
    //Sprung, eine LED zwischendrin frei (Nr 26)
    {7, 27},
    {8, 28},
    {9, 29},
    //Sprung, eine LED zwischendrin frei (Nr 30)
    {10, 31},
    {11, 32},
    {12, 33}
  };

  for (int i = 0; i <= wert - 1; i++) {
    leds[(werte_led_tabelle[i][1])] = CRGB::FARBE_STUNDEN;
    FastLED.show();
  }

  for (int f = wert; f <= 11; f++) {
    leds[(werte_led_tabelle[f][1])].fadeToBlackBy(255);
    FastLED.delay(10);
    FastLED.show();
  }
}


void AnzeigeStundenBlinkend(int stunden) {

  if (blinkenStunden > 350) {
    static bool lastMode = 0;

    if (lastMode == 0)
    {
      AnzeigeStunden(stunden);
      lastMode = 1;
    }

    else
    {
      ClearStrip();
      lastMode = 0;
    }
    blinkenStunden = 0;
  }
}

int AnzeigeZehnerminuten (int wert) { // Funktion, um die Zehnerminuten anzuzeigen.

  if (wert > 5 || wert < 0) {
    wert = 0;
  }

  int werte_led_tabelle[5][2] = {
    {1, 17},
    {2, 16},
    {3, 15},
    //Sprung, eine LED zwischendrin frei
    {4, 13},
    {5, 12}
  };

  for (int j = 0; j <= wert - 1; j++) {
    leds[(werte_led_tabelle[j][1])] = CRGB::FARBE_ZEHNERMINUTEN;
    FastLED.show();
  }

  for (int g = wert; g <= 4; g++) {
    leds[(werte_led_tabelle[g][1])].fadeToBlackBy(255);
    FastLED.delay(10);
    FastLED.show();
  }

}

void AnzeigeZehnerminutenBlinkend(int wert) {

  if (blinkenZehnerminuten > 350) {
    static bool lastMode = 0;

    if (lastMode == 0)
    {
      AnzeigeZehnerminuten(wert);
      lastMode = 1;
    }

    else
    {
      ClearStrip();
      lastMode = 0;
    }
    blinkenZehnerminuten = 0;
  }
}

int AnzeigeEinerminuten (int wert) { // // Funktion, um die Einerminuten anzuzeigen. LEDs 0 - 10 von links

  if (wert > 9 || wert < 0) {
    wert = 0;
  }

  int werte_led_tabelle[9][2] = {
    {1, 10},
    {2,  9},
    {3,  8},
    //Sprung, eine LED zwischendrin frei
    {4,  6},
    {5,  5},
    {6,  4},
    //Sprung, eine LED zwischendrin frei
    {7,  2},
    {8,  1},
    {9,  0} // erste LED im Streifen = LED 0, weil Array bei 0 beginnt
  };

  for (int k = 0; k <= wert - 1; k++) {
    leds[(werte_led_tabelle[k][1])] = CRGB::FARBE_MINUTEN;
    FastLED.show();
  }

  for (int m = wert; m <= 8; m++) {

    leds[(werte_led_tabelle[m][1])].fadeToBlackBy(255);
    FastLED.delay(10);
    FastLED.show();
  }
}

void AnzeigeEinerminutenBlinkend(int wert) {

  if (blinkenEinerminuten > 350) {
    static bool lastMode = 0;

    if (lastMode == 0)
    {
      AnzeigeEinerminuten(wert);
      lastMode = 1;
    }

    else
    {
      ClearStrip();
      lastMode = 0;
    }
    blinkenEinerminuten = 0;
  }
}


      

Credits

Mirko Pavleski

Mirko Pavleski

13 projects • 413 followers

Comments

Add projectSign up / Login