#include "countdown.h" #include #include "display.h" enum State { CONFIG, READY, RUNNING, PAUSED, END }; State state = CONFIG; bool countdownConfigDirty = false; long countdownMillis = 6 * 60 * 1000; long countdownRest = countdownMillis; unsigned long last = 0; void updateTime() { if (state != RUNNING) { return; } const auto now = max(1UL, millis()); if (last != 0) { const auto diff = now - last; countdownRest -= static_cast(diff); if (countdownRest < 0) { countdownRest = 0; } } last = now; } void drawSequence() { for (int x = 0; x < 3; ++x) { for (int i = 0; i < 2; ++i) { drawAll(WHITE); delay(100); drawBlack(); delay(100); } drawDashes(); delay(500); } } void setState(const State newState) { if (state == newState) { return; } state = newState; const char* name; switch (state) { case CONFIG: countdownRest = countdownMillis; name = "CONFIG"; break; case READY: countdownRest = countdownMillis; name = "READY"; break; case RUNNING: last = millis(); name = "RUNNING"; break; case PAUSED: name = "PAUSED"; break; case END: drawSequence(); name = "END"; break; default: name = "[???]"; break; } Serial.printf("Mode: %s\n", name); } void countdownUpdate() { switch (state) { case CONFIG: drawMillis(countdownRest, MAGENTA); break; case READY: drawMillis(countdownRest, GREEN); break; case RUNNING: { if (countdownRest > 0) { const auto color = countdownRest >= 60000 ? WHITE : (countdownRest >= 10000 ? YELLOW : RED); drawMillis(countdownRest, color); } else { setState(END); setState(READY); } break; } case PAUSED: drawMillis(countdownRest, BLUE); break; case END: drawDashes(); break; } } void countdownConfigLoad() { if (LittleFS.begin(true)) { Serial.println("Filesystem mounted."); File file = LittleFS.open("/countdownMillis"); if (file) { const String content = file.readString(); if (content) { countdownMillis = content.toInt(); Serial.printf("Read countdownMillis from file: countdownMillis=%lu, file=%s", countdownMillis, file.path()); } else { Serial.printf("WARN: Cannot parse content of: %s\n", file.path()); } file.close(); } else { Serial.printf("WARN: Cannot open file for READ: %s\n", file.path()); } LittleFS.end(); Serial.println("Filesystem unmounted."); } else { Serial.println("ERROR: Failed to mount filesystem."); } } void countdownConfigWrite() { if (LittleFS.begin(true)) { Serial.println("Filesystem mounted."); File file = LittleFS.open("/countdownMillis", "w", true); if (file) { file.printf("%lu", countdownMillis); Serial.printf("Wrote countdownMillis to file: countdownMillis=%lu, file=%s", countdownMillis, file.path()); file.close(); countdownConfigDirty = false; } else { Serial.printf("WARN: Cannot open file for WRITE: %s\n", file.path()); } LittleFS.end(); Serial.println("Filesystem unmounted."); } else { Serial.println("ERROR: failed to mount filesystem"); } } void countdownSetup() { countdownConfigLoad(); setState(READY); } void countdownLoop() { updateTime(); countdownUpdate(); } void buttonShortPressed() { switch (state) { case CONFIG: { const auto seconds = (countdownMillis / 30000) * 30 % (15 * 60) + 30; countdownMillis = seconds * 1000; countdownRest = countdownMillis; countdownConfigDirty = true; break; } case READY: case END: setState(CONFIG); break; case RUNNING: case PAUSED: setState(READY); break; } } void buttonLongPressed() { switch (state) { case CONFIG: if (countdownConfigDirty) { countdownConfigWrite(); } setState(READY); break; case RUNNING: setState(PAUSED); break; case READY: case PAUSED: setState(RUNNING); break; case END: setState(READY); break; } }