Compare commits

...

3 Commits

Author SHA1 Message Date
3a151703a4 removed space prefixes from Timer display 2025-08-07 13:35:03 +02:00
528f2816ca wifi, server 2025-08-07 13:12:48 +02:00
67a676c320 code clean 2025-08-07 11:06:15 +02:00
10 changed files with 261 additions and 51 deletions

View File

@ -3,12 +3,14 @@
#include <Arduino.h> #include <Arduino.h>
#define BEEP_PIN 2
inline void beepSet(const bool state) { inline void beepSet(const bool state) {
digitalWrite(2, state ? HIGH : LOW); digitalWrite(BEEP_PIN, state ? HIGH : LOW);
} }
inline void beepSetup() { inline void beepSetup() {
pinMode(2,OUTPUT); pinMode(BEEP_PIN,OUTPUT);
beepSet(false); beepSet(false);
} }

View File

@ -1,12 +1,57 @@
#include <WiFi.h>
#include "Button.h" #include "Button.h"
#include "server.h"
#include "wifi.h"
#include "mode/Mode.h" #include "mode/Mode.h"
#include "mode/ModeTimer.h" #include "mode/ModeTimer.h"
void stepMode();
void readConsole();
void buttonCallback(ButtonEvent event);
Display display; Display display;
ModeTimer mode; ModeTimer mode;
Button button(23, buttonCallback);
void setup() {
delay(500);
Serial.begin(115200);
Serial.println("\n\n\nStartup!");
beepSetup();
display.setup();
mode.init();
serverSetup();
}
void loop() {
stepMode();
readConsole();
wifiLoop();
}
void stepMode() {
const uint64_t now = millis();
static uint64_t lastMillis = 0;
const uint64_t deltaMillis = now - lastMillis;
lastMillis = now;
mode.step(deltaMillis);
mode.draw(display);
}
void readConsole() {
const auto code = Serial.read();
if (code == ' ') {
mode.buttonOK();
}
if (code == 'x') {
mode.buttonESC();
}
}
void buttonCallback(const ButtonEvent event) { void buttonCallback(const ButtonEvent event) {
if (event == BUTTON_PRESSED) { if (event == BUTTON_PRESSED) {
mode.buttonOK(); mode.buttonOK();
@ -15,33 +60,3 @@ void buttonCallback(const ButtonEvent event) {
mode.buttonESC(); 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();
}
}

8
src/mode.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef MODE2_H
#define MODE2_H
#include "mode/ModeTimer.h"
extern ModeTimer mode;
#endif

View File

@ -2,7 +2,6 @@
#define MODE_H #define MODE_H
#include <WString.h> #include <WString.h>
#include <Preferences.h>
#include "Display.h" #include "Display.h"

View File

@ -1,6 +1,8 @@
#ifndef MODE_TIMER_H #ifndef MODE_TIMER_H
#define MODE_TIMER_H #define MODE_TIMER_H
#include <Preferences.h>
#include "beep.h" #include "beep.h"
#include "Mode.h" #include "Mode.h"
#include "Rest.h" #include "Rest.h"
@ -10,6 +12,38 @@ enum Stage {
FIRST_HALF, SECOND_HALF, LAST_MINUTE, FINALE FIRST_HALF, SECOND_HALF, LAST_MINUTE, FINALE
}; };
inline uint32_t getTimerConfig(const char *name, const uint32_t fallback) {
Preferences preferences;
preferences.begin("Timer", true);
const auto configMillis = preferences.getULong(name, fallback);
preferences.end();
return configMillis;
}
inline void setTimerConfig(const char *name, const uint32_t configMillis) {
Preferences preferences;
preferences.begin("Timer", false);
preferences.putULong(name, configMillis);
preferences.end();
Serial.printf("CONFIG SET: %s.%s = %d\n", "Timer", name, configMillis);
}
inline uint32_t getTimerConfigCountdown() {
return getTimerConfig("countdown", 6 * 60 * 1000);
}
inline uint32_t getTimerConfigYellow() {
return getTimerConfig("yellow", 3 * 60 * 1000);
}
inline uint32_t getTimerConfigOrange() {
return getTimerConfig("orange", 1 * 60 * 1000);
}
inline uint32_t getTimerConfigRed() {
return getTimerConfig("red", 10 * 1000);
}
class ModeTimer final : public Mode { class ModeTimer final : public Mode {
Timer countdown; Timer countdown;
@ -20,6 +54,12 @@ class ModeTimer final : public Mode {
Timer beep; Timer beep;
uint32_t yellow = 3 * 60 * 1000;
uint32_t orange = 1 * 60 * 1000;
uint32_t red = 10 * 1000;
bool dirty = true; bool dirty = true;
bool dirtyPrint = true; bool dirtyPrint = true;
@ -52,11 +92,6 @@ public:
void init() override { void init() override {
Serial.printf("Mode INIT: %s\n", name); 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); beepSet(false);
countdown.stop(); countdown.stop();
@ -64,6 +99,10 @@ public:
pause.stop(); pause.stop();
beep.stop(); beep.stop();
const auto configMillis = getTimerConfigCountdown();
yellow = getTimerConfigYellow();
orange = getTimerConfigOrange();
red = getTimerConfigRed();
countdown.countConfig(configMillis, 1); countdown.countConfig(configMillis, 1);
flash.countConfig(100, 19); flash.countConfig(100, 19);
beep.countConfig(200, 1); beep.countConfig(200, 1);
@ -87,6 +126,7 @@ public:
return; return;
} }
if (!countdown.isRunning()) { if (!countdown.isRunning()) {
init();
countdown.start(); countdown.start();
} else { } else {
pause.toggle(); pause.toggle();
@ -143,13 +183,13 @@ private:
void drawTime(Display &display) { void drawTime(Display &display) {
Color color = RED; Color color = RED;
Stage stage = FINALE; Stage stage = FINALE;
if (rest.millisTotal > countdown.getConfigMillis() / 2) { if (rest.millisTotal > yellow) {
color = WHITE; color = WHITE;
stage = FIRST_HALF; stage = FIRST_HALF;
} else if (rest.millisTotal > 15 * 1000) { } else if (rest.millisTotal > orange) {
color = YELLOW; color = YELLOW;
stage = SECOND_HALF; stage = SECOND_HALF;
} else if (rest.millisTotal > 10 * 1000) { } else if (rest.millisTotal > red) {
color = ORANGE; color = ORANGE;
stage = LAST_MINUTE; stage = LAST_MINUTE;
} }
@ -171,19 +211,19 @@ private:
} }
if (rest.daysTotal > 0) { if (rest.daysTotal > 0) {
display.print(16, 0, ALIGN_CENTER, FONT7, color, "%3d. %2d", rest.daysPart, rest.hoursPart); display.print(16, 0, ALIGN_CENTER, FONT7, color, "%d. %2d", rest.daysPart, rest.hoursPart);
dirty |= rest.hourChanged(last); dirty |= rest.hourChanged(last);
dirtyPrint = true; dirtyPrint = true;
} else if (rest.hoursTotal > 0) { } else if (rest.hoursTotal > 0) {
display.print(16, 0, ALIGN_CENTER, FONT7, color, "%2d:%02d:%02d", rest.hoursPart, rest.minutesPart, rest.secondsPart); display.print(16, 0, ALIGN_CENTER, FONT7, color, "%d:%02d:%02d", rest.hoursPart, rest.minutesPart, rest.secondsPart);
dirty |= rest.secondChanged(last); dirty |= rest.secondChanged(last);
dirtyPrint = true; dirtyPrint = true;
} else if (rest.minutesTotal > 0) { } else if (rest.minutesTotal > 0) {
display.print(16, 0, ALIGN_CENTER, FONT7, color, "%2d:%02d", rest.minutesPart, rest.secondsPart); display.print(16, 0, ALIGN_CENTER, FONT7, color, "%d:%02d", rest.minutesPart, rest.secondsPart);
dirty |= rest.secondChanged(last); dirty |= rest.secondChanged(last);
dirtyPrint = true; dirtyPrint = true;
} else { } else {
display.print(16, 0, ALIGN_CENTER, FONT7, color, "%2d.%d", rest.secondsPart, rest.decisPart); display.print(16, 0, ALIGN_CENTER, FONT7, color, "%d.%d", rest.secondsPart, rest.decisPart);
dirty |= rest.deciChanged(last); dirty |= rest.deciChanged(last);
dirtyPrint |= rest.secondChanged(last); dirtyPrint |= rest.secondChanged(last);
} }

View File

@ -1,11 +1,6 @@
#ifndef REST_H #ifndef REST_H
#define REST_H #define REST_H
#include <cstdint>
#include "ModeTimer.h"
#include "ModeTimer.h"
struct Rest { struct Rest {
uint64_t millisTotal; uint64_t millisTotal;

85
src/server.cpp Normal file
View File

@ -0,0 +1,85 @@
#include "server.h"
#include "mode.h"
WebServer server;
constexpr auto MAX_SECONDS = 10 * 366 * 24 * 60 * 60L;
void serverIndex() {
const auto html = R"(
<!DOCTYPE html>
<html>
<head>
<title>MatrixDisplay2022</title>
<style>body{font-family: sans-serif;text-align:right;font-size:4.5vw;margin:0.5em}h2{text-align:left;}form{all:unset;}input{font-size:inherit;}input[type=number]{text-align:right;width:70%%}th{text-align:right;}table{width:100%%}</style>
</head>
<body>
<h2>Einstellungen</h2>
<form method=POST action=/timer/config>
<table>
<tr><th>Countdown:</th><td><input type=number name=countdown value=%d min=0 max=%d> Sek.</td></tr>
<tr><th>Gelb:</th><td><input type=number name=yellow value=%d min=0 max=%d> Sek.</td></tr>
<tr><th>Orange:</th><td><input type=number name=orange value=%d min=0 max=%d> Sek.</td></tr>
<tr><th>Rot:</th><td><input type=number name=red value=%d min=0 max=%d> Sek.</td></tr>
</table>
<input type=submit value="Speichern">
</form>
<h2>Steuerung</h2>
<form method=GET action=/timer/pause><input type=submit value="Pause/Weiter"></form>
<form method=GET action=/timer/reset><input type=submit value="Reset"></form>
</body>
</html>
)";
char buffer[2000];
snprintf(
buffer, sizeof buffer, html,
getTimerConfigCountdown() / 1000, MAX_SECONDS,
getTimerConfigYellow() / 1000, MAX_SECONDS,
getTimerConfigOrange() / 1000, MAX_SECONDS,
getTimerConfigRed() / 1000, MAX_SECONDS
);
server.send(200, "text/html", buffer);
}
void redirect() {
server.sendHeader("Location", "/");
server.send(302, "text/plain", "redirecting...");
}
void serverTimerConfig2(const char *name) {
if (server.hasArg(name)) {
const auto seconds = max(1L, min(server.arg(name).toInt(), MAX_SECONDS));
setTimerConfig(name, seconds * 1000);
} else {
Serial.printf("Missing parameter: %s\n", name);
}
}
void serverTimerConfig() {
serverTimerConfig2("countdown");
serverTimerConfig2("yellow");
serverTimerConfig2("orange");
serverTimerConfig2("red");
redirect();
}
void serverTimerPause() {
mode.buttonOK();
redirect();
}
void serverTimerReset() {
mode.buttonESC();
redirect();
}
void serverSetup() {
server.on("", serverIndex);
server.on("/", serverIndex);
server.on("/timer/pause", serverTimerPause);
server.on("/timer/pause/", serverTimerPause);
server.on("/timer/reset", serverTimerReset);
server.on("/timer/reset/", serverTimerReset);
server.on("/timer/config", serverTimerConfig);
server.on("/timer/config/", serverTimerConfig);
}

10
src/server.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef SERVER_H
#define SERVER_H
#include <WebServer.h>
extern WebServer server;
void serverSetup();
#endif

50
src/wifi.cpp Normal file
View File

@ -0,0 +1,50 @@
#include "wifi.h"
#include <WiFi.h>
#include "server.h"
#define WIFI_SSID "HappyNet"
#define WIFI_PASS "1Grausame!Sackratte7"
auto wifiConnected = false;
auto wifiLastTry = 0UL;
void wifiConnect() {
server.stop();
WiFi.disconnect();
WiFi.enableAP(false);
WiFi.setAutoConnect(false);
WiFi.setAutoReconnect(false);
yield();
wifiLastTry = max(1UL, millis());
WiFi.begin(WIFI_SSID, WIFI_PASS);
Serial.printf("WiFi connecting: %s\n", WIFI_SSID);
yield();
}
void wifiLoop() {
const auto connected = WiFi.localIP() != 0;
if (wifiConnected != connected) {
if (connected) {
Serial.printf("WiFi connected: ip=%s, ssid=%s\n", WiFi.localIP().toString().c_str(), WIFI_SSID);
configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", WiFi.gatewayIP().toString().c_str());
server.begin();
} else {
Serial.println("WiFi DISCONNECTED");
wifiConnect();
}
wifiConnected = connected;
} else if (connected) {
server.handleClient();
} else if (wifiLastTry == 0 || millis() - wifiLastTry > 10000) {
if (wifiLastTry != 0) {
Serial.println("WiFi TIMEOUT");
}
wifiConnect();
}
}

6
src/wifi.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef WIFI_H
#define WIFI_H
void wifiLoop();
#endif