//Libreria per la gestione di NeoPixel
#include <Adafruit_NeoPixel.h>
#include <RTClib.h>
#include <SPI.h>
// Definizioni
//Pulsante On/Off
#define BTN_ON_OFF_PIN 7
//Pulsante per aumentare la luminosità
#define BTN_DIM_UP_PIN 3
//Pulsante per diminuire la luminosità
#define BTN_DIM_DOWN_PIN 4
//Pulsante per cambiare colore
#define BTN_COLOR_CHANGE_PIN 5
//Pulsante stato
#define BTN_STATE 6
//Pin Dati per NeoPixel
#define HOUR0_PIN 2
#define HOUR1_PIN 8
#define MIN0_PIN 9
#define MIN1_PIN 10
#define SEPARATOR_PIN 11
//Numero di LED della striscia
#define NUMPIXELS 28
#define SEPARATOR_PIXELS 2
//Fotoresistenza
#define PHOTORESISTOR_PIN A3
// ========== MAPPATURE LED PER DISPLAY 7 SEGMENTI (PROGMEM) ==========
// Queste mappature sono memorizzate nella Flash invece che nella RAM
const int ZERO[] PROGMEM = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 24, 25, 26, 27};
const int ZERO_SIZE = 24;
const int ONE[] PROGMEM = {4, 5, 6, 7, 8, 9, 10, 11};
const int ONE_SIZE = 8;
const int TWO[] PROGMEM = {0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25, 26, 27};
const int TWO_SIZE = 20;
const int THREE[] PROGMEM = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 20, 21, 22, 23};
const int THREE_SIZE = 20;
const int FOUR[] PROGMEM = {4, 5, 6, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 21, 22, 23};
const int FOUR_SIZE = 16;
const int FIVE[] PROGMEM = {0, 1, 2, 3, 4, 5, 6, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23};
const int FIVE_SIZE = 20;
const int SIX[] PROGMEM = {0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27};
const int SIX_SIZE = 20;
const int SEVEN[] PROGMEM = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
const int SEVEN_SIZE = 12;
const int EIGHT[] PROGMEM = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27};
const int EIGHT_SIZE = 28;
const int NINE[] PROGMEM = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23};
const int NINE_SIZE = 24;
// ========== ARRAY DI PUNTATORI PER ACCESSO SEMPLIFICATO ==========
const int* const DIGIT_ARRAYS[] PROGMEM = {
ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE
};
const int DIGIT_SIZES[] PROGMEM = {
ZERO_SIZE, ONE_SIZE, TWO_SIZE, THREE_SIZE, FOUR_SIZE,
FIVE_SIZE, SIX_SIZE, SEVEN_SIZE, EIGHT_SIZE, NINE_SIZE
};
// ========== FUNZIONI HELPER PER LEGGERE DA PROGMEM ==========
// Ottiene la dimensione dell'array per una cifra
int getDigitSize(int digit) {
if (digit >= 0 && digit <= 9) {
return pgm_read_word(&DIGIT_SIZES[digit]);
}
return ZERO_SIZE; // Default
}
//Istanzio oggetto NeoPixel
Adafruit_NeoPixel hour0 = Adafruit_NeoPixel(NUMPIXELS, HOUR0_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel hour1 = Adafruit_NeoPixel(NUMPIXELS, HOUR1_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel minute0 = Adafruit_NeoPixel(NUMPIXELS, MIN0_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel minute1 = Adafruit_NeoPixel(NUMPIXELS, MIN1_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel separator = Adafruit_NeoPixel(SEPARATOR_PIXELS, SEPARATOR_PIN, NEO_GRB + NEO_KHZ800);
//Definizione colori. I valori dei colori sono calcolati tenendo conton della luminosità
//La luminosità puà essere impostata su 5 valori, lo step fra l'uno e l'altro
//è pari a 51, partendo da 51 fino a 255, parto dalla metà che è pari a 153
//Valori validi per prima accensione dopo inseriento spina
struct Color {
uint8_t r;
uint8_t g;
uint8_t b;
};
// Definizione dei colori base
const Color colorList[] = {
{153, 153, 153}, // WHITE
{153, 153, 138}, // WARM_WHITE
{153, 0, 0}, // RED
{0, 153, 0}, // GREEN
{153, 153, 0}, // YELLOW
{0, 76, 153} // BLUE
};
const int numColors = sizeof(colorList) / sizeof(Color);
int currentColorIndex = 0;
// Stato del pulsante state
int state_ButtonState = 0;
// Ultimo stato del pulsante state
int state_LastButtonState = 0;
// State variable
static uint8_t state = 1;
//Variabile timer per pressione lunga
unsigned long t_pulsante = 0;
//Step di variazione della luminosità
unsigned int step = 51;
//Variabile di appoggio con il valore attuale dello step di luminosità
//per prima accensione dopo inserimento spina
unsigned int lum_corrente = 153;
//Variabile di buffer con il valore di luminosità attivo
//allo spegnimento della lampada
unsigned int buffer_luminosita = 0;
//Colore di partenza solo per prima accensione dopo inserimento spina
uint32_t colore = colorList[0];
//Variabile di buffer con il colore attivo allo spegnimento della lampada
//Colori bufferColore = EMPTY;
uint32_t bufferColore = hour0.Color(0, 0, 0);
//Ritardo per la funzione anti rimbalzo
int debounceDelay = 50;
//Variabile di verifica striscia On o Off
bool on = false;
//Variabili che contengono l'ora ed i minuti, vengono aggiornati dal modulo RTC
String ora = "00";
String minuti = "00";
//Create RTC_DS3231 instance
RTC_DS3231 rtc;
//Date & time variable
DateTime dateTime;
//Variables to store time characters to be printed
char strTime[7] = " ";
String timeLine = String();
//Variabile controllo passaggio minuti
int lastMinute = -1;
//Variabile controllo passaggio secondi
int lastSecond = -1;
//Variabile booleana che indica se i separatori sono accesi o spenti
bool SwitchedOn = false;
void setup()
{
hour0.begin();
hour1.begin();
minute0.begin();
minute1.begin();
separator.begin();
Serial.begin(9600);
//start I2C communication
if (!rtc.begin()) {
Serial.println("Impossibile trovare il modulo RTC");
Serial.flush();
while(1) delay(10);
}
//Da eliminare
//rtc.adjust(DateTime(2025, 3, 23, 20, 30, 0));
//getTime();
//set initial Date-Time
if (rtc.lostPower()) {
Serial.println("Imposto l'ora....");
// When time needs to be set on a new device, or after a power loss, the
// 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(2025, 3, 23, 20, 50, 0));
//rtc.adjust(strTime);
}
else Serial.println("Il modulo Rtc è in esecuzione....");
pinMode(BTN_STATE, INPUT);
Serial.println(luxmeter());
}
void loop()
{
setState();
switch (state)
{
case 1:
getTime();
Show();
break;
case 2:
Dim_up();
Dim_down();
//getHour();
break;
case 3:
Change_color();
//getMinute();
break;
default:
break;
}
}
//========================== Funzioni =================================
//Funzione di gestione el tasto On/Off
void On_Off()
{
//Se premo il pulsante On/Off e la striscia è spenta
if (debounce(BTN_ON_OFF_PIN)==HIGH && !on) {
//Memorizzo il valore millis
t_pulsante = millis();
//Verifico se il pulsante On/Off rimane premuto
while(debounce(BTN_ON_OFF_PIN)==HIGH){
//Se rimane premuto per il tempo prefissato
if (millis() - t_pulsante >=1000) {
//Controllo che il buffer colore non sia vuoto,
//se non lo è, accendo i LED con quel valore
if (bufferColore != hour0.Color(0, 0, 0)) {
ShowHour(ora, bufferColore);
ShowMinute(minuti, bufferColore);
//Assegno il flag di striscia accesa
on = true;
} else {
//Assegno il colore tutti i LED della striscia
bufferColore = colore;
colore = bufferColore;
ShowHour(ora, bufferColore);
ShowMinute(minuti, bufferColore);
//Assegno il flag di striscia accesa
on = true;
}
}
}
}
//Se, invece, premo il pulsante On/Off e la striscia è accesa
else if (debounce(BTN_ON_OFF_PIN)==HIGH && on) {
//Salvo il valore di luminosità attivo
buffer_luminosita = lum_corrente;
//Salvo il colore attivo
bufferColore = colore;
//Spengo la striscia
Clear_LED();
//Assegno il flag di striscia spenta
on = false;
}
}
// Ottiene un LED specifico dall'array di una cifra
int getDigitLED(int digit, int index) {
const int* array;
switch(digit) {
case 0: array = ZERO; break;
case 1: array = ONE; break;
case 2: array = TWO; break;
case 3: array = THREE; break;
case 4: array = FOUR; break;
case 5: array = FIVE; break;
case 6: array = SIX; break;
case 7: array = SEVEN; break;
case 8: array = EIGHT; break;
case 9: array = NINE; break;
default: array = ZERO; break;
}
return pgm_read_word(&array[index]);
}
//Funzione che mostra l'orario
void Show() {
//Controllo che il buffer colore non sia vuoto,
//se non lo è, accendo i LED con quel valore
if (colore != hour0.Color(0, 0, 0)) {
ShowHour(ora, colore);
ShowMinute(minuti, colore);
}
}
//Funzione di spegnimento della striscia
void Clear_LED() {
//Assegno il colore tutti i LED della striscia
for(int i = 0; i < NUMPIXELS; i++){
hour0.setPixelColor(i, hour0.Color(0, 0, 0));
hour1.setPixelColor(i, hour0.Color(0, 0, 0));
minute0.setPixelColor(i, hour0.Color(0, 0, 0));
minute1.setPixelColor(i, hour0.Color(0, 0, 0));
//Spengo la striscia (assegnando colore 0,0,0 si spegne la striscia)
hour0.show();
hour1.show();
minute0.show();
minute1.show();
}
}
void setState() {
state_ButtonState = digitalRead(BTN_STATE);
if(state_ButtonState != state_LastButtonState) {
if(state_ButtonState == HIGH) {
if(state == 4){
state = 1;
} else {
state = state + 1;
}
Serial.println(state);
}
// Delay per evitare il bouncing
delay(300);
}
state_LastButtonState = state_ButtonState;
}
uint32_t applyBrightness(Color base, uint8_t brightness) {
return hour0.Color(
map(base.r, 0, 255, 0, brightness),
map(base.g, 0, 255, 0, brightness),
map(base.b, 0, 255, 0, brightness)
);
}
void updateClockDisplay() {
uint32_t adjustedColor = applyBrightness(colorList[currentColorIndex], lum_corrente);
ShowHour(ora, adjustedColor);
ShowMinute(minuti, adjustedColor);
}
//Funzione per aumentare la luminosità
void Dim_up() {
if (debounce(BTN_DIM_UP_PIN) == HIGH && lum_corrente < 255) {
lum_corrente = min(255, lum_corrente + step);
updateClockDisplay();
}
}
//Funzione per diminuire la luminosità
void Dim_down() {
if (debounce(BTN_DIM_DOWN_PIN) == HIGH && lum_corrente > 51) {
lum_corrente = max(51, lum_corrente - step);
updateClockDisplay();
}
}
//Funzione per il cambio colore
void Change_color() {
if (debounce(BTN_COLOR_CHANGE_PIN) == HIGH) {
currentColorIndex = (currentColorIndex + 1) % numColors;
updateClockDisplay();
}
}
//Funzione antirimbalzo
boolean debounce(int pin) {
boolean state;
boolean previousState;
previousState = digitalRead(pin);
for(int counter=0; counter < debounceDelay; counter++) {
delay(1);
state = digitalRead(pin);
if( state != previousState) {
counter = 0;
previousState = state; }
}
return state;
}