diff --git a/src/Node.h b/src/Node.h
index d96d365..6c4525b 100644
--- a/src/Node.h
+++ b/src/Node.h
@@ -37,6 +37,16 @@ function get(path){
r.open("GET", path, true);
r.send();
}
+function configDate(){
+ const year = document.getElementById('year').value;
+ const month = document.getElementById('month').value - 1;
+ const day = document.getElementById('day').value;
+ const hour = document.getElementById('hour').value;
+ const minute = document.getElementById('minute').value;
+ const second = document.getElementById('second').value;
+ const targetEpochSeconds = (new Date(year, month, day, hour, minute, second).getTime() / 1000).toFixed(0);
+ get('/config/date?targetEpochSeconds=' + targetEpochSeconds);
+}
)";
@@ -81,14 +91,16 @@ inline void httpIndex(AsyncWebServerRequest *request) {
response->print(R"(
)");
response->print(R"(Helligkeit: + / -
)");
response->print(R"(Geschwindigkeit: + / -
)");
- response->print(R"(FPS: EIN / AUS
)");
response->print(R"(
)");
response->print(R"()");
- response->print(R"()");
- response->print(R"()");
- response->print(R"()");
- response->print(R"()");
+ response->printf(R"()");
+ response->printf(R"()");
+ response->printf(R"()");
+ response->printf(R"()");
+ response->printf(R"()");
+ response->printf(R"()");
+ response->print(R"()");
response->print(R"(
)");
response->print(R"()");
@@ -98,6 +110,148 @@ inline void httpIndex(AsyncWebServerRequest *request) {
request->send(response);
}
+inline void web_player(AsyncWebServerRequest *request) {
+ char buffer[128];
+
+ if (!request->hasParam("index")) {
+ request->send(400, "text/plain", "Missing 'index'");
+ return;
+ }
+ double value = request->getParam("index")->value().toDouble();
+ int index = (int) value;
+
+ auto *response = request->beginResponseStream("text/html");
+
+ response->print(style);
+ response->print(script);
+ response->print(R"()");
+ response->print(R"(
)");
+ response->print(R"()");
+ response->print(R"(| ← | )");
+ response->print(R"()");
+ snprintf(buffer, sizeof buffer, R"( )", index);
+ response->print(buffer);
+ response->print(R"( | )");
+ response->print(R"( | )");
+ response->print(R"(
)");
+ response->print(R"()");
+ response->print(R"()");
+ snprintf(buffer, sizeof buffer, R"( )", index);
+ response->print(buffer);
+ response->print(R"( | )");
+ response->print(R"()");
+ snprintf(buffer, sizeof buffer, R"( )", index);
+ response->print(buffer);
+ response->print(R"( | )");
+ response->print(R"()");
+ snprintf(buffer, sizeof buffer, R"( )", index);
+ response->print(buffer);
+ response->print(R"( | )");
+ response->print(R"(
)");
+ response->print(R"()");
+ response->print(R"(| | )");
+ response->print(R"()");
+ snprintf(buffer, sizeof buffer, R"( )", index);
+ response->print(buffer);
+ response->print(R"( | )");
+ response->print(R"( | )");
+ response->print(R"(
)");
+ response->print(R"(
)");
+ request->send(response);
+}
+
+inline void web_player_move(AsyncWebServerRequest *request) {
+ double value;
+
+ if (!request->hasParam("index")) {
+ request->send(400, "text/plain", "Missing 'index'");
+ return;
+ }
+ value = request->getParam("index")->value().toDouble();
+ int index = (int) value;
+
+ if (!request->hasParam("x")) {
+ request->send(400, "text/plain", "Missing 'x'");
+ return;
+ }
+ value = request->getParam("x")->value().toDouble();
+ int x = (int) value;
+
+ if (!request->hasParam("y")) {
+ request->send(400, "text/plain", "Missing 'y'");
+ return;
+ }
+ value = request->getParam("y")->value().toDouble();
+ int y = (int) value;
+
+ modeMove(index, x, y);
+
+ request->send(200, "application/json", "true");
+}
+
+inline void web_player_fire(AsyncWebServerRequest *request) {
+ double value;
+
+ if (!request->hasParam("index")) {
+ request->send(400, "text/plain", "Missing 'index'");
+ return;
+ }
+ value = request->getParam("index")->value().toDouble();
+ int index = (int) value;
+
+ modeFire(index);
+
+ request->send(200, "application/json", "true");
+}
+
+inline void web_setMode(AsyncWebServerRequest *request) {
+ if (!request->hasParam("mode")) {
+ request->send(400, "text/plain", "Missing 'mode'");
+ return;
+ }
+ double value = request->getParam("mode")->value().toDouble();
+ if (isnan(value)) {
+ request->send(400, "text/plain", "'mode' not a number");
+ return;
+ }
+ setMode((ModeId) value);
+ request->send(200);
+}
+
+inline void web_brighter(AsyncWebServerRequest *request) {
+ const auto newBrightness = display.getBrightness() + 10;
+ display.setBrightness(newBrightness >= 255 ? 255 : newBrightness);
+ request->send(200);
+}
+
+inline void web_darker(AsyncWebServerRequest *request) {
+ const auto newBrightness = display.getBrightness() - 10;
+ display.setBrightness(newBrightness <= 0 ? 0 : newBrightness);
+ request->send(200);
+}
+
+inline void web_faster(AsyncWebServerRequest *request) {
+ setSpeed(getSpeed() * 1.1);
+ request->send(200);
+}
+
+inline void web_slower(AsyncWebServerRequest *request) {
+ setSpeed(getSpeed() / 1.1);
+ request->send(200);
+}
+
+inline void web_config_save(AsyncWebServerRequest *request) {
+ config.write();
+ request->send(200);
+}
+
+inline void web_config_date(AsyncWebServerRequest *request) {
+ const auto targetEpochSeconds = std::stoul(request->getParam("targetEpochSeconds")->value().c_str());
+ config.set("targetEpochSeconds", targetEpochSeconds);
+ modeLoadConfig();
+ request->send(200);
+}
+
class Node final : public PatrixNode {
public:
@@ -110,7 +264,16 @@ public:
modeSetup();
server.on("/", httpIndex);
+ server.on("/player", web_player);
+ server.on("/player/move", web_player_move);
+ server.on("/player/fire", web_player_fire);
server.on("/mode", httpMode);
+ server.on("/brighter", web_brighter);
+ server.on("/darker", web_darker);
+ server.on("/faster", web_faster);
+ server.on("/slower", web_slower);
+ server.on("/config/date", web_config_date);
+ server.on("/config/save", web_config_save);
display.setup();
display.setBrightness(10);
diff --git a/src/mode.cpp b/src/mode.cpp
index 85651dd..3b646cd 100644
--- a/src/mode.cpp
+++ b/src/mode.cpp
@@ -12,8 +12,6 @@
#include "mode/Energy/Energy.h"
#include "mode/Timer/Timer.h"
-#include
-
Config config("/main.json");
auto current = NONE;
@@ -51,11 +49,21 @@ void modeMqttMessage(const char *topic, const char *message) {
}
}
+void modeLoadConfig() {
+ if (mode != nullptr) {
+ mode->loadConfig();
+ }
+}
+
void setMode(const ModeId newMode) {
config.setIfNot("mode", newMode);
wanted = newMode;
}
+double getSpeed() {
+ return modeSpeed;
+}
+
void setSpeed(const double newSpeed) {
modeSpeed = min(max(0.01, newSpeed), 10000.0);
config.setIfNot("speed", modeSpeed);
diff --git a/src/mode.h b/src/mode.h
index 5f9292b..8020efb 100644
--- a/src/mode.h
+++ b/src/mode.h
@@ -1,14 +1,19 @@
#ifndef RGB_MATRIX_DISPLAY_MODE_H
#define RGB_MATRIX_DISPLAY_MODE_H
+#include
#include "mode/Mode.h"
+extern Config config;
+
void modeSetup();
void modeLoop(Display& display);
void setMode(ModeId newMode);
+double getSpeed();
+
void setSpeed(double newSpeed);
void modeMove(int index, int x, int y);
@@ -17,4 +22,6 @@ void modeFire(int index);
void modeMqttMessage(const char *topic, const char *message);
+void modeLoadConfig();
+
#endif
diff --git a/src/mode/Border/Border.h b/src/mode/Border/Border.h
index 8b32929..bd48687 100644
--- a/src/mode/Border/Border.h
+++ b/src/mode/Border/Border.h
@@ -18,7 +18,7 @@ public:
protected:
void draw(Display& display) override {
- display.foreground = White;
+ display.clear();
display.drawLineWH(0, 0, display.width, 0, 1);
display.drawLineWH(0, 0, 0, display.height, 1);
display.drawLineWH(display.width - 1, display.height - 1, -display.width, 0, 1);
diff --git a/src/mode/Clock/Clock.h b/src/mode/Clock/Clock.h
index 6738587..6039d82 100644
--- a/src/mode/Clock/Clock.h
+++ b/src/mode/Clock/Clock.h
@@ -27,11 +27,9 @@ protected:
void draw(Display& display) override {
display.clear();
- display.foreground = White;
- display.background = Transparent;
display.cursorX = 2;
display.cursorY = 1;
- display.printf("%2d:%02d:%02d", now.tm_hour, now.tm_min, now.tm_sec);
+ display.printf(true, "%2d:%02d:%02d", now.tm_hour, now.tm_min, now.tm_sec);
}
};
diff --git a/src/mode/CountDown/CountDown.h b/src/mode/CountDown/CountDown.h
index 41d80f3..726f8b7 100644
--- a/src/mode/CountDown/CountDown.h
+++ b/src/mode/CountDown/CountDown.h
@@ -12,6 +12,8 @@ class CountDown : public Mode {
Firework fireworks[MAX_FIREWORKS];
+ time_t targetEpochSeconds = 0;
+
tm target{};
uint16_t days = 0;
@@ -43,6 +45,10 @@ public:
return "CountDown (Numbers)";
}
+ void start() override {
+ targetEpochSeconds = config.get("targetEpochSeconds", 1767222000);
+ }
+
protected:
void step(const microseconds_t microseconds) override {
@@ -59,22 +65,18 @@ protected:
return;
}
- // GRRRRRRR...
- target.tm_year -= 1900;
- target.tm_mon -= 1;
- const auto dateEpochSeconds = mktime(&target);
+ localtime_r(&targetEpochSeconds, &target);
target.tm_year += 1900;
target.tm_mon += 1;
- // ---
- const auto diffSeconds = difftime(dateEpochSeconds, nowEpochSeconds);
+ const auto diffSeconds = difftime(targetEpochSeconds, nowEpochSeconds);
days = static_cast(floor(diffSeconds / (24 * 60 * 60)));
hours = static_cast(floor(diffSeconds / (60 * 60))) % 24;
minutes = static_cast(floor(diffSeconds / 60)) % 60;
seconds = static_cast(diffSeconds) % 60;
// Serial.printf("now=%4d.%02d.%02d (%ld), conf=%4d.%02d.%02d (%ld), diff=%f, %dd %2d:%02d:%02d\n",
// now.tm_year, now.tm_mon, now.tm_mday, nowEpochSeconds,
- // target.tm_year, target.tm_mon, target.tm_mday, dateEpochSeconds,
+ // target.tm_year, target.tm_mon, target.tm_mday, targetEpochSeconds,
// diffSeconds,
// days, hours, minutes, seconds);
setMode(COUNTDOWN);
@@ -146,7 +148,7 @@ private:
void drawSleepingCount(Display& display) const {
const auto sleepCount = days + 1;
- display.printf("%02d Tag%s", sleepCount, sleepCount == 1 ? "" : "e");
+ display.printf(true, "%3d TAG%s", sleepCount, sleepCount == 1 ? "" : "E");
}
void drawCountdownBars(Display& display) const {
@@ -177,10 +179,10 @@ private:
void drawCountdownNumbers(Display& display) const {
if (days > 0) {
- display.printf("%02d. %2d:%02d", days, hours, minutes);
+ display.printf(true, "%02d. %2d:%02d", days, hours, minutes);
drawSecondsBar(display, seconds);
} else {
- display.printf("%2d:%02d:%02d", hours, minutes, seconds);
+ display.printf(true, "%2d:%02d:%02d", hours, minutes, seconds);
drawSubSecondsBar(display);
}
}
@@ -209,11 +211,11 @@ private:
void drawYear(Display& display, const int year) const {
if (plus1DayForSleepingCount) {
- display.printf("Emil 5");
+ display.printf(true, "Emil 5");
display.cursorX = 32 - 8;
display.cursorY = 0;
} else {
- display.printf("%5d", year);
+ display.printf(true, "%5d", year);
}
}
diff --git a/src/mode/Energy/Energy.h b/src/mode/Energy/Energy.h
index 70c2e0f..a3f8ec4 100644
--- a/src/mode/Energy/Energy.h
+++ b/src/mode/Energy/Energy.h
@@ -76,19 +76,19 @@ protected:
display.cursorY = 1;
if (page == 0) {
display.foreground = Green;
- display.printf("%3.0f€", costSaved);
+ display.printf(true, "%3.0f€", costSaved);
display.foreground = White;
- display.printf(" %3.0f%%", amortisationPercent);
+ display.printf(true, " %3.0f%%", amortisationPercent);
} else if (page == 1) {
display.foreground = Blue;
- display.printf("%3.0f", photovoltaicEnergyKWh);
+ display.printf(true, "%3.0f", photovoltaicEnergyKWh);
display.foreground = Green;
- display.printf(" %3.0f", selfConsumedKWh);
+ display.printf(true, " %3.0f", selfConsumedKWh);
} else {
display.foreground = Yellow;
- display.printf("%4.0f", gridImportKWh);
+ display.printf(true, "%4.0f", gridImportKWh);
display.foreground = Magenta;
- display.printf(" %3.0f", gridExportKWh);
+ display.printf(true, " %3.0f", gridExportKWh);
}
}
diff --git a/src/mode/Mode.h b/src/mode/Mode.h
index 31b16d4..2148973 100644
--- a/src/mode/Mode.h
+++ b/src/mode/Mode.h
@@ -105,6 +105,8 @@ public:
virtual void mqttMessage(const String& topic, const String& message) {}
+ virtual void loadConfig() {}
+
void loop(const microseconds_t microseconds) {
handleRealtime();
handleTimers(microseconds);
diff --git a/src/mode/Pong/Pong.h b/src/mode/Pong/Pong.h
index 523a9ca..d702961 100644
--- a/src/mode/Pong/Pong.h
+++ b/src/mode/Pong/Pong.h
@@ -95,10 +95,10 @@ protected:
switch (status) {
case SCORE:
display.foreground = Green;
- display.printf("%d", player0.score);
+ display.printf(true, "%d", player0.score);
display.foreground = Red;
- display.printf("%5d", player1.score);
+ display.printf(true, "%5d", player1.score);
break;
case PLAY:
for (auto i = 0; i < player0.size; ++i) {
@@ -112,16 +112,16 @@ protected:
case OVER:
if (player0.score > player1.score) {
display.foreground = Green;
- display.printf("W", player0.score);
+ display.printf(true, "W", player0.score);
display.foreground = Red;
- display.printf(" L", player1.score);
+ display.printf(true, " L", player1.score);
} else if (player0.score < player1.score) {
display.foreground = Red;
- display.printf("L", player0.score);
+ display.printf(true, "L", player0.score);
display.foreground = Green;
- display.printf(" W", player1.score);
+ display.printf(true, " W", player1.score);
}
break;
}
diff --git a/src/mode/Power/Power.h b/src/mode/Power/Power.h
index 368a3a1..1425c77 100644
--- a/src/mode/Power/Power.h
+++ b/src/mode/Power/Power.h
@@ -59,10 +59,10 @@ protected:
display.clear();
display.foreground = photovoltaicPowerW >= 100 ? Green : photovoltaicPowerW >= 20 ? Yellow : Red;
- display.printf("%3.0f", photovoltaicPowerW);
+ display.printf(true, "%3.0f", photovoltaicPowerW);
display.foreground = gridPowerW >= 20 ? Yellow : gridPowerW >= -20 ? Green : Magenta;
- display.printf(" %4.0f", gridPowerW);
+ display.printf(true, " %4.0f", gridPowerW);
}
};
diff --git a/src/mode/Timer/Timer.h b/src/mode/Timer/Timer.h
index 60dbb3d..3d986c1 100644
--- a/src/mode/Timer/Timer.h
+++ b/src/mode/Timer/Timer.h
@@ -3,9 +3,15 @@
#include "mode/Mode.h"
+#define DEFAULT_DURATION_MILLIS (6 * 60 * 1000L)
+
class Timer2 final : public Mode {
- const unsigned long targetMillis = millis() + 6 * 60 * 1000;
+ long durationMillis = DEFAULT_DURATION_MILLIS;
+
+ long restMillis = durationMillis;
+
+ unsigned long lastMillis = 0;
uint16_t days = 0;
@@ -15,8 +21,6 @@ class Timer2 final : public Mode {
uint16_t seconds = 0;
- unsigned long diffSeconds = 0;
-
public:
explicit Timer2(Display& display) : Mode(display) {
@@ -27,30 +31,53 @@ public:
return "Timer";
}
+ void loadConfig() override {
+ const auto newDurationMillis = config.get("durationMillis", DEFAULT_DURATION_MILLIS);
+ if (restMillis > 0) {
+ restMillis += newDurationMillis - durationMillis;
+ }
+ durationMillis = newDurationMillis;
+ }
+
+ void start() override {
+ restMillis = durationMillis;
+ lastMillis = millis();
+ }
+
protected:
void step(microseconds_t microseconds) override {
const auto now = millis();
- diffSeconds = now >= targetMillis ? 0 : (targetMillis - now) / 1000;
- days = diffSeconds / (24 * 60 * 60);
- hours = diffSeconds / (60 * 60) % 24;
- minutes = diffSeconds / 60 % 60;
- seconds = diffSeconds % 60;
+ const auto deltaMillis = now - lastMillis;
+ lastMillis = now;
+
+ restMillis -= static_cast(deltaMillis);
+ if (restMillis < 0) {
+ restMillis = 0;
+ }
+
+ const auto restSeconds = restMillis / 1000;
+ days = restSeconds / (24 * 60 * 60);
+ hours = restSeconds / (60 * 60) % 24;
+ minutes = restSeconds / 60 % 60;
+ seconds = restSeconds % 60;
markDirty();
}
void draw(Display& display) override {
display.clear();
+ display.cursorX = 1;
+ display.cursorY = 1;
if (days > 1) {
- display.printf("%4d Tage", days);
+ display.printf(true, "%4d TAGE", days);
} else if (days > 0) {
- display.printf("%2d. %02d:%02d", days, hours, minutes);
+ display.printf(true, "%2d. %02d:%02d", days, hours, minutes);
} else if (hours > 0) {
- display.printf("%2d:%02d:%02d", hours, minutes, seconds);
+ display.printf(true, "%2d:%02d:%02d", hours, minutes, seconds);
} else if (minutes > 0) {
- display.printf("%2d:%02d", minutes, seconds);
+ display.printf(true, "%2d:%02d", minutes, seconds);
} else {
- display.printf("%2d", seconds);
+ display.printf(true, "%2d", seconds);
}
}