diff --git a/.gitignore b/.gitignore
index d9cd68c..d6bb399 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
-.pio/
-img/
+/.pio/
+/.idea/
+/data/http
+cmake-build-*/
CMakeListsPrivate.txt
-cmake-build-*/
-/.idea
\ No newline at end of file
diff --git a/data-pretty/Canvas.js b/data-pretty/Canvas.js
deleted file mode 100644
index 74d9611..0000000
--- a/data-pretty/Canvas.js
+++ /dev/null
@@ -1,41 +0,0 @@
-class Canvas {
-
- width;
-
- height;
-
- canvas;
-
- ctx;
-
- constructor(canvasElementId, width, height, mouseMove) {
- this.width = width;
- this.height = height;
-
- this.canvas = document.getElementById(canvasElementId);
- this.canvas.width = width;
- this.canvas.height = height;
- this.canvas.addEventListener('contextmenu', event => event.preventDefault());
- this.canvas.onmousemove = mouseMove;
- this.canvas.onmousedown = mouseMove;
- this.canvas.onmouseup = mouseMove;
-
- this.ctx = this.canvas.getContext("2d");
- }
-
- getPixelColor(event) {
- const colorArray = this.ctx.getImageData(event.offsetX, event.offsetY, 1, 1).data;
- const colorInt = (colorArray[0] << 16) | (colorArray[1] << 8) | colorArray[2];
- const colorHex = colorInt.toString(16);
- return "#" + colorHex.padStart(6, "0");
- }
-
- fillRect(x, y, color) {
- this.ctx.beginPath();
- this.ctx.fillStyle = color;
- this.ctx.rect(x * this.size, y * this.size, this.size, this.size);
- this.ctx.fill();
- }
-
-
-}
\ No newline at end of file
diff --git a/data-pretty/Drawing.js b/data-pretty/Drawing.js
deleted file mode 100644
index 7d3f491..0000000
--- a/data-pretty/Drawing.js
+++ /dev/null
@@ -1,87 +0,0 @@
-class Drawing extends Canvas {
-
- mouseRasterX = -1;
-
- mouseRasterY = -1;
-
- color0 = "#00F";
-
- color1 = "#FFF";
-
- constructor(rasterCountWidth, rasterCountHeight, size, canvasElementId) {
- super(canvasElementId, rasterCountWidth * size, rasterCountHeight * size, (event) => this.mouseEvent(event));
- this.size = size;
- this.matrix = new Array(rasterCountHeight).fill(0).map(() => new Array(rasterCountWidth).fill(undefined));
- this.draw();
- }
-
- mouseEvent(event) {
- this.updateCursor(event);
- this.mouseDraw(event);
- this.draw();
- }
-
- mouseDraw(event) {
- let color;
- if (Boolean(event.buttons & 1)) {
- color = this.color0;
- } else if (Boolean(event.buttons & 2)) {
- color = undefined;
- } else {
- return;
- }
- this.matrix[this.mouseRasterY][this.mouseRasterX] = color;
- }
-
- updateCursor(event) {
- this.mouseRasterX = Math.floor(event.offsetX / this.size);
- this.mouseRasterY = Math.floor(event.offsetY / this.size);
- }
-
- draw() {
- this.ctx.fillStyle = "#fff";
- this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.drawMatrix();
- this.drawRaster(0, 0, this.canvas.width, this.canvas.height, this.size, this.size);
- }
-
- drawMatrix() {
- for (let y = 0; y < this.matrix.length; y++) {
- const row = this.matrix[y];
- for (let x = 0; x < row.length; x++) {
- const color = row[x];
- if (color === undefined) {
- continue;
- }
- this.ctx.beginPath();
- this.ctx.fillStyle = color;
- this.ctx.rect(x * this.size, y * this.size, this.size, this.size);
- this.ctx.fill();
- }
- }
- }
-
- drawRaster(xBgn, yBgn, w, h, rx, ry) {
- const xEnd = xBgn + w;
- const yEnd = yBgn + h;
-
- this.ctx.beginPath();
- for (let x = xBgn; x <= xEnd; x += rx) {
- this.ctx.moveTo(x, yBgn);
- this.ctx.lineTo(x, yEnd);
- }
- for (let y = yBgn; y <= yEnd; y += ry) {
- this.ctx.moveTo(xBgn, y);
- this.ctx.lineTo(xEnd, y);
- }
- this.ctx.strokeStyle = "#aaa";
- this.ctx.stroke();
-
- if (this.mouseRasterX >= 0) {
- this.ctx.beginPath();
- this.ctx.strokeStyle = "blue";
- this.ctx.rect(this.mouseRasterX * this.size, this.mouseRasterY * this.size, this.size, this.size);
- this.ctx.stroke();
- }
- }
-}
diff --git a/data-pretty/Picker.js b/data-pretty/Picker.js
deleted file mode 100644
index 496d4aa..0000000
--- a/data-pretty/Picker.js
+++ /dev/null
@@ -1,48 +0,0 @@
-const STEPS = 8;
-
-class Picker extends Canvas {
-
- setColor0;
-
- setColor1;
-
- constructor(size, canvasElementId, setColor0, setColor1) {
- super(canvasElementId, (STEPS + 1) * size, 7 * size, (event) => this.mouseEvent(event));
- this.setColor0 = setColor0;
- this.setColor1 = setColor1;
- this.size = size;
- this.draw();
- }
-
- mouseEvent(event) {
- const color = this.getPixelColor(event);
- if (Boolean(event.buttons & 1)) {
- this.setColor0(color);
- } else if (Boolean(event.buttons & 2)) {
- this.setColor1(color);
- }
- }
-
- draw() {
- this.ctx.fillStyle = "#fff";
- this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
- let y = 0;
- this.drawMatrix(true, false, false, y++);
- this.drawMatrix(true, true, false, y++);
- this.drawMatrix(false, true, false, y++);
- this.drawMatrix(false, true, true, y++);
- this.drawMatrix(false, false, true, y++);
- this.drawMatrix(true, false, true, y++);
- this.drawMatrix(true, true, true, y++);
- }
-
- drawMatrix(dr, dg, db, y) {
- for (let x = 0; x <= STEPS; x++) {
- const r = x * (dr ? 256 / STEPS : 0) - 1;
- const g = x * (dg ? 256 / STEPS : 0) - 1;
- const b = x * (db ? 256 / STEPS : 0) - 1;
- this.fillRect(x, y, `rgb(${r},${g},${b})`);
- }
- }
-
-}
diff --git a/data-pretty/index.css b/data-pretty/index.css
deleted file mode 100644
index 699a279..0000000
--- a/data-pretty/index.css
+++ /dev/null
@@ -1,3 +0,0 @@
-body {
- margin: 0;
-}
diff --git a/data-pretty/index.html b/data-pretty/index.html
deleted file mode 100644
index d961844..0000000
--- a/data-pretty/index.html
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
- RGBMatrixDisplay
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/data-pretty/index.js b/data-pretty/index.js
deleted file mode 100644
index 3ba4d2f..0000000
--- a/data-pretty/index.js
+++ /dev/null
@@ -1,9 +0,0 @@
-window.onload = function () {
- const drawing = new Drawing(8, 8, 60, "canvas");
- new Picker(
- 60,
- "picker",
- (color => drawing.color0 = color),
- (color => drawing.color1 = color),
- );
-};
diff --git a/http/favicon.svg b/http/favicon.svg
new file mode 100644
index 0000000..8532115
--- /dev/null
+++ b/http/favicon.svg
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/http/index.htm b/http/index.htm
new file mode 100644
index 0000000..acdfc99
--- /dev/null
+++ b/http/index.htm
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+ RGBMatrixDisplay
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/http/main.css b/http/main.css
new file mode 100644
index 0000000..1738923
--- /dev/null
+++ b/http/main.css
@@ -0,0 +1,47 @@
+body {
+ font-family: sans-serif;
+ font-size: 7vw;
+ margin: 0;
+}
+
+button.player {
+ width: 33vmin;
+ height: 33vmin;
+ font-size: 9vw;
+}
+
+a {
+ text-decoration: none;
+}
+
+div {
+ box-sizing: border-box;
+}
+
+input, select, textarea, button {
+ font-family: inherit;
+ font-size: inherit;
+}
+
+table {
+ border-collapse: collapse;
+}
+
+td {
+ text-align: center;
+}
+
+.paragraph {
+ margin-top: 1em;
+ margin-bottom: 1em;
+}
+
+.modeActive {
+ background-color: lightgreen;
+}
+
+@media (min-width: 1000px) {
+ body {
+ font-size: 16px;
+ }
+}
\ No newline at end of file
diff --git a/http/main.js b/http/main.js
new file mode 100644
index 0000000..f6d123a
--- /dev/null
+++ b/http/main.js
@@ -0,0 +1,109 @@
+const index = parseInt(new URLSearchParams(window.location.search).get('index')) || 0;
+
+const dlYe = document.getElementById("dlYe");
+const dlMo = document.getElementById("dlMo");
+const dlDa = document.getElementById("dlDa");
+const dlHo = document.getElementById("dlHo");
+const dlMi = document.getElementById("dlMi");
+const dlSe = document.getElementById("dlSe");
+
+const tmDa = document.getElementById("tmDa");
+const tmHo = document.getElementById("tmHo");
+const tmMi = document.getElementById("tmMi");
+const tmSe = document.getElementById("tmSe");
+
+const brightness = document.getElementById("brightness")
+
+const speed = document.getElementById("speed")
+
+let interval = undefined;
+
+function dl() {
+ const y = parseInt(dlYe.value);
+ const M = parseInt(dlMo.value) - 1;
+ const d = parseInt(dlDa.value);
+ const h = parseInt(dlHo.value) || 0;
+ const m = parseInt(dlMi.value) || 0;
+ const s = parseInt(dlSe.value) || 0;
+ const deadlineEpoch = (new Date(y, M, d, h, m, s).getTime() / 1000).toFixed(0);
+ set("deadlineEpoch", deadlineEpoch);
+}
+
+function tm() {
+ const d = parseInt(tmDa.value) || 0;
+ const h = parseInt(tmHo.value) || 0;
+ const m = parseInt(tmMi.value) || 0;
+ const s = parseInt(tmSe.value) || 0;
+ const timerMillis = (((d * 24 + h) * 60 + m) * 60 + s) * 1000;
+ set("timerMillis", timerMillis);
+}
+
+const sm = (v) => set('mode', v);
+
+const br = (v) => set('brightness', v);
+
+const sp = (v) => set('speed', v);
+
+const set = (n, v) => gf(`/set?n=${n}&v=${v}`)
+
+const fetch = () => {
+ // if (interval !== undefined) {
+ // clearInterval(interval);
+ // interval = undefined;
+ // }
+ // interval = setInterval(fetch, 2000);
+ get("/state", showState());
+}
+
+const gf = (path) => {
+ get(path, fetch);
+}
+
+function get(path, cb = null) {
+ const r = new XMLHttpRequest();
+ r.onreadystatechange = () => !!cb && r.readyState === 4 && r.status === 200 && cb(r);
+ r.open("GET", path, true);
+ r.send();
+}
+
+function showState() {
+ return function (r) {
+ const json = JSON.parse(r.responseText);
+
+ // noinspection JSUnresolvedReference
+ const d = new Date(parseInt(json.config.deadlineEpoch || 0) * 1000);
+ dlYe.value = "" + d.getFullYear();
+ dlMo.value = "" + d.getMonth() + 1;
+ dlDa.value = "" + d.getDate();
+ dlHo.value = "" + d.getHours();
+ dlMi.value = "" + d.getMinutes();
+ dlSe.value = "" + d.getSeconds();
+
+ // noinspection JSUnresolvedReference
+ const s = parseInt(json.config.timerMillis || 0) / 1000;
+ const m = Math.floor(s / 60);
+ const h = Math.floor(m / 60);
+ tmDa.value = "" + Math.floor(h / 24);
+ tmHo.value = "" + h % 24;
+ tmMi.value = "" + m % 60;
+ tmSe.value = "" + s % 60;
+
+ const id = parseInt(json.config.mode);
+ for (const mode of document.getElementsByClassName("mode")) {
+ if (mode.id === "mode" + id) {
+ if (!mode.classList.contains("modeActive")) {
+ mode.classList.add("modeActive");
+ }
+ } else {
+ mode.classList.remove("modeActive");
+ }
+ }
+
+ // noinspection JSUnresolvedReference
+ brightness.innerText = (parseInt(json.config.brightness) / 2.56).toFixed(0) + "%";
+
+ speed.innerText = parseInt(json.config.speed).toFixed(5) + "x";
+ };
+}
+
+fetch();
\ No newline at end of file
diff --git a/http/player.htm b/http/player.htm
new file mode 100644
index 0000000..65ebad4
--- /dev/null
+++ b/http/player.htm
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+ RGBMatrixDisplay
+
+
+
+
+
+ | ← |
+
+
+
+ |
+ |
+
+
+
+
+
+ |
+
+
+
+ |
+
+
+
+ |
+
+
+ | |
+
+
+
+ |
+ |
+
+
+
+
\ No newline at end of file
diff --git a/platformio.ini b/platformio.ini
index 87bab0f..6ad839c 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -3,6 +3,7 @@ platform = espressif32
board = esp32dev
framework = arduino
board_build.filesystem = littlefs
+extra_scripts = pre:./scripts/prepare_http.py
lib_deps = ../Patrix
build_flags = -DWIFI_SSID=\"HappyNet\" -DWIFI_PKEY=\"1Grausame!Sackratte7\" -DWIFI_HOST=\"RGBMatrixDisplay\"
monitor_port = /dev/ttyUSB0
@@ -14,6 +15,7 @@ platform = ${basic.platform}
board = ${basic.board}
framework = ${basic.framework}
board_build.filesystem = ${basic.board_build.filesystem}
+extra_scripts = ${basic.extra_scripts}
lib_deps = ${basic.lib_deps}
build_flags = ${basic.build_flags}
monitor_port = ${basic.monitor_port}
@@ -27,6 +29,7 @@ platform = ${basic.platform}
board = ${basic.board}
framework = ${basic.framework}
board_build.filesystem = ${basic.board_build.filesystem}
+extra_scripts = ${basic.extra_scripts}
lib_deps = ${basic.lib_deps}
build_flags = ${basic.build_flags}
monitor_port = ${basic.monitor_port}
diff --git a/scripts/prepare_http.py b/scripts/prepare_http.py
new file mode 100644
index 0000000..3c9304e
--- /dev/null
+++ b/scripts/prepare_http.py
@@ -0,0 +1,3 @@
+import subprocess
+
+subprocess.run(["./scripts/prepare_http.sh"], check=True)
\ No newline at end of file
diff --git a/scripts/prepare_http.sh b/scripts/prepare_http.sh
new file mode 100755
index 0000000..1ec83b1
--- /dev/null
+++ b/scripts/prepare_http.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+SOURCE_DIR="./http"
+DESTINATION_DIR="./data/http"
+
+echo
+echo "+----------------------+"
+echo "| minifying http files |"
+echo "+----------------------+"
+
+cd "$(dirname "$0")/../" || exit 1
+
+if [ -e "$DESTINATION_DIR" ]; then
+ rm -r "$DESTINATION_DIR"
+fi
+
+mkdir -p "$DESTINATION_DIR"
+
+find "$SOURCE_DIR" -type f | while read -r src; do
+ dst="$DESTINATION_DIR/$(basename "$src").gz"
+ echo "source: $(du -sb --apparent-size "$src")"
+ minify "$src" | gzip > "$dst"
+ echo "destination: $(du -sb --apparent-size "$dst")"
+ echo
+done
+
+du -sh --apparent-size "$SOURCE_DIR"
+du -sh --apparent-size "$DESTINATION_DIR"
+
+echo "+--------------------+"
+echo "| minifying COMPLETE |"
+echo "+--------------------+"
+echo
\ No newline at end of file
diff --git a/src/Display.cpp b/src/Display.cpp
new file mode 100644
index 0000000..a4e4228
--- /dev/null
+++ b/src/Display.cpp
@@ -0,0 +1,20 @@
+#include "Display.h"
+
+#include
+
+DisplayMatrix<32, 8> display(13);
+
+void displaySetup() {
+ display.setup(config.get("brightness", 10));
+}
+
+void displayLoop() {
+ display.loop();
+}
+
+uint8_t setBrightness(const int brightness) {
+ uint8_t result = display.setBrightness(display.getBrightness() + brightness);
+ config.set("brightness", result);
+ debug("brightness = %d", result);
+ return result;
+}
diff --git a/src/Display.h b/src/Display.h
new file mode 100644
index 0000000..8157917
--- /dev/null
+++ b/src/Display.h
@@ -0,0 +1,14 @@
+#ifndef DISPLAY_H
+#define DISPLAY_H
+
+#include
+
+extern DisplayMatrix<32, 8> display;
+
+void displaySetup();
+
+void displayLoop();
+
+uint8_t setBrightness(int brightness);
+
+#endif
diff --git a/src/Node.h b/src/Node.h
index 9285266..95109e9 100644
--- a/src/Node.h
+++ b/src/Node.h
@@ -1,258 +1,11 @@
#ifndef NODE_H
#define NODE_H
-#include
#include
+#include
+#include
#include
-#include
-
-DisplayMatrix<32, 8> display(13);
-
-static const auto style = R"(
-
-)";
-
-static const auto script = R"(
-
-)";
-
-inline void httpMode(AsyncWebServerRequest *request) {
- if (!request->hasParam("mode")) {
- request->send(400);
- }
- setMode(static_cast(request->getParam("mode")->value().toInt()));
- request->send(200);
-}
-
-inline void httpIndex(AsyncWebServerRequest *request) {
- auto *response = request->beginResponseStream("text/html");
- response->print(style);
- response->print(script);
-
- response->print(R"()");
- response->print(R"(Player 0
)");
- response->print(R"(Player 1
)");
- response->print(R"(
)");
-
- response->print(R"()");
- response->print(R"(NONE
)");
- response->print(R"(BORDER
)");
- response->print(R"(CLOCK
)");
- response->print(R"(GAME_OF_LIFE_BLACK_WHITE
)");
- response->print(R"(GAME_OF_LIFE_GRAYSCALE
)");
- response->print(R"(GAME_OF_LIFE_COLOR_FADE
)");
- response->print(R"(GAME_OF_LIFE_RANDOM_COLOR
)");
- response->print(R"(PONG
)");
- response->print(R"(SPACE_INVADERS
)");
- response->print(R"(COUNT_DOWN
)");
- response->print(R"(COUNT_DOWN_BARS
)");
- response->print(R"(COUNT_DOWN_SLEEP
)");
- response->print(R"(STARFIELD
)");
- response->print(R"(MATRIX
)");
- response->print(R"(POWER
)");
- response->print(R"(ENERGY
)");
- response->print(R"(TIMER
)");
- response->print(R"(
)");
-
- response->print(R"()");
- response->print(R"(Helligkeit: + / -
)");
- response->print(R"(Geschwindigkeit: + / -
)");
- 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"()");
- response->print(R"()");
- response->print(R"(
)");
-
- request->send(response);
-}
-
-inline void web_player(AsyncWebServerRequest *request) {
- char buffer[128];
-
- if (!request->hasParam("index")) {
- request->send(400, "text/plain", "Missing 'index'");
- return;
- }
- const auto value = request->getParam("index")->value().toDouble();
- const auto index = static_cast(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) {
- // ReSharper disable once CppJoinDeclarationAndAssignment
- double value;
-
- if (!request->hasParam("index")) {
- request->send(400, "text/plain", "Missing 'index'");
- return;
- }
- value = request->getParam("index")->value().toDouble();
- const auto index = static_cast(value);
-
- if (!request->hasParam("x")) {
- request->send(400, "text/plain", "Missing 'x'");
- return;
- }
- value = request->getParam("x")->value().toDouble();
- const auto x = static_cast(value);
-
- if (!request->hasParam("y")) {
- request->send(400, "text/plain", "Missing 'y'");
- return;
- }
- value = request->getParam("y")->value().toDouble();
- const auto y = static_cast(value);
-
- modeMove(index, x, y);
-
- request->send(200, "application/json", "true");
-}
-
-inline void web_player_fire(AsyncWebServerRequest *request) {
- // ReSharper disable once CppJoinDeclarationAndAssignment
- double value;
-
- if (!request->hasParam("index")) {
- request->send(400, "text/plain", "Missing 'index'");
- return;
- }
- value = request->getParam("index")->value().toDouble();
- const auto index = static_cast(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;
- }
- auto value = request->getParam("mode")->value().toDouble();
- if (isnan(value)) {
- request->send(400, "text/plain", "'mode' not a number");
- return;
- }
- setMode(static_cast(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 {
@@ -263,28 +16,14 @@ public:
}
void setup() override {
+ displaySetup();
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);
- display.clear();
+ patrixHttpSetup();
}
void loop() override {
modeLoop(display);
- display.loop();
+ displayLoop();
}
void mqttMessage(char *topic, char *message) override {
diff --git a/src/http.cpp b/src/http.cpp
new file mode 100644
index 0000000..729cc3e
--- /dev/null
+++ b/src/http.cpp
@@ -0,0 +1,86 @@
+#include "http.h"
+
+#include
+
+#include
+#include
+
+// ReSharper disable CppLocalVariableMayBeConst
+
+void httpState(AsyncWebServerRequest *request) {
+ auto doc = JsonDocument();
+ auto rootJson = doc.to();
+ rootJson["config"] = config.json;
+ rootJson["configAutoWriteInMillis"] = config.getAutoWriteInMillis();
+ rootJson["configAutoWriteAtEpoch"] = config.getAutoWriteAtEpoch();
+ auto stream = request->beginResponseStream("application/json");
+ serializeJson(doc, *stream);
+ request->send(stream);
+}
+
+// ReSharper restore CppLocalVariableMayBeConst
+
+void httpPlayerMove(AsyncWebServerRequest *request) {
+ if (!request->hasParam("index") || !request->hasParam("x") || !request->hasParam("y")) {
+ request->send(400, "text/plain", "required parameters: index, x, y");
+ return;
+ }
+ const auto index = request->getParam("index")->value().toInt();
+ const auto x = request->getParam("x")->value().toInt();
+ const auto y = request->getParam("y")->value().toInt();
+ modeMove(index, x, y);
+ request->send(200);
+}
+
+void httpPlayerFire(AsyncWebServerRequest *request) {
+ if (!request->hasParam("index")) {
+ request->send(400, "text/plain", "required parameters: index");
+ return;
+ }
+ const auto index = request->getParam("index")->value().toInt();
+ modeFire(index);
+ request->send(200);
+}
+
+void httpSet(AsyncWebServerRequest *request) {
+ if (!request->hasParam("n") || !request->hasParam("v")) {
+ request->send(400, "text/plain", "required parameters: n, v");
+ return;
+ }
+
+ const auto name = request->getParam("n")->value();
+ const auto value = request->getParam("v")->value();
+ debug(R"(http: set("%s", "%s"))", name.c_str(), value.c_str());
+ if (name.equals("mode")) {
+ const auto mode = static_cast(value.toInt());
+ debug(" => mode = %d", mode);
+ setMode(mode);
+ } else if (name.equals("timerMillis") || name.equals("deadlineEpoch")) {
+ debug(" => config");
+ config.set(name, value.toInt());
+ modeLoadConfig();
+ } else if (name.equals("brightness")) {
+ const auto brightness = value.toInt();
+ setBrightness(brightness);
+ } else if (name.equals("speed")) {
+ const auto speed = value.toDouble();
+ setModeSpeed(getModeSpeed() * speed);
+ }
+
+ request->send(200);
+}
+
+void httpConfigSave(AsyncWebServerRequest *request) {
+ config.write();
+ request->send(200);
+}
+
+void patrixHttpSetup() {
+ server.on("/state", httpState);
+
+ server.on("/set", httpSet);
+ server.on("/config/save", httpConfigSave);
+
+ server.on("/player/move", httpPlayerMove);
+ server.on("/player/fire", httpPlayerFire);
+}
diff --git a/src/http.h b/src/http.h
new file mode 100644
index 0000000..1d69497
--- /dev/null
+++ b/src/http.h
@@ -0,0 +1,6 @@
+#ifndef HTTP_H
+#define HTTP_H
+
+void patrixHttpSetup();
+
+#endif
diff --git a/src/mode.cpp b/src/mode.cpp
index 51a1535..8bfb255 100644
--- a/src/mode.cpp
+++ b/src/mode.cpp
@@ -32,7 +32,7 @@ void modeStep();
void modeSetup() {
wanted = config.get("mode", GAME_OF_LIFE_RANDOM_COLOR);
- modeSpeed = config.get("mode_speed", 1.0);
+ modeSpeed = config.get("speed", 1.0);
}
void modeLoop(Display& display) {
@@ -61,11 +61,15 @@ void setMode(const ModeId newMode) {
wanted = newMode;
}
-double getSpeed() {
+ModeId getModeId() {
+ return current;
+}
+
+double getModeSpeed() {
return modeSpeed;
}
-void setSpeed(const double newSpeed) {
+void setModeSpeed(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 8020efb..19044bc 100644
--- a/src/mode.h
+++ b/src/mode.h
@@ -12,9 +12,11 @@ void modeLoop(Display& display);
void setMode(ModeId newMode);
-double getSpeed();
+ModeId getModeId();
-void setSpeed(double newSpeed);
+double getModeSpeed();
+
+void setModeSpeed(double newSpeed);
void modeMove(int index, int x, int y);
diff --git a/src/mode/CountDown/CountDown.h b/src/mode/CountDown/CountDown.h
index 938d9a6..1610ac8 100644
--- a/src/mode/CountDown/CountDown.h
+++ b/src/mode/CountDown/CountDown.h
@@ -10,7 +10,7 @@ class CountDown final : public Mode {
Firework fireworks[MAX_FIREWORKS];
- time_t targetEpochSeconds = 0;
+ time_t deadlineEpoch = 0;
tm target{};
@@ -44,7 +44,7 @@ public:
}
void loadConfig() override {
- targetEpochSeconds = config.get("targetEpochSeconds", 1767222000);
+ deadlineEpoch = config.get("deadlineEpoch", 1767222000);
}
protected:
@@ -63,11 +63,11 @@ protected:
return;
}
- localtime_r(&targetEpochSeconds, &target);
+ localtime_r(&deadlineEpoch, &target);
target.tm_year += 1900;
target.tm_mon += 1;
- const auto diffSeconds = difftime(targetEpochSeconds, nowEpochSeconds);
+ const auto diffSeconds = difftime(deadlineEpoch, nowEpochSeconds);
days = static_cast(floor(diffSeconds / (24 * 60 * 60)));
hours = static_cast(floor(diffSeconds / (60 * 60))) % 24;
minutes = static_cast(floor(diffSeconds / 60)) % 60;
diff --git a/src/mode/Timer/Timer.h b/src/mode/Timer/Timer.h
index dae5ee0..bf61c68 100644
--- a/src/mode/Timer/Timer.h
+++ b/src/mode/Timer/Timer.h
@@ -7,9 +7,9 @@
class Timer2 final : public Mode {
- long durationMillis = DEFAULT_DURATION_MILLIS;
+ long timerMillis = DEFAULT_DURATION_MILLIS;
- long restMillis = durationMillis;
+ long restMillis = timerMillis;
unsigned long lastMillis = 0;
@@ -32,15 +32,15 @@ public:
}
void loadConfig() override {
- const auto newDurationMillis = config.get("durationMillis", DEFAULT_DURATION_MILLIS);
+ const auto newTimerMillis = config.get("timerMillis", DEFAULT_DURATION_MILLIS);
if (restMillis > 0) {
- restMillis += newDurationMillis - durationMillis;
+ restMillis += newTimerMillis - timerMillis;
}
- durationMillis = newDurationMillis;
+ timerMillis = newTimerMillis;
}
void start() override {
- restMillis = durationMillis;
+ restMillis = timerMillis;
lastMillis = millis();
}