#include #include #include #define PIN_LED 6 #define NUM_LEDS 15 #define BUTTON_HOUR 2 #define BUTTON_MINUTE_5 3 #define BUTTON_MINUTE_1 4 #define BUTTON_BRIGHTNESS 5 Adafruit_NeoPixel strip(NUM_LEDS, PIN_LED, NEO_GRB + NEO_KHZ800); RTC_DS3231 rtc; enum State { SHOW_HOUR, WAIT_HOUR_OFF, SHOW_MIN_BLOCK, WAIT_MIN_OFF, SHOW_MIN_EXTRA, WAIT_RESTART }; State currentState = SHOW_HOUR; unsigned long stateStartTime = 0; unsigned long blinkStart = 0; bool blinking = false; bool ledsOn = false; int extraBlinkCount = 0; int lastSecond = -1; int lastMinute = -1; bool lastHourButtonState = HIGH; bool lastMin5ButtonState = HIGH; bool lastMin1ButtonState = HIGH; bool lastBrightnessButtonState = HIGH; bool adjusting = false; unsigned long lastAdjustTime = 0; enum AdjustType { NONE, HOUR, MIN5, MIN1 } adjustType = NONE; int hourIndex = 0; bool isPM = false; int minuteIndex = 0; int minuteRemainder = 0; int yellowStep = 0; // Contador del botón amarillo // ---------------------- BRILLO ----------------------- int brightnessLevels[] = {250,192,165,130,75,30,12,4,0}; int brightnessIndex = 0; int numBrightnessLevels = sizeof(brightnessLevels) / sizeof(brightnessLevels[0]); bool runningHourAnimation = false; const unsigned long hourAnimationDuration = 1500; // EEPROM #define EEPROM_ADDR_BRIGHTNESS 0 #define EEPROM_ADDR_HOUR 1 #define EEPROM_ADDR_MINUTE 2 // Nuevo: modo brillo bool inBrightnessMode = false; unsigned long brightnessModeStart = 0; // ---------------------------------------- void setup() { strip.begin(); strip.setBrightness(255); strip.show(); pinMode(BUTTON_HOUR, INPUT_PULLUP); pinMode(BUTTON_MINUTE_5, INPUT_PULLUP); pinMode(BUTTON_MINUTE_1, INPUT_PULLUP); pinMode(BUTTON_BRIGHTNESS, INPUT_PULLUP); Serial.begin(9600); if (!rtc.begin()) { Serial.println("No se encontró el RTC"); while (1); } // Leer brillo guardado en EEPROM brightnessIndex = EEPROM.read(EEPROM_ADDR_BRIGHTNESS); if (brightnessIndex < 0 || brightnessIndex >= numBrightnessLevels) brightnessIndex = 0; strip.setBrightness(brightnessLevels[brightnessIndex]); // Si RTC perdió energía, restaurar desde EEPROM if (rtc.lostPower()) { int savedHour = EEPROM.read(EEPROM_ADDR_HOUR); int savedMinute = EEPROM.read(EEPROM_ADDR_MINUTE); if (savedHour >= 0 && savedHour < 24 && savedMinute >= 0 && savedMinute < 60) { rtc.adjust(DateTime(2025, 1, 1, savedHour, savedMinute, 0)); Serial.println("RTC restaurado desde EEPROM"); } else { rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); Serial.println("RTC restaurado con fecha de compilación"); } } DateTime now = rtc.now(); prepareDisplayValues(now.hour(), now.minute()); stateStartTime = millis(); lastSecond = now.second(); lastMinute = now.minute(); } // ---------------------------------------- void loop() { DateTime now = rtc.now(); handleButtons(now); if (now.second() != lastSecond) { lastSecond = now.second(); printTime(now); } if (inBrightnessMode) { if (millis() - brightnessModeStart >= 2000) { inBrightnessMode = false; currentState = SHOW_HOUR; stateStartTime = millis(); } return; } if (runningHourAnimation) return; if (!adjusting) { int nowMinute = now.minute(); if (nowMinute != lastMinute) { if (nowMinute == 0) runHourChangeAnimation(); lastMinute = nowMinute; prepareDisplayValues(now.hour(), nowMinute); currentState = SHOW_HOUR; stateStartTime = millis(); } } if (adjusting) { if (millis() - lastAdjustTime > 1000) { adjusting = false; adjustType = NONE; } else { clearAll(); switch(adjustType){ case HOUR: setLedColor(hourIndex, isPM ? strip.Color(255,0,0) : strip.Color(0,0,255)); break; case MIN5: if (minuteIndex >= 0) { if (minuteIndex == 1) { setLedColor(1, strip.Color(0,255,0)); } else { setLedColor(minuteIndex, strip.Color(0,255,0)); } } break; case MIN1: switch(yellowStep){ case 1: setLedColor(14, strip.Color(255,255,255)); break; case 2: setLedColor(2, strip.Color(255,255,255)); break; case 3: setLedColor(13, strip.Color(255,255,255)); break; case 4: setLedColor(3, strip.Color(255,255,255)); break; case 0: setLedColor(1, strip.Color(255,255,0)); break; } break; default: break; } strip.show(); return; } } switch(currentState) { case SHOW_HOUR: clearAll(); setLedColor(hourIndex, isPM ? strip.Color(255,0,0) : strip.Color(0,0,255)); strip.show(); stateStartTime = millis(); currentState = WAIT_HOUR_OFF; break; case WAIT_HOUR_OFF: if (millis() - stateStartTime >= 1500) { currentState = SHOW_MIN_BLOCK; stateStartTime = millis(); } break; case SHOW_MIN_BLOCK: clearAll(); if (minuteIndex >= 0) { if (minuteIndex == 1) { setLedColor(1, strip.Color(0,255,0)); } else { setLedColor(minuteIndex, strip.Color(0,255,0)); } } strip.show(); stateStartTime = millis(); currentState = WAIT_MIN_OFF; break; case WAIT_MIN_OFF: if (millis() - stateStartTime >= 1500) { currentState = SHOW_MIN_EXTRA; blinkStart = millis(); ledsOn = false; blinking = false; extraBlinkCount = 0; } break; case SHOW_MIN_EXTRA: if (minuteRemainder == 0) { currentState = WAIT_RESTART; stateStartTime = millis(); break; } if (!blinking && millis() - blinkStart >= 0) { int blinkLed = minuteIndex; setLedColor(blinkLed, strip.Color(255,255,255)); strip.show(); blinking = true; ledsOn = true; blinkStart = millis(); } if (blinking && ledsOn && millis() - blinkStart >= 500) { int blinkLed = minuteIndex >= 0 ? minuteIndex : 1; setLedColor(blinkLed, 0); strip.show(); ledsOn = false; blinkStart = millis(); } if (blinking && !ledsOn && millis() - blinkStart >= 500) { extraBlinkCount++; blinking = false; if (extraBlinkCount >= minuteRemainder) { currentState = WAIT_RESTART; stateStartTime = millis(); } } break; case WAIT_RESTART: if (millis() - stateStartTime >= 1000) { currentState = SHOW_HOUR; stateStartTime = millis(); } break; } } // ---------------------------------------- void handleButtons(DateTime now) { bool currHourBtn = digitalRead(BUTTON_HOUR); bool currMin5Btn = digitalRead(BUTTON_MINUTE_5); bool currMin1Btn = digitalRead(BUTTON_MINUTE_1); bool currBrightnessBtn = digitalRead(BUTTON_BRIGHTNESS); // Botón hora if (lastHourButtonState == HIGH && currHourBtn == LOW) { int newHour = (now.hour() + 1) % 24; int currentMinute = now.minute(); rtc.adjust(DateTime(now.year(), now.month(), now.day(), newHour, currentMinute, 1)); EEPROM.update(EEPROM_ADDR_HOUR, newHour); EEPROM.update(EEPROM_ADDR_MINUTE, currentMinute); adjusting = true; lastAdjustTime = millis(); adjustType = HOUR; yellowStep = 0; prepareDisplayValues(newHour, currentMinute); } // Botón +5 minutos if (lastMin5ButtonState == HIGH && currMin5Btn == LOW) { int currentMinute = now.minute(); int currentHour = now.hour(); int newMinute; if(currentMinute % 5 == 0) newMinute = currentMinute + 5; else newMinute = ((currentMinute / 5) + 1) * 5; if(newMinute >= 60) newMinute = 0; rtc.adjust(DateTime(now.year(), now.month(), now.day(), currentHour, newMinute, 1)); EEPROM.update(EEPROM_ADDR_HOUR, currentHour); EEPROM.update(EEPROM_ADDR_MINUTE, newMinute); adjusting = true; lastAdjustTime = millis(); adjustType = MIN5; yellowStep = 0; prepareDisplayValues(currentHour, newMinute); } // Botón amarillo +1 minuto if (lastMin1ButtonState == HIGH && currMin1Btn == LOW) { yellowStep++; if (yellowStep > 4) yellowStep = 0; int currentHour = now.hour(); int currentMinute = now.minute(); int blockMinute = (currentMinute / 5) * 5; int newMinute; switch(yellowStep){ case 1: newMinute = blockMinute + 1; break; case 2: newMinute = blockMinute + 2; break; case 3: newMinute = blockMinute + 3; break; case 4: newMinute = blockMinute + 4; break; case 0: newMinute = blockMinute; break; } rtc.adjust(DateTime(now.year(), now.month(), now.day(), currentHour, newMinute, 1)); EEPROM.update(EEPROM_ADDR_HOUR, currentHour); EEPROM.update(EEPROM_ADDR_MINUTE, newMinute); adjusting = true; lastAdjustTime = millis(); adjustType = MIN1; prepareDisplayValues(currentHour,newMinute); } // Botón brillo if (lastBrightnessButtonState == HIGH && currBrightnessBtn == LOW) { brightnessIndex++; if (brightnessIndex >= numBrightnessLevels) brightnessIndex = 0; strip.setBrightness(brightnessLevels[brightnessIndex]); EEPROM.update(EEPROM_ADDR_BRIGHTNESS, brightnessIndex); inBrightnessMode = true; brightnessModeStart = millis(); clearAll(); uint32_t color = isPM ? strip.Color(255,0,0) : strip.Color(0,0,255); for(int i=1;i<=14;i++){ if(i==7 || i==8) continue; strip.setPixelColor(i,color); } strip.show(); } lastHourButtonState = currHourBtn; lastMin5ButtonState = currMin5Btn; lastMin1ButtonState = currMin1Btn; lastBrightnessButtonState = currBrightnessBtn; } // ---------------------------------------- void prepareDisplayValues(int hour,int minute) { switch(hour%12){ case 0: hourIndex = 1; break; case 1: hourIndex =14; break; case 2: hourIndex = 2; break; case 3: hourIndex = 13; break; case 4: hourIndex = 3; break; case 5: hourIndex = 12; break; case 6: hourIndex = 4; break; case 7: hourIndex = 11; break; case 8: hourIndex = 5; break; case 9: hourIndex = 10; break; case 10: hourIndex = 6; break; case 11: hourIndex = 9; break; } isPM = hour >= 12; int block = minute / 5; minuteRemainder = minute % 5; switch(block){ case 0: minuteIndex = 1; break; case 1: minuteIndex = 14; break; case 2: minuteIndex = 2; break; case 3: minuteIndex = 13; break; case 4: minuteIndex = 3; break; case 5: minuteIndex = 12; break; case 6: minuteIndex = 4; break; case 7: minuteIndex = 11; break; case 8: minuteIndex = 5; break; case 9: minuteIndex = 10; break; case 10: minuteIndex = 6; break; case 11: minuteIndex = 9; break; } } // ---------------------------------------- void setLedColor(int index,uint32_t color){ if(index==0 || index==7 || index==8) return; if(index>=0 && index=12; uint32_t color = pm ? strip.Color(255,0,0) : strip.Color(0,0,255); runningHourAnimation=true; int spiralOrder[] = {1,14,2,13,3,12,4,11,5,10,6,9}; int numSteps = sizeof(spiralOrder)/sizeof(spiralOrder[0]); int delayPerStep = hourAnimationDuration / numSteps; for(int i=0;i