227 lines
5.1 KiB
C++
227 lines
5.1 KiB
C++
#include "countdown.h"
|
|
#include <LittleFS.h>
|
|
#include "display.h"
|
|
|
|
#define COUNTDOWN_END_SEQUENCE_STEPS 5
|
|
#define COUNTDOWN_END_SEQUENCE_REPEAT 3
|
|
|
|
enum CountdownState {
|
|
CONFIG, READY, RUNNING, PAUSED, END
|
|
};
|
|
|
|
CountdownState countdownState = CONFIG;
|
|
|
|
bool countdownConfigDirty = false;
|
|
|
|
long countdownMillis = 6 * 60 * 1000;
|
|
|
|
long countdownRest = countdownMillis;
|
|
|
|
unsigned long countdownLast = 0;
|
|
|
|
int countdownEndSequenceStep = 0;
|
|
|
|
unsigned long countdownEndSequenceDelay = 0;
|
|
|
|
unsigned long countdownEndSequenceLast = 0;
|
|
|
|
void setState(const CountdownState newState) {
|
|
if (countdownState == newState) {
|
|
return;
|
|
}
|
|
|
|
countdownState = newState;
|
|
const char* name;
|
|
switch (countdownState) {
|
|
case CONFIG:
|
|
countdownRest = countdownMillis;
|
|
name = "CONFIG";
|
|
break;
|
|
case READY:
|
|
countdownRest = countdownMillis;
|
|
name = "READY";
|
|
break;
|
|
case RUNNING:
|
|
countdownLast = millis();
|
|
name = "RUNNING";
|
|
break;
|
|
case PAUSED:
|
|
name = "PAUSED";
|
|
break;
|
|
case END:
|
|
countdownEndSequenceDelay = 0;
|
|
countdownEndSequenceStep = 0;
|
|
name = "END";
|
|
break;
|
|
default:
|
|
name = "[???]";
|
|
break;
|
|
}
|
|
Serial.printf("Mode: %s\n", name);
|
|
}
|
|
|
|
void drawSequence() {
|
|
const auto now = max(1UL, millis());
|
|
if (countdownEndSequenceLast != 0 && now - countdownEndSequenceLast < countdownEndSequenceDelay) {
|
|
return;
|
|
}
|
|
switch (countdownEndSequenceStep % COUNTDOWN_END_SEQUENCE_STEPS) {
|
|
case 0:
|
|
case 2:
|
|
drawAll(WHITE);
|
|
countdownEndSequenceDelay = 100;
|
|
break;
|
|
case 1:
|
|
case 3:
|
|
drawBlack();
|
|
countdownEndSequenceDelay = 100;
|
|
break;
|
|
default:
|
|
drawDashes();
|
|
countdownEndSequenceDelay = 500;
|
|
break;
|
|
}
|
|
countdownEndSequenceStep++;
|
|
countdownEndSequenceLast = now;
|
|
if (countdownEndSequenceStep >= COUNTDOWN_END_SEQUENCE_STEPS * COUNTDOWN_END_SEQUENCE_REPEAT) {
|
|
setState(READY);
|
|
}
|
|
}
|
|
|
|
void countdownDraw() {
|
|
switch (countdownState) {
|
|
case CONFIG:
|
|
drawMillis(countdownRest, MAGENTA);
|
|
break;
|
|
case READY:
|
|
drawMillis(countdownRest, GREEN);
|
|
break;
|
|
case RUNNING: {
|
|
const auto color = countdownRest >= 60000 ? WHITE : (countdownRest >= 10000 ? YELLOW : RED);
|
|
drawMillis(countdownRest, color);
|
|
break;
|
|
}
|
|
case PAUSED:
|
|
drawMillis(countdownRest, BLUE);
|
|
break;
|
|
case END:
|
|
drawSequence();
|
|
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 updateTime() {
|
|
if (countdownState != RUNNING) {
|
|
return;
|
|
}
|
|
const auto now = max(1UL, millis());
|
|
if (countdownLast != 0) {
|
|
const auto diff = now - countdownLast;
|
|
countdownRest -= static_cast<long>(diff);
|
|
if (countdownRest <= 0) {
|
|
countdownRest = 0;
|
|
setState(END);
|
|
}
|
|
}
|
|
countdownLast = now;
|
|
}
|
|
|
|
void countdownLoop() {
|
|
updateTime();
|
|
countdownDraw();
|
|
}
|
|
|
|
void buttonShortPressed() {
|
|
switch (countdownState) {
|
|
case CONFIG: {
|
|
const auto seconds = (countdownMillis / 30000) * 30 % (15 * 60) + 30;
|
|
countdownMillis = seconds * 1000;
|
|
countdownRest = countdownMillis;
|
|
countdownConfigDirty = true;
|
|
break;
|
|
}
|
|
case READY:
|
|
setState(RUNNING);
|
|
break;
|
|
case RUNNING:
|
|
setState(PAUSED);
|
|
break;
|
|
case PAUSED:
|
|
setState(RUNNING);
|
|
break;
|
|
case END:
|
|
setState(READY);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void buttonLongPressed() {
|
|
switch (countdownState) {
|
|
case CONFIG:
|
|
if (countdownConfigDirty) {
|
|
countdownConfigWrite();
|
|
}
|
|
setState(READY);
|
|
break;
|
|
case RUNNING:
|
|
setState(READY);
|
|
break;
|
|
case READY:
|
|
setState(CONFIG);
|
|
break;
|
|
case PAUSED: // NOLINT(*-branch-clone)
|
|
setState(READY);
|
|
break;
|
|
case END:
|
|
setState(READY);
|
|
break;
|
|
}
|
|
}
|