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 + + + + +
+ Player 0
+ Player 1
+
+ +
+ NONE
+ BORDER
+ CLOCK
+ GAME_OF_LIFE_BLACK_WHITE
+ GAME_OF_LIFE_GRAYSCALE
+ GAME_OF_LIFE_COLOR_FADE
+ GAME_OF_LIFE_RANDOM_COLOR
+ PONG
+ SPACE_INVADERS
+ COUNT_DOWN
+ COUNT_DOWN_BARS
+ COUNT_DOWN_SLEEP
+ STARFIELD
+ MATRIX
+ POWER
+ ENERGY
+ TIMER
+
+ +
+
+ Dunkler/ + Heller + ? +
+
+ Langsamer/ + Schneller + ? +
+
+ +
+
+ + + +
+
+ + + +
+
+ +
+
+ +
+ + + + + +
+ +
+ +
+ + + + \ 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"()"); - response->print(R"()"); - response->print(R"()"); - response->print(R"()"); - response->print(R"()"); - response->print(R"()"); - response->print(R"()"); - response->print(R"()"); - response->print(R"()"); - 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"(
 
)"); - snprintf(buffer, sizeof buffer, R"(
)", index); - response->print(buffer); - response->print(R"(
)"); - snprintf(buffer, sizeof buffer, R"(
)", index); - response->print(buffer); - response->print(R"(
)"); - snprintf(buffer, sizeof buffer, R"(
)", index); - response->print(buffer); - response->print(R"(
 )"); - snprintf(buffer, sizeof buffer, R"(
)", index); - response->print(buffer); - 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(); }