#include // ----------------------------- // CONFIGURACIÓN DE PINES // ----------------------------- #define LED_PIN D0 #define LED_COUNT 16 // cambia según tu tira o aro RGB #define BUZZER_PIN D1 // UART hacia Nextion #define NEXTION_SERIAL Serial1 Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800); // ----------------------------- // ESTADOS // ----------------------------- enum TimerState { STOPPED, RUNNING, PAUSED, FINISHED }; TimerState timerState = STOPPED; // presets en segundos const uint32_t PRESET_5_MIN = 300; const uint32_t PRESET_4_MIN = 240; const uint32_t PRESET_3_MIN = 180; uint32_t selectedDuration = PRESET_5_MIN; uint32_t remainingSeconds = PRESET_5_MIN; unsigned long lastTickMs = 0; unsigned long lastDisplayUpdateMs = 0; bool buzzerActive = false; unsigned long buzzerStartMs = 0; // buffer serial String rxLine = ""; // ----------------------------- // FUNCIONES AUXILIARES // ----------------------------- void nextionEnd() { NEXTION_SERIAL.write(0xFF); NEXTION_SERIAL.write(0xFF); NEXTION_SERIAL.write(0xFF); } void nextionCommand(const String &cmd) { NEXTION_SERIAL.print(cmd); nextionEnd(); } void updateDisplayTime(uint32_t totalSeconds) { uint32_t minutes = totalSeconds / 60; uint32_t seconds = totalSeconds % 60; char buffer[10]; sprintf(buffer, "%02lu:%02lu", minutes, seconds); String cmd = "t0.txt=\""; cmd += buffer; cmd += "\""; nextionCommand(cmd); } void setAllPixels(uint8_t r, uint8_t g, uint8_t b) { for (int i = 0; i < LED_COUNT; i++) { strip.setPixelColor(i, strip.Color(r, g, b)); } strip.show(); } void updateLedByTime() { if (timerState == STOPPED || timerState == PAUSED) { // mostrar color según estado actual del tiempo } if (remainingSeconds <= 10) { // rojo setAllPixels(255, 0, 0); } else if (remainingSeconds <= (selectedDuration * 20UL) / 100UL) { // amarillo setAllPixels(255, 180, 0); } else { // verde setAllPixels(0, 255, 0); } } void stopTimerAndResetToSelected() { timerState = STOPPED; remainingSeconds = selectedDuration; updateDisplayTime(remainingSeconds); updateLedByTime(); } void startTimer() { if (remainingSeconds == 0) { remainingSeconds = selectedDuration; } timerState = RUNNING; lastTickMs = millis(); updateLedByTime(); } void pauseTimer() { if (timerState == RUNNING) { timerState = PAUSED; } } void finishTimer() { timerState = FINISHED; remainingSeconds = 0; updateDisplayTime(remainingSeconds); setAllPixels(255, 0, 0); buzzerActive = true; buzzerStartMs = millis(); tone(BUZZER_PIN, 2000); // 2 kHz } void setPresetMinutes(uint8_t minutes) { switch (minutes) { case 5: selectedDuration = PRESET_5_MIN; break; case 4: selectedDuration = PRESET_4_MIN; break; case 3: selectedDuration = PRESET_3_MIN; break; default: return; } timerState = STOPPED; remainingSeconds = selectedDuration; updateDisplayTime(remainingSeconds); updateLedByTime(); } void processCommand(String cmd) { cmd.trim(); if (cmd == "START") { startTimer(); } else if (cmd == "PAUSE") { pauseTimer(); } else if (cmd == "RESET") { stopTimerAndResetToSelected(); } else if (cmd.startsWith("PRESET:")) { int val = cmd.substring(7).toInt(); setPresetMinutes(val); } } void readNextionSerial() { while (NEXTION_SERIAL.available()) { char c = NEXTION_SERIAL.read(); // filtramos bytes FF de terminación si aparecen if ((uint8_t)c == 0xFF) continue; if (c == '\n' || c == '\r') { if (rxLine.length() > 0) { processCommand(rxLine); rxLine = ""; } } else { rxLine += c; // por seguridad, si el comando ya es reconocible if (rxLine == "START" || rxLine == "PAUSE" || rxLine == "RESET" || rxLine == "PRESET:5" || rxLine == "PRESET:4" || rxLine == "PRESET:3") { processCommand(rxLine); rxLine = ""; } } } } // ----------------------------- // SETUP // ----------------------------- void setup() { pinMode(BUZZER_PIN, OUTPUT); noTone(BUZZER_PIN); Serial.begin(115200); NEXTION_SERIAL.begin(9600); strip.begin(); strip.show(); selectedDuration = PRESET_5_MIN; remainingSeconds = selectedDuration; delay(500); updateDisplayTime(remainingSeconds); updateLedByTime(); } // ----------------------------- // LOOP // ----------------------------- void loop() { readNextionSerial(); unsigned long now = millis(); if (timerState == RUNNING) { if (now - lastTickMs >= 1000) { lastTickMs += 1000; if (remainingSeconds > 0) { remainingSeconds--; updateDisplayTime(remainingSeconds); updateLedByTime(); } if (remainingSeconds == 0) { finishTimer(); } } } if (buzzerActive) { if (now - buzzerStartMs >= 5000) { noTone(BUZZER_PIN); buzzerActive = false; } } }