ModeTimer on console (no real leds, no wifi)

This commit is contained in:
Patrick Haßel 2025-08-07 10:18:39 +02:00
commit 00ba6d1af6
11 changed files with 1239 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.pio

9
platformio.ini Normal file
View File

@ -0,0 +1,9 @@
[env:MatrixDisplay2025]
platform = espressif32
board = esp32dev
framework = arduino
lib_deps = https://github.com/adafruit/Adafruit_NeoPixel
upload_port = /dev/ttyACM0
upload_speed = 921600
monitor_port = /dev/ttyACM0
monitor_speed = 115200

71
src/Button.h Normal file
View File

@ -0,0 +1,71 @@
#ifndef BUTTON_H
#define BUTTON_H
#include <Arduino.h>
enum ButtonEvent {
BUTTON_PRESSED,
BUTTON_PRESSED_LONG,
BUTTON_RELEASED,
BUTTON_RELEASED_SHORT,
BUTTON_RELEASED_LONG,
};
class Button {
public:
typedef void (*callback_t)(ButtonEvent event);
private:
const uint8_t pin;
const callback_t callback;
bool lastState = false;
bool pressedLong = false;
unsigned long lastMillis = 0;
public:
explicit Button(const uint8_t pin, const callback_t callback) : pin(pin), callback(callback) {
//
}
void setup() {
pinMode(pin, INPUT_PULLUP);
lastMillis = millis();
lastState = digitalRead(pin) == LOW;
}
void loop() {
const auto currentMillis = millis();
const auto currentState = digitalRead(pin) == LOW;
const auto duration = currentMillis - lastMillis;
if (lastState != currentState && duration >= 200) {
if (currentState) {
callback(BUTTON_PRESSED);
} else {
callback(BUTTON_RELEASED);
if (pressedLong) {
callback(BUTTON_RELEASED_LONG);
} else {
callback(BUTTON_RELEASED_SHORT);
}
pressedLong = false;
}
lastState = currentState;
lastMillis = currentMillis;
}
if (lastState && duration >= 3000 && !pressedLong) {
pressedLong = true;
callback(BUTTON_PRESSED_LONG);
}
}
};
#endif

414
src/Display.cpp Normal file
View File

@ -0,0 +1,414 @@
#include "Display.h"
#define __ false // NOLINT(*-reserved-identifier)
#define XX true
Color WHITE = {255, 255, 255};
Color BLACK = {0, 0, 0};
Color RED = {255, 0, 0};
Color GREEN = {0, 255, 0};
Color BLUE = {0, 0, 255};
Color YELLOW = {255, 255, 0};
Color CYAN = {0, 255, 255};
Color MAGENTA = {255, 0, 255};
Color ORANGE = {255, 127, 0};
Color GREY = {128, 128, 128};
const bool DOT7[][7][1] = {
{
{__},
{__},
{__},
{__},
{__},
{__},
{XX},
},
{
{__},
{__},
{__},
{__},
{__},
{XX},
{XX},
},
{
{__},
{XX},
{__},
{__},
{__},
{XX},
{__},
},
{
{__},
{XX},
{__},
{__},
{__},
{XX},
{XX},
},
};
const bool SPECIAL7[][7][3] = {
{
{__,__,__},
{__,__,__},
{__,__,__},
{__,__,__},
{__,__,__},
{__,__,__},
{__,__,__},
},
{
{__,__,__},
{__,__,__},
{__,__,__},
{XX,XX,XX},
{__,__,__},
{__,__,__},
{__,__,__},
},
};
const bool NUM7[][7][4] = {
{
{__, XX, XX, __},
{XX, __, __, XX},
{XX, __, __, XX},
{XX, __, __, XX},
{XX, __, __, XX},
{XX, __, __, XX},
{__, XX, XX, __},
},
{
{__, __, XX, XX},
{__, XX, __, XX},
{XX, __, __, XX},
{__, __, __, XX},
{__, __, __, XX},
{__, __, __, XX},
{__, __, __, XX},
},
{
{__, XX, XX, __},
{XX, __, __, XX},
{__, __, __, XX},
{__, __, XX, __},
{__, XX, __, __},
{XX, __, __, __},
{XX, XX, XX, XX},
},
{
{__, XX, XX, __},
{XX, __, __, XX},
{__, __, __, XX},
{__, __, XX, __},
{__, __, __, XX},
{XX, __, __, XX},
{__, XX, XX, __},
},
{
{XX, __, __, XX},
{XX, __, __, XX},
{XX, __, __, XX},
{XX, XX, XX, XX},
{__, __, __, XX},
{__, __, __, XX},
{__, __, __, XX},
},
{
{XX, XX, XX, XX},
{XX, __, __, __},
{XX, __, __, __},
{XX, XX, XX, __},
{__, __, __, XX},
{__, __, __, XX},
{XX, XX, XX, __},
},
{
{__, XX, XX, __},
{XX, __, __, __},
{XX, __, __, __},
{XX, XX, XX, __},
{XX, __, __, XX},
{XX, __, __, XX},
{__, XX, XX, __},
},
{
{XX, XX, XX, XX},
{__, __, __, XX},
{__, __, XX, __},
{__, XX, __, __},
{XX, __, __, __},
{XX, __, __, __},
{XX, __, __, __},
},
{
{__, XX, XX, __},
{XX, __, __, XX},
{XX, __, __, XX},
{__, XX, XX, __},
{XX, __, __, XX},
{XX, __, __, XX},
{__, XX, XX, __},
},
{
{__, XX, XX, __},
{XX, __, __, XX},
{XX, __, __, XX},
{__, XX, XX, XX},
{__, __, __, XX},
{__, __, __, XX},
{__, XX, XX, __},
},
};
const bool ALPHA7[][7][4] = {
{
{__, XX, XX, __},
{XX, __, __, XX},
{XX, __, __, XX},
{XX, XX, XX, XX},
{XX, __, __, XX},
{XX, __, __, XX},
{XX, __, __, XX},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{XX, XX, XX, XX},
{XX, __, __, __},
{XX, __, __, __},
{XX, XX, XX, __},
{XX, __, __, __},
{XX, __, __, __},
{XX, XX, XX, XX},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{XX, XX, XX, __},
{XX, __, __, XX},
{XX, __, __, XX},
{XX, XX, XX, __},
{XX, __, __, __},
{XX, __, __, __},
{XX, __, __, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{__, XX, XX, __},
{XX, __, __, __},
{XX, __, __, __},
{__, XX, XX, __},
{__, __, __, XX},
{__, __, __, XX},
{__, XX, XX, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{XX, __, __, XX},
{XX, __, __, XX},
{XX, __, __, XX},
{XX, __, __, XX},
{XX, __, __, XX},
{XX, __, __, XX},
{__, XX, XX, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
{
{__, __, __, __},
{__, __, __, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, XX, XX, __},
{__, __, __, __},
{__, __, __, __},
},
};

244
src/Display.h Normal file
View File

@ -0,0 +1,244 @@
#ifndef DISPLAY_H
#define DISPLAY_H
#include <Adafruit_NeoPixel.h>
enum Align {
ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT
};
enum Font {
FONT5, FONT7
};
struct Color {
uint8_t r, g, b;
};
extern Color WHITE;
extern Color BLACK;
extern Color RED;
extern Color GREEN;
extern Color BLUE;
extern Color YELLOW;
extern Color CYAN;
extern Color MAGENTA;
extern Color ORANGE;
extern Color GREY;
extern const bool DOT7[][7][1];
extern const bool SPECIAL7[][7][3];
extern const bool NUM7[][7][4];
extern const bool ALPHA7[][7][4];
class Display {
Adafruit_NeoPixel leds;
bool pixels[8 * 32]{};
static int mapPixel(const int x, const int y) {
return y * 32 + (y % 2 == 0 ? x : 31 - x);
}
public:
explicit Display(const uint8_t pin): leds(8 * 32, pin,NEO_GRB + NEO_KHZ800) {
//
}
void setBrightness(int i) {
#if LEDS_ENABLED
leds.setBrightness(max(0, min(i, 255)));
#endif
}
void setup() {
#if LEDS_ENABLED
leds.begin();
#endif
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 32; x++) {
pixels[mapPixel(x, y)] = false;
}
}
setBrightness(32);
clear();
show();
}
void show() {
#if LEDS_ENABLED
leds.show();
#endif
}
void setPixel(const int x, const int y, const Color &color) {
const auto index = mapPixel(x, y);
if (index >= 8 * 32) {
Serial.printf("[ERROR] No pixel at (%d, %d) = %d >= %d\n", x, y, index, 8 * 32);
return;
}
#if LEDS_ENABLED
leds.setPixelColor(index, color.r, color.g, color.b);
#endif
pixels[index] = color.r > 0 || color.g > 0 || color.b > 0;
}
void rect(const int x0, const int y0, const int w, const int h, const Color &color) {
for (int y = y0; y < y0 + h; y++) {
for (int x = x0; x < x0 + w; x++) {
setPixel(x, y, color);
}
}
}
void print(int x, const int y, const Align align, const Font font, const Color &color, const char *format, ...) {
char text[32];
va_list args;
va_start(args, format);
vsnprintf(text, sizeof text, format, args);
va_end(args);
const auto width = measure(text, font);
x = align == ALIGN_LEFT ? x : align == ALIGN_RIGHT ? x - width : x - width / 2;
for (const char *c = text; *c; c++) {
x += printChar(x, y, *c, font, color) + 1;
}
}
int printChar(const int x, const int y, const char c, const Font font, const Color &color) {
if (isDot(c)) {
if (c == '.') {
return printSymbol(x, y, 0, color, DOT7);
}
if (c == ',') {
return printSymbol(x, y, 1, color, DOT7);
}
if (c == ':') {
return printSymbol(x, y, 2, color, DOT7);
}
if (c == ';') {
return printSymbol(x, y, 3, color, DOT7);
}
}
if (isSpecial(c)) {
if (c == ' ') {
return printSymbol(x, y, 0, color, SPECIAL7);
}
if (c == '-') {
return printSymbol(x, y, 1, color, SPECIAL7);
}
}
if (isNumber(c)) {
return printSymbol(x, y, c - '0', color, NUM7);
}
if (isLower(c)) {
return printSymbol(x, y, c - 'a', color, ALPHA7);
}
if (isUpper(c)) {
return printSymbol(x, y, c - 'A', color, ALPHA7);
}
Serial.printf("UNKNOWN SYMBOL '%c'\n", c);
return 0;
}
template<int symbolHeight, int symbolWidth>
int printSymbol(const int x, const int y, const int index, const Color &color, const bool symbols[][symbolHeight][symbolWidth]) {
for (int innerY = 0; innerY < symbolHeight; innerY++) {
for (int innerX = 0; innerX < symbolWidth; innerX++) {
if (symbols[index][innerY][innerX]) {
setPixel(x + innerX, y + innerY, color);
}
}
}
return symbolWidth;
}
void clear() {
rect(0, 0, 32, 8, BLACK);
}
void log() const {
Serial.print("+");
for (int x = 0; x < 32; x++) {
Serial.print("--");
}
Serial.print("+\n");
for (int y = 0; y < 8; y++) {
Serial.print("|");
for (int x = 0; x < 32; x++) {
if (pixels[mapPixel(x, y)]) {
Serial.print("##");
} else {
Serial.print(" ");
}
}
Serial.print("|\n");
}
Serial.print("+");
for (int x = 0; x < 32; x++) {
Serial.print("--");
}
Serial.print("+\n");
Serial.print('\n');
}
static uint8_t measure(const char *text, const Font font) {
uint8_t width = 0;
for (const char *c = text; *c; c++) {
if (c != text) {
width++; // space between chars
}
width += getWidth(*c, font);
}
return width;
}
static uint8_t getWidth(const char c, const Font font) {
if (isDot(c)) {
return 1;
}
if (isSpecial(c)) {
return 3;
}
return font == FONT5 ? 3 : 4;
}
static bool isDot(const char c) {
return c == ':' || c == ';' || c == '.' || c == ',';
}
static bool isSpecial(const char c) {
return c == ' ' || c == '-';
}
static bool isNumber(const char c) {
return c >= '0' && c <= '9';
}
static bool isLower(const char c) {
return c >= 'a' && c <= 'z';
}
static bool isUpper(const char c) {
return c >= 'A' && c <= 'Z';
}
};
#endif

15
src/beep.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef BEEP_H
#define BEEP_H
#include <Arduino.h>
inline void beepSet(const bool state) {
digitalWrite(2, state ? HIGH : LOW);
}
inline void beepSetup() {
pinMode(2,OUTPUT);
beepSet(false);
}
#endif

47
src/main.cpp Normal file
View File

@ -0,0 +1,47 @@
#include <WiFi.h>
#include "Button.h"
#include "mode/Mode.h"
#include "mode/ModeTimer.h"
Display display(33);
ModeTimer mode;
void buttonCallback(const ButtonEvent event) {
if (event == BUTTON_PRESSED) {
mode.buttonOK();
}
if (BUTTON_PRESSED_LONG) {
mode.buttonESC();
}
}
Button button(23, buttonCallback);
void setup() {
WiFi.disconnect();
delay(500);
Serial.begin(115200);
Serial.println("\n\n\nStartup!");
beepSetup();
display.setup();
mode.init();
}
void loop() {
const uint64_t now = millis();
static uint64_t lastMillis = 0;
const uint64_t deltaMillis = now - lastMillis;
lastMillis = now;
mode.step(deltaMillis);
mode.draw(display);
const auto code = Serial.read();
if (code == ' ') {
mode.buttonOK();
}
if (code == 'x') {
mode.buttonESC();
}
}

53
src/mode/Mode.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef MODE_H
#define MODE_H
#include <WString.h>
#include <Preferences.h>
#include "Display.h"
class Mode {
public:
virtual ~Mode() = default;
String getName() const {
return name;
}
virtual void init() {
//
}
virtual void step(uint64_t deltaMillis) {
//
}
virtual void draw(Display &display) {
//
}
virtual void buttonOK() {
//
}
virtual void buttonESC() {
//
}
virtual void stop() {
//
}
protected:
const char *name;
explicit Mode(const char *name) : name(name) {
//
}
};
#endif

193
src/mode/ModeTimer.h Normal file
View File

@ -0,0 +1,193 @@
#ifndef MODE_TIMER_H
#define MODE_TIMER_H
#include "beep.h"
#include "Mode.h"
#include "Rest.h"
#include "Timer.h"
enum Stage {
FIRST_HALF, SECOND_HALF, LAST_MINUTE, FINALE
};
class ModeTimer final : public Mode {
Timer countdown;
Timer flash;
Timer pause;
Timer beep;
bool dirty = true;
bool dirtyPrint = true;
Rest rest{0};
Rest last{0};
public:
explicit ModeTimer()
: Mode("Timer"),
countdown("countdown", [this]() {
flash.start();
dirty = true;
dirtyPrint = true;
}),
flash("flash", [this]() {
dirty = true;
dirtyPrint = true;
}),
pause("pause", [this]() {
dirty = true;
dirtyPrint = true;
}),
beep("beep", [this]() { beepSet(beep.isEven()); }) {
//
}
void init() override {
Serial.printf("Mode INIT: %s\n", name);
Preferences preferences;
preferences.begin("Timer", true);
const auto configMillis = preferences.getULong("configMillis", 40 * 1000);
preferences.end();
beepSet(false);
countdown.stop();
flash.stop();
pause.stop();
beep.stop();
countdown.countConfig(configMillis, 1);
flash.countConfig(100, 19);
beep.countConfig(200, 1);
pause.foreverConfig(500);
dirty = true;
dirtyPrint = true;
}
void step(const uint64_t deltaMillis) override {
flash.step(deltaMillis);
pause.step(deltaMillis);
beep.step(deltaMillis);
if (!pause.isRunning()) {
countdown.step(deltaMillis);
}
}
void buttonOK() override {
if (flash.isRunning()) {
return;
}
if (!countdown.isRunning()) {
countdown.start();
} else {
pause.toggle();
}
dirty = true;
dirtyPrint = true;
}
void buttonESC() override {
init();
}
void draw(Display &display) override {
rest = Rest(countdown.getRestMillis());
display.clear();
if (flash.isRunning()) {
const auto state = flash.isEven();
if (state) {
display.rect(0, 0, 32, 8, WHITE);
}
beepSet(true);
} else {
if (countdown.isRunning()) {
if (pause.isRunning() && pause.isEven()) {
display.print(15, 0, ALIGN_CENTER, FONT7, BLUE, "PAUSE");
} else {
drawTime(display);
}
} else {
beepSet(false);
display.print(15, 0, ALIGN_CENTER, FONT7, RED, "-- : --");
}
}
if (dirty) {
dirty = false;
display.show();
if (dirtyPrint) {
dirtyPrint = false;
rest.log();
display.log();
}
}
last = rest;
}
private:
void doBeep(const uint32_t count) {
beepSet(true);
beep.startCount(count * 2 - 1);
}
void drawTime(Display &display) {
Color color = RED;
Stage stage = FINALE;
if (rest.millisTotal > countdown.getConfigMillis() / 2) {
color = WHITE;
stage = FIRST_HALF;
} else if (rest.millisTotal > 15 * 1000) {
color = YELLOW;
stage = SECOND_HALF;
} else if (rest.millisTotal > 10 * 1000) {
color = ORANGE;
stage = LAST_MINUTE;
}
static int lastStage = stage;
if (lastStage != stage) {
if (stage == SECOND_HALF) {
doBeep(1);
} else if (stage == LAST_MINUTE) {
doBeep(2);
}
lastStage = stage;
}
if (rest.secondChanged(last)) {
if (stage == FINALE) {
doBeep(1);
}
}
if (rest.daysTotal > 0) {
display.print(16, 0, ALIGN_CENTER, FONT7, color, "%3d. %2d", rest.daysPart, rest.hoursPart);
dirty |= rest.hourChanged(last);
dirtyPrint = true;
} else if (rest.hoursTotal > 0) {
display.print(16, 0, ALIGN_CENTER, FONT7, color, "%2d:%02d:%02d", rest.hoursPart, rest.minutesPart, rest.secondsPart);
dirty |= rest.secondChanged(last);
dirtyPrint = true;
} else if (rest.minutesTotal > 0) {
display.print(16, 0, ALIGN_CENTER, FONT7, color, "%2d:%02d", rest.minutesPart, rest.secondsPart);
dirty |= rest.secondChanged(last);
dirtyPrint = true;
} else {
display.print(16, 0, ALIGN_CENTER, FONT7, color, "%2d.%d", rest.secondsPart, rest.decisPart);
dirty |= rest.deciChanged(last);
dirtyPrint |= rest.secondChanged(last);
}
}
};
#endif

66
src/mode/Rest.h Normal file
View File

@ -0,0 +1,66 @@
#ifndef REST_H
#define REST_H
#include <cstdint>
#include "ModeTimer.h"
#include "ModeTimer.h"
struct Rest {
uint64_t millisTotal;
uint64_t decisTotal;
uint64_t secondsTotal;
uint64_t minutesTotal;
uint64_t hoursTotal;
uint64_t daysTotal;
uint32_t decisPart;
uint32_t secondsPart;
uint32_t minutesPart;
uint32_t hoursPart;
uint32_t daysPart;
explicit Rest(const uint32_t millisTotal)
: millisTotal(millisTotal),
decisTotal(millisTotal / 100),
secondsTotal(decisTotal / 10),
minutesTotal(secondsTotal / 60),
hoursTotal(minutesTotal / 60),
daysTotal(hoursTotal / 24),
decisPart(decisTotal % 10),
secondsPart(secondsTotal % 60),
minutesPart(minutesTotal % 60),
hoursPart(hoursTotal % 24),
daysPart(daysTotal) {
//
}
bool deciChanged(const Rest &rest) const {
return decisTotal != rest.decisTotal;
}
bool secondChanged(const Rest &last) const {
return secondsTotal != last.secondsTotal;
}
bool hourChanged(const Rest &rest) const {
return hoursTotal != rest.hoursTotal;
}
void log() const {
Serial.printf("%3d.%02d:%02d:%02d.%d\n", daysPart, hoursPart, minutesPart, secondsPart, decisPart);
}
};
#endif

126
src/mode/Timer.h Normal file
View File

@ -0,0 +1,126 @@
#ifndef TIMER_H
#define TIMER_H
class Timer {
const char *name;
std::function<void()> callback;
bool running = false;
bool forever = false;
uint64_t configCount = 0;
uint64_t configMillis = 0;
uint64_t restMillis = 0;
uint64_t restCount = 0;
uint64_t doneCount = 0;
public:
explicit Timer(const char *name, std::function<void()> callback): name(name), callback(std::move(callback)) {
//
}
void foreverConfig(const uint64_t configMillis) {
this->configMillis = configMillis;
this->configCount = 1;
this->forever = true;
// Serial.printf("Timer(%s) CONFIG: %lld ms, FOREVER\n", name, configMillis);
}
void countConfig(const uint64_t configMillis, const uint64_t configCount) {
this->configMillis = configMillis;
this->configCount = configCount;
this->forever = false;
// Serial.printf("Timer(%s) CONFIG: %lld ms x %lld\n", name, configMillis, configCount);
}
void startCount(const uint64_t count) {
configCount = count;
start();
}
void start() {
if (configCount > 0 && configMillis > 0) {
doneCount = 0;
restCount = configCount;
restMillis = configMillis;
running = true;
// Serial.printf("Timer(%s) START\n", name);
} else {
// Serial.printf("Timer(%s) CANNOT START ZERO INTERVAL", name);
stop();
}
}
void stop() {
if (running || restCount > 0 || restMillis > 0) {
restCount = 0;
restMillis = 0;
running = false;
// Serial.printf("Timer(%s) STOP\n", name);
}
}
void step(uint64_t deltaMillis) {
auto ticks = 0;
while (running && restCount > 0 && deltaMillis > 0) {
if (restMillis > deltaMillis) {
restMillis -= deltaMillis;
break;
}
ticks++;
doneCount++;
if (forever) {
// Serial.printf("Timer(%s) TICK\n", name);
} else {
restCount--;
// Serial.printf("Timer(%s) TICK %lld/%lld\n", name, configCount - restCount, configCount);
}
deltaMillis -= restMillis;
restMillis = configMillis;
if (restCount == 0) {
stop();
}
if (callback) {
callback();
}
}
if (ticks > 1) {
// Serial.printf("[WARN] Skipped %d ticks\n", ticks - 1);
}
}
bool isRunning() const {
return running;
}
uint64_t getRestMillis() const {
return restMillis;
}
uint64_t getConfigMillis() const {
return configMillis;
}
void toggle() {
if (running) {
stop();
} else {
start();
}
}
bool isEven() const {
return doneCount % 2 == 0;
}
};
#endif