Categories
Uncategorized

Neopixel Clock, V2, by David Zweben on December 3, 2017

HEY! If you’re finding this page then you’re probably just as excited to make the Neopixel clock you saw on Hackster.io as much as I am. BUT IT’S GONE!

Never fear, it was licensed under Creative Commons 3.0 non-commercial, so even though the website has evaporated we can work on!

The original project can be found here. And a full mini project can be found here on hackaday.io.

Here’s the original video:

Just incase you thought it was impossible to replicate, here’s somebody’s replication.

But I hear you asking, what about the files?

Here’s the Gerber file for the PCBs. I recommend using JLCPCB if you’re not in a position to make it yourself.

This is my best guess at how to attach the components to the back. Hope it’s enough for you.

This is the code:

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

#define PIN 9

#define SEGMENTS_PER_DIGIT 7
#define LEDS_PER_SEGMENT 2
#define LEDS_PER_DIGIT 14
#define GRID_WIDTH 17
#define GRID_HEIGHT 7

#define NUMPIXELS 58

#include <Wire.h>
#include "RTClib.h"
#include <Encoder.h>

RTC_DS3231 rtc;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

int mode = 0;
bool justEnteredMode = true;

double timeOfLastInput = 0;
double timeOfLastRefresh = 0;
int inactivityTimeoutDuration = 3000;

uint32_t startColor,endColor;

uint16_t startHue = 0;
uint16_t endHue = 80;

int brightness = 10;


int buttonPin = 10;
bool oldButtonState = 1;

Encoder myEnc(3, 2);
long oldPosition = -999;
int inputHours = 1;
int inputTenMinutes = 0;
int inputMinutes = 0;

int inputColor = 0;
int inputColorShift = 12;
int inputColorDirection = 0;


// Defines the light number for each light by its position in a grid covering the whole clock face
// 999 indicates positions where no light exists

const int PROGMEM pixelNumber[7][17] = 
{
  {999,  0,  1,999,999, 14, 15,999,999,999, 30, 31,999,999, 44, 45,999},
  { 11,999,999,  2, 25,999,999, 16,999, 41,999,999, 32, 55,999,999, 46},
  { 10,999,999,  3, 24,999,999, 17, 29, 40,999,999, 33, 54,999,999, 47},
  {999, 12, 13,999,999, 26, 27,999,999,999, 42, 43,999,999, 56, 57,999},
  {  9,999,999,  4, 23,999,999, 18, 28, 39,999,999, 34, 53,999,999, 48},
  {  8,999,999,  5, 22,999,999, 19,999, 38,999,999, 35, 52,999,999, 49},
  {999,  7,  6,999,999, 21, 20,999,999,999, 37, 36,999,999, 51, 50,999},
};


const int pixelValues[][14] = {
  {1,1,1,1,1,1,1,1,1,1,1,1,0,0}, // Number 0
  {0,0,1,1,1,1,0,0,0,0,0,0,0,0}, // Number 1
  {1,1,1,1,0,0,1,1,1,1,0,0,1,1}, // Number 2
  {1,1,1,1,1,1,1,1,0,0,0,0,1,1}, // Number 3
  {0,0,1,1,1,1,0,0,0,0,1,1,1,1}, // Number 4
  {1,1,0,0,1,1,1,1,0,0,1,1,1,1}, // Number 5
  {1,1,0,0,1,1,1,1,1,1,1,1,1,1}, // Number 6
  {1,1,1,1,1,1,0,0,0,0,0,0,0,0}, // Number 7
  {1,1,1,1,1,1,1,1,1,1,1,1,1,1}, // Number 8
  {1,1,1,1,1,1,1,1,0,0,1,1,1,1}, // Number 9
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0}  // Digit off
};


void setup () {

  Serial.begin(9600);


  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }
    
  if (rtc.lostPower()) {
    Serial.println("RTC lost power, lets set the time!");
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }

  // Uncomment below to set time to compile time.
  // THIS WILL KEEP RESETTING THE TIME EVERY BOOT UNTIL CODE WITH THIS DISABLED IS UPLOADED
  //rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));


  strip.begin(); // This initializes the NeoPixel library.


  pinMode(buttonPin, INPUT_PULLUP);      // sets the digital pin as output

}

void loop () {

  if (digitalRead(buttonPin) == 0 && oldButtonState == 1) {
    mode++;
    justEnteredMode = true;
    timeOfLastInput = millis(); // Count mode switching as an input
  }

  oldButtonState = digitalRead(buttonPin);

  int delayNum = 500;

  DateTime now = rtc.now();
  

  int convertedHour;

  if (now.hour() > 12) {
    convertedHour = now.hour() - 12;
  }
  else {
    convertedHour = now.hour();
  }


  int num0 = (convertedHour /10) % 10;
  int num1 = convertedHour % 10;
  int num2 = (now.minute() /10) % 10;
  int num3 = now.minute() % 10;


  // Time display and brightness adjust mode
  if (mode == 0) {
    // Run only on first instance of loop
    if (justEnteredMode == true) {
      Serial.println("Mode 0, display time");
    }
    justEnteredMode = false;

    adjustVariable(&brightness, 1, 1, 20);

    updateDigits(num0,num1,num2,num3);

    inputColor += 5;
  }

  // Color set mode
  else if (mode == 1) {
    // Run only on first instance of loop
    if (justEnteredMode == true) {
      Serial.println("Mode 1, set color 1");
      updateDigits(1, 10, 10, 10);
      delay(500);
    }
    justEnteredMode = false;


    adjustVariable(&inputColor, 10, 0, 360);
    
    resetModeAfterInactivity();

    updateDigits(8, 8, 8, 8);

  }
  
  // Color shift amount set mode
  else if (mode == 2) {
    // Run only on first instance of loop
    if (justEnteredMode == true) {
      Serial.println("Mode 2, set color shift amount");
      updateDigits(2, 10, 10, 10);
      delay(500);
    }
    justEnteredMode = false;

    adjustVariable(&inputColorShift, 1, 0, 30);
    
    resetModeAfterInactivity();
        
    updateDigits(8, 8, 8, 8);

  }  
  
  // Gradient direction set mode
  else if (mode == 3) {
    // Run only on first instance of loop
    if (justEnteredMode == true) {
      Serial.println("Mode 3, set gradient direction");
      updateDigits(3, 10, 10, 10);
      delay(500);
    }
    justEnteredMode = false;

    adjustVariable(&inputColorDirection, 1, 0, 2);
    
    resetModeAfterInactivity();

    updateDigits(8, 8, 8, 8);

  }

  // Hour set mode
  else if (mode == 4) {
    // Run only on first instance of loop
    if (justEnteredMode == true) {
      Serial.println("Mode 4, set hour");
    }
    justEnteredMode = false;
    
    adjustVariable(&inputHours, 1, 1, 12);

    int digitOne = (inputHours / 10) % 10;
    int digitTwo = inputHours % 10;
    
    resetModeAfterInactivity();

    updateDigits(digitOne, digitTwo, 10, 10);

    rtc.adjust(DateTime(now.year(), now.month(), now.day(), inputHours, now.minute(), now.second()));
    
  }

  //Ten minute set mode
  else if (mode == 5) {
    // Run only on first instance of loop
    if (justEnteredMode == true) {
      Serial.println("Mode 5, set ten minutes");
    }
    justEnteredMode = false;

    adjustVariable(&inputTenMinutes, 1, 0, 5);
    
    resetModeAfterInactivity();

    updateDigits(10, 10, inputTenMinutes, 10);

    rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), inputTenMinutes*10, 0));
    
  }

  //Minute set mode
  else if (mode == 6) {
    // Run only on first instance of loop
    if (justEnteredMode == true) {
      Serial.println("Mode 6, set minute");
    }
    justEnteredMode = false;
    
    adjustVariable(&inputMinutes, 1, 0, 9);
    
    resetModeAfterInactivity();

    updateDigits(10, 10, 10, inputMinutes);

    rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), (inputTenMinutes*10) + inputMinutes, 0));
    
  }
  
  else {
    mode = 0;
  }
  
}


void resetModeAfterInactivity() {

  if (timeOfLastInput + inactivityTimeoutDuration < millis()) {
    Serial.print("Time of last input: ");
    Serial.print(timeOfLastInput);
    Serial.print(". Current milis: ");
    Serial.println(millis());
    mode = 0;
  }
  
}

void updateDigits(int num0, int num1, int num2, int num3) {

  if (timeOfLastRefresh + 100 < millis()) {

    timeOfLastRefresh = millis();
    
    // Set the segments
    for (int v=0; v<GRID_HEIGHT; v++) {
    
      for (int h=0; h<GRID_WIDTH; h++) {
    
        int currentPixelNumber = pgm_read_word_near(&(pixelNumber[v][h]));
    
        if (currentPixelNumber != 999) {
    
          // There is a pixel here, see if it should be lit
          int digitOffset = 0;
  
          // Track which number we want to draw to the current digit position
          int numberToDraw;
  
          // Track if we're on a divider pixel
          bool divider = false;
          
          // First digit
          if (h >= 0 && h < LEDS_PER_SEGMENT + 2) {
            digitOffset = 0;
            numberToDraw = num0;
            if (numberToDraw == 0) {
              numberToDraw = 10;
            }
          }
  
          // Second digit
          if (h >=LEDS_PER_SEGMENT + 2 && h < (LEDS_PER_SEGMENT * 2) + 4) {
            digitOffset = LEDS_PER_SEGMENT * SEGMENTS_PER_DIGIT;
            numberToDraw = num1;
          }
  
          // Divider
          if (h == (LEDS_PER_SEGMENT * 2) + 4) {
            digitOffset = (LEDS_PER_SEGMENT * SEGMENTS_PER_DIGIT) * 2;
            divider = true;
          }
  
          if (h >= (LEDS_PER_SEGMENT * 2) + 5 && h <= (LEDS_PER_SEGMENT * 3) + 6) {
            digitOffset = (LEDS_PER_SEGMENT * SEGMENTS_PER_DIGIT) * 2 + 2;
            numberToDraw = num2;
          }
  
          if (h >= (LEDS_PER_SEGMENT * 3) + 7 && h <= (LEDS_PER_SEGMENT * 4) + 8) {
            digitOffset = (LEDS_PER_SEGMENT * SEGMENTS_PER_DIGIT) * 3 + 2;
            numberToDraw = num3;
          }
          
          if (pixelValues[numberToDraw][currentPixelNumber-digitOffset] == 1 || divider == true) {   
            if (inputColorDirection == 0) {
              strip.setPixelColor(currentPixelNumber, HSV_to_RGB(inputColor + h*inputColorShift,100,brightness)); 
            }
            else if (inputColorDirection == 1) {
              strip.setPixelColor(currentPixelNumber, HSV_to_RGB(inputColor + v*inputColorShift,100,brightness)); 
            }
            else {
              strip.setPixelColor(currentPixelNumber, HSV_to_RGB(inputColor,100,brightness)); 
            }
          }
          else {
            strip.setPixelColor(currentPixelNumber, strip.Color(0,0,0));
          }
          
          
          strip.show();
    
        }
        else {
          // 999 means no pixel here, do nothing
        }
      }
  
    }
  
  }
    
}


void adjustVariable(int *variable, int increment, int minNum, int maxNum) {

  long newPosition = myEnc.read();
  
  if (newPosition != oldPosition) {
    if (oldPosition > newPosition) {
      *variable -= increment;
      
      timeOfLastInput = millis();
      
      if (*variable < minNum) {
        *variable = maxNum;
      }  
      Serial.print("Decrease to");
      Serial.println(*variable + increment);
    }
    if (oldPosition < newPosition) {
      *variable += increment;

      timeOfLastInput = millis();
      
      if (*variable > maxNum) {
        *variable = minNum;
      }
      Serial.print("Increase to ");
      Serial.println(*variable - increment);
    }      

    oldPosition = newPosition;
  }
  
}


uint32_t HSV_to_RGB(float h, float s, float v) {
  int i;
  float f,p,q,t;

  uint8_t r,g,b;

  while (h >= 360) {
    h = h - 360;
  }
  while (h < 0) {
    h = h + 360;
  }
  
  h = max(0.0, min(360.0, h));
  s = max(0.0, min(100.0, s));
  v = max(0.0, min(100.0, v));
  
  s /= 100;
  v /= 100;
  
  if(s == 0) {
    // Achromatic (grey)
    r = g = b = round(v*255);
    return;
  }

  h /= 60; // sector 0 to 5
  i = floor(h);
  f = h - i; // factorial part of h
  p = v * (1 - s);
  q = v * (1 - s * f);
  t = v * (1 - s * (1 - f));
    
  switch(i) {
    case 0:
      r = round(255*v);
      g = round(255*t);
      b = round(255*p);
      break;
    case 1:
      r = round(255*q);
      g = round(255*v);
      b = round(255*p);
      break;
    case 2:
      r = round(255*p);
      g = round(255*v);
      b = round(255*t);
      break;
    case 3:
      r = round(255*p);
      g = round(255*q);
      b = round(255*v);
      break;
    case 4:
      r = round(255*t);
      g = round(255*p);
      b = round(255*v);
      break;
    default: // case 5:
      r = round(255*v);
      g = round(255*p);
      b = round(255*q);
  }

  return strip.Color(r,g,b);  
};
#include <Adafruit_NeoPixel.h>
#include <math.h>
#include <Wire.h>
#include "RTClib.h"
#include <Encoder.h>

#ifdef __AVR__
  #include <avr/power.h>
#endif

#define PIN 9

#define SEGMENTS_PER_DIGIT 7
#define LEDS_PER_SEGMENT 2
#define LEDS_PER_DIGIT 14
#define GRID_WIDTH 17
#define GRID_HEIGHT 7

#define NUMPIXELS 58


RTC_DS3231 rtc;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

int mode = 0;
bool justEnteredMode = true;

double timeOfLastInput = 0;
double timeOfLastRefresh = 0;
int inactivityTimeoutDuration = 10000;

uint32_t startColor,endColor;

uint16_t startHue = 0;
uint16_t endHue = 80;

int brightness = 5;

int inputHours = 1;
int inputMinutes = 0;

int inputColor = 0;
int inputColorShift = 12;
int inputColorDirection = 0;

int analogInput = 0;
bool upIsPressed, rightIsPressed, downIsPressed, leftIsPressed, centerIsPressed = false;
int upHeldCycles, rightHeldCycles, downHeldCycles, leftHeldCycles, centerHeldCycles = 0;

// Defines the light number for each light by its position in a grid covering the whole clock face
// 0 indicates positions where no light exists

const int PROGMEM pixelNumber[7][17] = 
{
  { 0, 1, 2, 0,    0,15,16, 0,    0,    0,31,32, 0,    0,45,46, 0},
  {12, 0, 0, 3,   26, 0, 0,17,    0,   42, 0, 0,33,   56, 0, 0,47},
  {11, 0, 0, 4,   25, 0, 0,18,   30,   41, 0, 0,34,   55, 0, 0,48},
  { 0,13,14, 0,    0,27,28, 0,    0,    0,43,44, 0,    0,57,58, 0},
  {10, 0, 0, 5,   24, 0, 0,19,   29,   40, 0, 0,35,   54, 0, 0,49},
  { 9, 0, 0, 6,   23, 0, 0,20,    0,   39, 0, 0,36,   53, 0, 0,50},
  { 0, 8, 7, 0,    0,22,21, 0,    0,    0,38,37, 0,    0,52,51, 0},
};


const int pixelValues[][14] = 
{
  {1,1,1,1,1,1,1,1,1,1,1,1,0,0}, // Number 0
  {0,0,1,1,1,1,0,0,0,0,0,0,0,0}, // Number 1
  {1,1,1,1,0,0,1,1,1,1,0,0,1,1}, // Number 2
  {1,1,1,1,1,1,1,1,0,0,0,0,1,1}, // Number 3
  {0,0,1,1,1,1,0,0,0,0,1,1,1,1}, // Number 4
  {1,1,0,0,1,1,1,1,0,0,1,1,1,1}, // Number 5
  {1,1,0,0,1,1,1,1,1,1,1,1,1,1}, // Number 6
  {1,1,1,1,1,1,0,0,0,0,0,0,0,0}, // Number 7
  {1,1,1,1,1,1,1,1,1,1,1,1,1,1}, // Number 8
  {1,1,1,1,1,1,1,1,0,0,1,1,1,1}, // Number 9
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0}  // Digit off
};


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

  if (! rtc.begin()) 
  {
    Serial.println("Couldn't find RTC");
    while (1);
  }
    
  if (rtc.lostPower()) 
  {
    Serial.println("RTC lost power, lets set the time!");
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }

  // Uncomment below to set time to compile time.
  // THIS WILL KEEP RESETTING THE TIME EVERY BOOT UNTIL CODE WITH THIS DISABLED IS UPLOADED
  //rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

  strip.begin(); // This initializes the NeoPixel library.

  pinMode(A3, INPUT);
}

void loop () 
{
  analogInput = analogRead(A2);

  if (upIsPressed)
    upHeldCycles++;
  else
    upHeldCycles = 0;
    
  if (rightIsPressed)
    rightHeldCycles++;
  else
    rightHeldCycles = 0;
    
  if (downIsPressed)
    downHeldCycles++;
  else
    downHeldCycles = 0;
    
  if (leftIsPressed)
    leftHeldCycles++;
  else
    leftHeldCycles = 0;

  if (centerIsPressed)
    centerHeldCycles++;
  else
    centerHeldCycles = 0;
  

  upIsPressed = rightIsPressed = downIsPressed = leftIsPressed = centerIsPressed = false;

  
  // Set isPressedValues
  if (analogInput > 30 && analogInput < 60)
  {
      upIsPressed = true;
      timeOfLastInput = millis();
      Serial.print("up ");
      Serial.println(upHeldCycles);
  }
  else if (analogInput > 160 && analogInput < 200)
  {
      rightIsPressed = true;
      timeOfLastInput = millis();
      Serial.print("right ");
      Serial.println(rightHeldCycles);
  }
  else if (analogInput > 500 && analogInput < 550)
  {
      downIsPressed = true;
      timeOfLastInput = millis();
      Serial.print("down ");
      Serial.println(downHeldCycles);
  }
  else if (analogInput > 660 && analogInput < 720)
  {
      leftIsPressed = true;
      timeOfLastInput = millis();
      Serial.print("left ");
      Serial.println(leftHeldCycles);
  }
  else if (analogInput > 1000 && analogInput < 1025)
  {
      centerIsPressed = true;
      timeOfLastInput = millis();
      Serial.print("center ");
      Serial.println(centerHeldCycles);
  }

  
  if (leftHeldCycles == 1)
  {
    mode--;
    justEnteredMode = true;
  }

  if (rightHeldCycles == 1) 
  {
    mode++;
    justEnteredMode = true;
  }

  int delayNum = 500;

  DateTime now = rtc.now();
  

  int convertedHour;

  if (now.hour() > 12) 
  {
    convertedHour = now.hour() - 12;
  }
  else 
  {
    convertedHour = now.hour();
  }

  int num0 = (convertedHour /10) % 10;
  int num1 = convertedHour % 10;
  int num2 = (now.minute() /10) % 10;
  int num3 = now.minute() % 10;

  // Time display and brightness adjust mode
  if (mode == 0) 
  {
    // Run only on first instance of loop
    if (justEnteredMode == true) 
    {
      Serial.println("Mode 0, display time");
      Serial.print(num0);
      Serial.print(num1);
      Serial.print(":");
      Serial.print(num2);
      Serial.println(num3);
    }
    justEnteredMode = false;

    if (upIsPressed)
    {
      adjustVariable(&brightness, 1, 1, 100);
      Serial.print("Brightness raised to ");
      Serial.println(brightness);
    }
    
    if (downIsPressed)
    {
      adjustVariable(&brightness, -1, 1, 100);
      Serial.print("Brightness lowered to ");
      Serial.println(brightness);
    }

    updateDigits(num0,num1,num2,num3);

    inputColor += 5;
  }

  // Color set mode
  else if (mode == 1) 
  {
    // Run only on first instance of loop
    if (justEnteredMode == true) 
    {
      Serial.println("Mode 1, set color 1");
      updateDigits(1, 10, 10, 10);
      delay(300);
    }
    
    justEnteredMode = false;

    if (upIsPressed)
    {
      adjustVariable(&inputColor, -5, 0, 360);
      Serial.print("Input color raised to ");
      Serial.println(inputColor);
    }
    
    if (downIsPressed)
    {
      adjustVariable(&inputColor, 5, 0, 360);
      Serial.print("Input color lowered to ");
      Serial.println(inputColor);
    }
    
    resetModeAfterInactivity();

    updateDigits(8, 8, 8, 8);
  }

  
  // Color shift amount set mode
  else if (mode == 2) 
  {
    // Run only on first instance of loop
    if (justEnteredMode == true) 
    {
      Serial.println("Mode 2, set color shift amount");
      updateDigits(2, 10, 10, 10);
      delay(300);
    }
    justEnteredMode = false;

    if (upIsPressed)
    {
      adjustVariable(&inputColorShift, -1, -30, 30);
      Serial.print("Color shift amount raised to ");
      Serial.println(inputColorShift);
    }
    
    if (downIsPressed)
    {
      adjustVariable(&inputColorShift, 1, -30, 30);
      Serial.print("Color shift amount lowered to ");
      Serial.println(inputColorShift);
    }
    
    resetModeAfterInactivity();
        
    updateDigits(8, 8, 8, 8);
  }  

  
  // Gradient direction set mode
  else if (mode == 3) 
  {
    // Run only on first instance of loop
    if (justEnteredMode == true) 
    {
      Serial.println("Mode 3, set gradient direction");
      updateDigits(3, 10, 10, 10);
      delay(300);
    }
    justEnteredMode = false;

    if (upIsPressed)
    {
      adjustVariable(&inputColorDirection, -1, 0, 2);
      Serial.print("Input color direction set to ");
      Serial.println(inputColorDirection);
    }
    
    if (downIsPressed)
    {
      adjustVariable(&inputColorDirection, 1, 0, 2);
      Serial.print("Input color direction set to ");
      Serial.println(inputColorDirection);
    }

    resetModeAfterInactivity();

    updateDigits(8, 8, 8, 8);
  }


  // Hour set mode
  else if (mode == 4) 
  {
    // Run only on first instance of loop
    if (justEnteredMode == true) 
    {
      Serial.println("Mode 4, set hour");
    }
    justEnteredMode = false;

    if (upIsPressed)
    {
      adjustVariable(&inputHours, 1, 1, 12);
      Serial.print("Hours raised to ");
      Serial.println(inputHours);
    }
    
    if (downIsPressed)
    {
      adjustVariable(&inputHours, -1, 1, 12);
      Serial.print("Hours lowered to ");
      Serial.println(inputHours);
    }    

    int digitOne = (millis() % 1000 < 800) ? (inputHours / 10) % 10 : 10;
    int digitTwo = (millis() % 1000 < 800) ? inputHours % 10 : 10;
    
    resetModeAfterInactivity();

    updateDigits(digitOne, digitTwo, num2, num3);

    rtc.adjust(DateTime(now.year(), now.month(), now.day(), inputHours, now.minute(), 0));
  }

  // Minute set mode
  else if (mode == 5) 
  {
    inputMinutes = now.minute();
    
    // Run only on first instance of loop
    if (justEnteredMode == true) 
    {
      Serial.println("Mode 6, set minutes");
    }
    justEnteredMode = false;

    if (upIsPressed)
    {
      adjustVariable(&inputMinutes, 1, 0, 59);
      Serial.print("Minutes raised to ");
      Serial.println(inputMinutes);
    }
    
    if (downIsPressed)
    {
      adjustVariable(&inputMinutes, -1, 0, 59);
      Serial.print("Minutes lowered to ");
      Serial.println(inputMinutes);
    }        
    
    resetModeAfterInactivity();

    int digitThree = (millis() % 1000 < 800) ? floor(inputMinutes/10) : 10;
    int digitFour = (millis() % 1000 < 800) ? (inputMinutes % 10) : 10;

    //int digitThree = floor(inputMinutes / 10);
    //int digitFour = inputMinutes % 10;
    
    updateDigits(num0, num1, digitThree, digitFour);

    rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), inputMinutes, 0));
  }
  
  else 
  {
    mode = 0;
  }
  
}


void resetModeAfterInactivity() 
{
  if (timeOfLastInput + inactivityTimeoutDuration < millis()) 
  {
    Serial.print("Time of last input: ");
    Serial.print(timeOfLastInput);
    Serial.print(". Current milis: ");
    Serial.println(millis());
    mode = 0;
  }
}

void updateDigits(int num0, int num1, int num2, int num3) 
{
  if (timeOfLastRefresh + 100 < millis()) 
  {
    timeOfLastRefresh = millis();
    
    // Set the segments
    for (int v=0; v<GRID_HEIGHT; v++) 
    {
      for (int h=0; h<GRID_WIDTH; h++) 
      {
        // Subtract one from the value we retrieve because
        // we're starting at 1 in the array
        // but at 0 on strip.setPixelColor
        int currentPixelNumber = pgm_read_word_near(&(pixelNumber[v][h])) - 1;
    
        if (currentPixelNumber >= 0) 
        {
          // There is a pixel here, see if it should be lit
          int digitOffset = 0;
  
          // Track which number we want to draw to the current digit position
          int numberToDraw;
  
          // Track if we're on a divider pixel
          bool divider = false;
          
          // First digit
          if (h >= 0 && h < LEDS_PER_SEGMENT + 2) 
          {
            digitOffset = 0;
            numberToDraw = num0;
            if (numberToDraw == 0) 
            {
              numberToDraw = 10;
            }
          }
  
          // Second digit
          if (h >=LEDS_PER_SEGMENT + 2 && h < (LEDS_PER_SEGMENT * 2) + 4) 
          {
            digitOffset = LEDS_PER_SEGMENT * SEGMENTS_PER_DIGIT;
            numberToDraw = num1;
          }
  
          // Divider
          if (h == (LEDS_PER_SEGMENT * 2) + 4) 
          {
            digitOffset = (LEDS_PER_SEGMENT * SEGMENTS_PER_DIGIT) * 2;
            divider = true;
          }
  
          if (h >= (LEDS_PER_SEGMENT * 2) + 5 && h <= (LEDS_PER_SEGMENT * 3) + 6) 
          {
            digitOffset = (LEDS_PER_SEGMENT * SEGMENTS_PER_DIGIT) * 2 + 2;
            numberToDraw = num2;
          }
  
          if (h >= (LEDS_PER_SEGMENT * 3) + 7 && h <= (LEDS_PER_SEGMENT * 4) + 8) 
          {
            digitOffset = (LEDS_PER_SEGMENT * SEGMENTS_PER_DIGIT) * 3 + 2;
            numberToDraw = num3;
          }
          
          if (pixelValues[numberToDraw][currentPixelNumber-digitOffset] == 1 || divider == true) 
          {   
            if (inputColorDirection == 0) 
            {
              uint32_t color = HSV_to_RGB(inputColor + h*inputColorShift,100,brightness);
              strip.setPixelColor(currentPixelNumber, color); 
            }
            else if (inputColorDirection == 1) 
            {
              uint32_t color = HSV_to_RGB(inputColor + v*inputColorShift,100,brightness);
              strip.setPixelColor(currentPixelNumber, color); 
            }
            else 
            {
              uint32_t color = HSV_to_RGB(inputColor,100,brightness);
              strip.setPixelColor(currentPixelNumber, color); 
            }
          }
          else 
          {
            uint32_t color = strip.Color(0,0,0);
            strip.setPixelColor(currentPixelNumber, color);
          }
          
          strip.show();
        }
      }
  
    }
  
  }
    
}


void adjustVariable(int *variable, int change, int minNum, int maxNum) 
{
  *variable += change;
    
  if (*variable < minNum) 
    *variable = maxNum;

  if (*variable > maxNum)
    *variable = minNum;
}


uint32_t HSV_to_RGB(float h, float s, float v) 
{
  int i;
  float f,p,q,t;
  uint8_t r,g,b;

  while (h >= 360) 
  {
    h = h - 360;
  }
  while (h < 0) 
  {
    h = h + 360;
  }
  
  h = max(0.0, min(360.0, h));
  s = max(0.0, min(100.0, s));
  v = max(0.0, min(100.0, v));
  
  s /= 100;
  v /= 100;
  
  if (s == 0) 
  {
    // Achromatic (grey)
    r = g = b = round(v*255);
    return;
  }

  h /= 60; // sector 0 to 5
  i = floor(h);
  f = h - i; // factorial part of h
  p = v * (1 - s);
  q = v * (1 - s * f);
  t = v * (1 - s * (1 - f));
    
  switch(i)
  {
    case 0:
      r = round(255*v);
      g = round(255*t);
      b = round(255*p);
      break;
    case 1:
      r = round(255*q);
      g = round(255*v);
      b = round(255*p);
      break;
    case 2:
      r = round(255*p);
      g = round(255*v);
      b = round(255*t);
      break;
    case 3:
      r = round(255*p);
      g = round(255*q);
      b = round(255*v);
      break;
    case 4:
      r = round(255*t);
      g = round(255*p);
      b = round(255*v);
      break;
    default: // case 5:
      r = round(255*v);
      g = round(255*p);
      b = round(255*q);
  }

Leave a Reply