http static serve, minify, http set config, http state
This commit is contained in:
parent
978075d2de
commit
ad91b7ce86
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,5 +1,5 @@
|
|||||||
.pio/
|
/.pio/
|
||||||
img/
|
/.idea/
|
||||||
CMakeListsPrivate.txt
|
/data/http
|
||||||
cmake-build-*/
|
cmake-build-*/
|
||||||
/.idea
|
CMakeListsPrivate.txt
|
||||||
|
|||||||
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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})`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>RGBMatrixDisplay</title>
|
|
||||||
<link rel="stylesheet" href="./index.css">
|
|
||||||
<script src="Canvas.js"></script>
|
|
||||||
<script src="Drawing.js"></script>
|
|
||||||
<script src="Picker.js"></script>
|
|
||||||
<script src="./index.js"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<canvas id="canvas"></canvas><canvas id="picker"></canvas>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -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),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
7
http/favicon.svg
Normal file
7
http/favicon.svg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M11.082 10H11V6h4v2.187a7.868 7.868 0 0 0-3.72 1.623 3.642 3.642 0 0 0-.198.19zM20 1h-4v4h4zm0 10.423l-.055.002a13.903 13.903 0 0 0-2.06.287 3.01 3.01 0 0 0-.744.288l.009.076a2.686 2.686 0 0 0 .267.312L19.623 15H20zM7.866 16.647a4.766 4.766 0 0 1-.45-.647H6v4h4v-2.09a2.986 2.986 0 0 1-2.134-1.263zM1 5h4V1H1zm0 10h4v-4H1z" opacity=".25"/>
|
||||||
|
<path d="M16 8.052V6h4v2.266a18.371 18.371 0 0 0-3.106-.253c-.301 0-.6.015-.894.039zM1 20h4v-4H1zm15-1.335V20h.92q-.491-.724-.92-1.335zM10 1H6v4h4z" opacity=".5"/>
|
||||||
|
<path
|
||||||
|
d="M21 11.385v5.245L19.623 15H20v-3.577zm-8.142 4.106a2.952 2.952 0 0 1-.19.509h1.314c-.074-.083-.153-.176-.218-.243-.115-.116-.444-.448-.547-.455a2.09 2.09 0 0 0-.359.19zM17.592 21H0V0h21v8.921l-.293-.316a1 1 0 0 0-.502-.293l-.15-.035L20 8.266V6h-4v2.052c-.34.028-.673.076-1 .135V6h-4v4h.082a3.274 3.274 0 0 0-1.005 1.928l-.051.038c-.01 0-.016.004-.026.004V11H6v4h1.036a3.013 3.013 0 0 0 .38 1H6v4h4v-2.09c.029 0 .057.011.085.011a1.951 1.951 0 0 0 .915-.225V20h4v-2.724c.298.4.633.866 1 1.389V20h.92c.215.317.44.651.672 1zM16 5h4V1h-4zm-5 0h4V1h-4zM6 5h4V1H6zm0 5h4V6H6zm-1 6H1v4h4zm0-5H1v4h4zm0-5H1v4h4zm0-5H1v4h4zm17.575 20.99l-.822.753a1.308 1.308 0 0 1-.882.343 1.383 1.383 0 0 1-.167-.01 1.307 1.307 0 0 1-.932-.585 74.561 74.561 0 0 0-5.288-7.428c-.454-.458-.79-.761-1.27-.761a2.326 2.326 0 0 0-1.262.603 2.36 2.36 0 0 1-1.306 1.84c-.267.187-.997.493-2.009-.734-1.01-1.23-.57-1.888-.333-2.114a2.358 2.358 0 0 1 2.06-.926c.087-.073.175-.14.262-.204.394-.298.483-.395.453-.671-.075-.671.837-1.513.846-1.521a7.907 7.907 0 0 1 4.969-1.562 17.494 17.494 0 0 1 2.932.237l.148.036 1.02 1.098-1.087.042a14.724 14.724 0 0 0-2.246.312 4.385 4.385 0 0 0-1.635.797l.016.06a4.093 4.093 0 0 1 .13.765 2.322 2.322 0 0 0 .541.739l5.979 7.084a1.303 1.303 0 0 1-.117 1.808zm-7.844-8.063a5.606 5.606 0 0 1 .837-.63 1.8 1.8 0 0 1-.393-.865 3.211 3.211 0 0 0-.103-.591.872.872 0 0 1 .215-.996 5.678 5.678 0 0 1 1.374-.83 6.687 6.687 0 0 0-4.08 1.315 2.255 2.255 0 0 0-.508.706 1.607 1.607 0 0 1-.845 1.529c-.091.068-.185.138-.274.216a.781.781 0 0 1-.585.193c-.6-.05-.733.034-1.374.646a1.479 1.479 0 0 0 .414.756 1.587 1.587 0 0 0 .674.547c.711-.506.82-.62.886-1.219a.784.784 0 0 1 .302-.537 3.354 3.354 0 0 1 1.943-.865 2.27 2.27 0 0 1 1.517.625zm7.197 6.9l-5.705-6.762a5.388 5.388 0 0 0-.781.564 83.715 83.715 0 0 1 5.169 7.316.308.308 0 0 0 .467.06l.821-.752a.306.306 0 0 0 .029-.425z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.5 KiB |
82
http/index.htm
Normal file
82
http/index.htm
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<!--suppress HtmlFormInputWithoutLabel -->
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<link rel="stylesheet" href="main.css">
|
||||||
|
<title>RGBMatrixDisplay</title>
|
||||||
|
<link rel="icon" href="favicon.svg">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="paragraph">
|
||||||
|
<a href="player.htm?index=0">Player 0</a><br>
|
||||||
|
<a href="player.htm?index=1">Player 1</a><br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="paragraph">
|
||||||
|
<a class="mode" id="mode0" onclick="sm(0)">NONE</a><br>
|
||||||
|
<a class="mode" id="mode1" onclick="sm(1)">BORDER</a><br>
|
||||||
|
<a class="mode" id="mode2" onclick="sm(2)">CLOCK</a><br>
|
||||||
|
<a class="mode" id="mode3" onclick="sm(3)">GAME_OF_LIFE_BLACK_WHITE</a><br>
|
||||||
|
<a class="mode" id="mode4" onclick="sm(4)">GAME_OF_LIFE_GRAYSCALE</a><br>
|
||||||
|
<a class="mode" id="mode5" onclick="sm(5)">GAME_OF_LIFE_COLOR_FADE</a><br>
|
||||||
|
<a class="mode" id="mode6" onclick="sm(6)">GAME_OF_LIFE_RANDOM_COLOR</a><br>
|
||||||
|
<a class="mode" id="mode7" onclick="sm(7)">PONG</a><br>
|
||||||
|
<a class="mode" id="mode8" onclick="sm(8)">SPACE_INVADERS</a><br>
|
||||||
|
<a class="mode" id="mode9" onclick="sm(9)">COUNT_DOWN</a><br>
|
||||||
|
<a class="mode" id="mode10" onclick="sm(10)">COUNT_DOWN_BARS</a><br>
|
||||||
|
<a class="mode" id="mode11" onclick="sm(11)">COUNT_DOWN_SLEEP</a><br>
|
||||||
|
<a class="mode" id="mode12" onclick="sm(12)">STARFIELD</a><br>
|
||||||
|
<a class="mode" id="mode13" onclick="sm(13)">MATRIX</a><br>
|
||||||
|
<a class="mode" id="mode14" onclick="sm(14)">POWER</a><br>
|
||||||
|
<a class="mode" id="mode15" onclick="sm(15)">ENERGY</a><br>
|
||||||
|
<a class="mode" id="mode16" onclick="sm(16)">TIMER</a><br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="paragraph">
|
||||||
|
<div>
|
||||||
|
<a onclick="br(-10)">Dunkler</a>/
|
||||||
|
<a onclick="br(+10)">Heller</a>
|
||||||
|
<span id="brightness">?</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a onclick="sp(0.9)">Langsamer</a>/
|
||||||
|
<a onclick="sp(1.1)">Schneller</a>
|
||||||
|
<span id="speed">?</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="paragraph">
|
||||||
|
<div>
|
||||||
|
<input type="number" min="2025" max="3000" step="1" id="dlYe">
|
||||||
|
<input type="number" min="1" max="12" step="1" id="dlMo">
|
||||||
|
<input type="number" min="1" max="31" step="1" id="dlDa">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="number" min="0" max="23" step="1" id="dlHo">
|
||||||
|
<input type="number" min="0" max="59" step="1" id="dlMi">
|
||||||
|
<input type="number" min="0" max="59" step="1" id="dlSe">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button onclick="dl()">Datum setzen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="paragraph">
|
||||||
|
<input type="number" min="1" max="999" step="1" id="tmDa">
|
||||||
|
<input type="number" min="0" max="23" step="1" id="tmHo">
|
||||||
|
<input type="number" min="0" max="59" step="1" id="tmMi">
|
||||||
|
<input type="number" min="0" max="59" step="1" id="tmSe">
|
||||||
|
<button onclick="tm()">Datum setzen</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="paragraph">
|
||||||
|
<button onclick="gf('/config/save');">Speichern erzwingen</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
47
http/main.css
Normal file
47
http/main.css
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
109
http/main.js
Normal file
109
http/main.js
Normal file
@ -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();
|
||||||
46
http/player.htm
Normal file
46
http/player.htm
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<link rel="stylesheet" href="main.css">
|
||||||
|
<script src="main.js"></script>
|
||||||
|
<title>RGBMatrixDisplay</title>
|
||||||
|
<link rel="icon" href="favicon.svg">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td><a href='/'>←</a></td>
|
||||||
|
<td>
|
||||||
|
<button class="player" onclick="get(`/player/move?index=${index}&x=0&y=-1`);">↑</button>
|
||||||
|
<br>
|
||||||
|
</td>
|
||||||
|
<td> </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<button class="player" onclick="get(`/player/move?index=${index}&x=-1&y=0`);">←</button>
|
||||||
|
<br>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button class="player" onclick="get(`/player/fire?index=${index}`);">X</button>
|
||||||
|
<br>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button class="player" onclick="get(`/player/move?index=${index}&x=+1&y=0`);">→</button>
|
||||||
|
<br>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td> </td>
|
||||||
|
<td>
|
||||||
|
<button class="player" onclick="get(`/player/move?index=${index}&x=0&y=+1`);">↓</button>
|
||||||
|
<br>
|
||||||
|
</td>
|
||||||
|
<td> </td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -3,6 +3,7 @@ platform = espressif32
|
|||||||
board = esp32dev
|
board = esp32dev
|
||||||
framework = arduino
|
framework = arduino
|
||||||
board_build.filesystem = littlefs
|
board_build.filesystem = littlefs
|
||||||
|
extra_scripts = pre:./scripts/prepare_http.py
|
||||||
lib_deps = ../Patrix
|
lib_deps = ../Patrix
|
||||||
build_flags = -DWIFI_SSID=\"HappyNet\" -DWIFI_PKEY=\"1Grausame!Sackratte7\" -DWIFI_HOST=\"RGBMatrixDisplay\"
|
build_flags = -DWIFI_SSID=\"HappyNet\" -DWIFI_PKEY=\"1Grausame!Sackratte7\" -DWIFI_HOST=\"RGBMatrixDisplay\"
|
||||||
monitor_port = /dev/ttyUSB0
|
monitor_port = /dev/ttyUSB0
|
||||||
@ -14,6 +15,7 @@ platform = ${basic.platform}
|
|||||||
board = ${basic.board}
|
board = ${basic.board}
|
||||||
framework = ${basic.framework}
|
framework = ${basic.framework}
|
||||||
board_build.filesystem = ${basic.board_build.filesystem}
|
board_build.filesystem = ${basic.board_build.filesystem}
|
||||||
|
extra_scripts = ${basic.extra_scripts}
|
||||||
lib_deps = ${basic.lib_deps}
|
lib_deps = ${basic.lib_deps}
|
||||||
build_flags = ${basic.build_flags}
|
build_flags = ${basic.build_flags}
|
||||||
monitor_port = ${basic.monitor_port}
|
monitor_port = ${basic.monitor_port}
|
||||||
@ -27,6 +29,7 @@ platform = ${basic.platform}
|
|||||||
board = ${basic.board}
|
board = ${basic.board}
|
||||||
framework = ${basic.framework}
|
framework = ${basic.framework}
|
||||||
board_build.filesystem = ${basic.board_build.filesystem}
|
board_build.filesystem = ${basic.board_build.filesystem}
|
||||||
|
extra_scripts = ${basic.extra_scripts}
|
||||||
lib_deps = ${basic.lib_deps}
|
lib_deps = ${basic.lib_deps}
|
||||||
build_flags = ${basic.build_flags}
|
build_flags = ${basic.build_flags}
|
||||||
monitor_port = ${basic.monitor_port}
|
monitor_port = ${basic.monitor_port}
|
||||||
|
|||||||
3
scripts/prepare_http.py
Normal file
3
scripts/prepare_http.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import subprocess
|
||||||
|
|
||||||
|
subprocess.run(["./scripts/prepare_http.sh"], check=True)
|
||||||
33
scripts/prepare_http.sh
Executable file
33
scripts/prepare_http.sh
Executable file
@ -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
|
||||||
20
src/Display.cpp
Normal file
20
src/Display.cpp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include "Display.h"
|
||||||
|
|
||||||
|
#include <mode.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
14
src/Display.h
Normal file
14
src/Display.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef DISPLAY_H
|
||||||
|
#define DISPLAY_H
|
||||||
|
|
||||||
|
#include <patrix/display/DisplayMatrix.h>
|
||||||
|
|
||||||
|
extern DisplayMatrix<32, 8> display;
|
||||||
|
|
||||||
|
void displaySetup();
|
||||||
|
|
||||||
|
void displayLoop();
|
||||||
|
|
||||||
|
uint8_t setBrightness(int brightness);
|
||||||
|
|
||||||
|
#endif
|
||||||
271
src/Node.h
271
src/Node.h
@ -1,258 +1,11 @@
|
|||||||
#ifndef NODE_H
|
#ifndef NODE_H
|
||||||
#define NODE_H
|
#define NODE_H
|
||||||
|
|
||||||
#include <patrix/display/DisplayMatrix.h>
|
|
||||||
#include <patrix/node/PatrixNode.h>
|
#include <patrix/node/PatrixNode.h>
|
||||||
|
|
||||||
|
#include <Display.h>
|
||||||
|
#include <http.h>
|
||||||
#include <mode.h>
|
#include <mode.h>
|
||||||
#include <patrix/core/http.h>
|
|
||||||
|
|
||||||
DisplayMatrix<32, 8> display(13);
|
|
||||||
|
|
||||||
static const auto style = R"(
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 8vw;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
button.player{
|
|
||||||
width: 33vmin;
|
|
||||||
height: 33vmin;
|
|
||||||
font-size: 9vw;
|
|
||||||
}
|
|
||||||
table{
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
td{
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
)";
|
|
||||||
|
|
||||||
static const auto script = R"(
|
|
||||||
<script>
|
|
||||||
function get(path){
|
|
||||||
var r = new XMLHttpRequest();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
)";
|
|
||||||
|
|
||||||
inline void httpMode(AsyncWebServerRequest *request) {
|
|
||||||
if (!request->hasParam("mode")) {
|
|
||||||
request->send(400);
|
|
||||||
}
|
|
||||||
setMode(static_cast<ModeId>(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"(<p>)");
|
|
||||||
response->print(R"(<a href="/player?index=0">Player 0</a><br>)");
|
|
||||||
response->print(R"(<a href="/player?index=1">Player 1</a><br>)");
|
|
||||||
response->print(R"(</p>)");
|
|
||||||
|
|
||||||
response->print(R"(<p>)");
|
|
||||||
response->print(R"(<a onclick="get('/mode?mode=0');">NONE</a><br>)");
|
|
||||||
response->print(R"(<a onclick="get('/mode?mode=1');">BORDER</a><br>)");
|
|
||||||
response->print(R"(<a onclick="get('/mode?mode=2');">CLOCK</a><br>)");
|
|
||||||
response->print(R"(<a onclick="get('/mode?mode=3');">GAME_OF_LIFE_BLACK_WHITE</a><br>)");
|
|
||||||
response->print(R"(<a onclick="get('/mode?mode=4');">GAME_OF_LIFE_GRAYSCALE</a><br>)");
|
|
||||||
response->print(R"(<a onclick="get('/mode?mode=5');">GAME_OF_LIFE_COLOR_FADE</a><br>)");
|
|
||||||
response->print(R"(<a onclick="get('/mode?mode=6');">GAME_OF_LIFE_RANDOM_COLOR</a><br>)");
|
|
||||||
response->print(R"(<a onclick="get('/mode?mode=7');">PONG</a><br>)");
|
|
||||||
response->print(R"(<a onclick="get('/mode?mode=8');">SPACE_INVADERS</a><br>)");
|
|
||||||
response->print(R"(<a onclick="get('/mode?mode=9');">COUNT_DOWN</a><br>)");
|
|
||||||
response->print(R"(<a onclick="get('/mode?mode=10');">COUNT_DOWN_BARS</a><br>)");
|
|
||||||
response->print(R"(<a onclick="get('/mode?mode=11');">COUNT_DOWN_SLEEP</a><br>)");
|
|
||||||
response->print(R"(<a onclick="get('/mode?mode=12');">STARFIELD</a><br>)");
|
|
||||||
response->print(R"(<a onclick="get('/mode?mode=13');">MATRIX</a><br>)");
|
|
||||||
response->print(R"(<a onclick="get('/mode?mode=14');">POWER</a><br>)");
|
|
||||||
response->print(R"(<a onclick="get('/mode?mode=15');">ENERGY</a><br>)");
|
|
||||||
response->print(R"(<a onclick="get('/mode?mode=16');">TIMER</a><br>)");
|
|
||||||
response->print(R"(</p>)");
|
|
||||||
|
|
||||||
response->print(R"(<p>)");
|
|
||||||
response->print(R"(Helligkeit: <a onclick="get('/brighter');">+</a> / <a onclick="get('/darker');">-</a><br>)");
|
|
||||||
response->print(R"(Geschwindigkeit: <a onclick="get('/faster');">+</a> / <a onclick="get('/slower');">-</a><br>)");
|
|
||||||
response->print(R"(</p>)");
|
|
||||||
|
|
||||||
response->print(R"(<p>)");
|
|
||||||
response->printf(R"(<input type="number" min="2025" max="3000" step="1" id="year">)");
|
|
||||||
response->printf(R"(<input type="number" min="1" max="12" step="1" id="month">)");
|
|
||||||
response->printf(R"(<input type="number" min="1" max="31" step="1" id="day">)");
|
|
||||||
response->printf(R"(<input type="number" min="0" max="23" step="1" id="hour">)");
|
|
||||||
response->printf(R"(<input type="number" min="0" max="59" step="1" id="minute">)");
|
|
||||||
response->printf(R"(<input type="number" min="0" max="59" step="1" id="second">)");
|
|
||||||
response->print(R"(<button onclick="configDate();">Datum setzen</button>)");
|
|
||||||
response->print(R"(</p>)");
|
|
||||||
|
|
||||||
response->print(R"(<p>)");
|
|
||||||
response->print(R"(<button onclick="get('/config/save');">Speichern erzwingen</button>)");
|
|
||||||
response->print(R"(</p>)");
|
|
||||||
|
|
||||||
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<int>(value);
|
|
||||||
|
|
||||||
auto *response = request->beginResponseStream("text/html");
|
|
||||||
|
|
||||||
response->print(style);
|
|
||||||
response->print(script);
|
|
||||||
response->print(R"(<meta name="viewport" content= "width=device-width, user-scalable=no">)");
|
|
||||||
response->print(R"(<table>)");
|
|
||||||
response->print(R"(<tr>)");
|
|
||||||
response->print(R"(<td><a href='/'>←</td>)");
|
|
||||||
response->print(R"(<td>)");
|
|
||||||
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/move?index=%d&x=0&y=-1');">↑</button><br>)", index);
|
|
||||||
response->print(buffer);
|
|
||||||
response->print(R"(</td>)");
|
|
||||||
response->print(R"(<td> </td>)");
|
|
||||||
response->print(R"(</tr>)");
|
|
||||||
response->print(R"(<tr>)");
|
|
||||||
response->print(R"(<td>)");
|
|
||||||
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/move?index=%d&x=-1&y=0');">←</button><br>)", index);
|
|
||||||
response->print(buffer);
|
|
||||||
response->print(R"(</td>)");
|
|
||||||
response->print(R"(<td>)");
|
|
||||||
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/fire?index=%d');">X</button><br>)", index);
|
|
||||||
response->print(buffer);
|
|
||||||
response->print(R"(</td>)");
|
|
||||||
response->print(R"(<td>)");
|
|
||||||
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/move?index=%d&x=+1&y=0');">→</button><br>)", index);
|
|
||||||
response->print(buffer);
|
|
||||||
response->print(R"(</td>)");
|
|
||||||
response->print(R"(</tr>)");
|
|
||||||
response->print(R"(<tr>)");
|
|
||||||
response->print(R"(<td> </td>)");
|
|
||||||
response->print(R"(<td>)");
|
|
||||||
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/move?index=%d&x=0&y=+1');">↓</button><br>)", index);
|
|
||||||
response->print(buffer);
|
|
||||||
response->print(R"(</td>)");
|
|
||||||
response->print(R"(<td> </td>)");
|
|
||||||
response->print(R"(</tr>)");
|
|
||||||
response->print(R"(</table>)");
|
|
||||||
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<int>(value);
|
|
||||||
|
|
||||||
if (!request->hasParam("x")) {
|
|
||||||
request->send(400, "text/plain", "Missing 'x'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
value = request->getParam("x")->value().toDouble();
|
|
||||||
const auto x = static_cast<int>(value);
|
|
||||||
|
|
||||||
if (!request->hasParam("y")) {
|
|
||||||
request->send(400, "text/plain", "Missing 'y'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
value = request->getParam("y")->value().toDouble();
|
|
||||||
const auto y = static_cast<int>(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<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;
|
|
||||||
}
|
|
||||||
auto value = request->getParam("mode")->value().toDouble();
|
|
||||||
if (isnan(value)) {
|
|
||||||
request->send(400, "text/plain", "'mode' not a number");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setMode(static_cast<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 {
|
class Node final : public PatrixNode {
|
||||||
|
|
||||||
@ -263,28 +16,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setup() override {
|
void setup() override {
|
||||||
|
displaySetup();
|
||||||
modeSetup();
|
modeSetup();
|
||||||
|
patrixHttpSetup();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() override {
|
void loop() override {
|
||||||
modeLoop(display);
|
modeLoop(display);
|
||||||
display.loop();
|
displayLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void mqttMessage(char *topic, char *message) override {
|
void mqttMessage(char *topic, char *message) override {
|
||||||
|
|||||||
86
src/http.cpp
Normal file
86
src/http.cpp
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#include "http.h"
|
||||||
|
|
||||||
|
#include <patrix/core/http.h>
|
||||||
|
|
||||||
|
#include <Display.h>
|
||||||
|
#include <mode.h>
|
||||||
|
|
||||||
|
// ReSharper disable CppLocalVariableMayBeConst
|
||||||
|
|
||||||
|
void httpState(AsyncWebServerRequest *request) {
|
||||||
|
auto doc = JsonDocument();
|
||||||
|
auto rootJson = doc.to<JsonObject>();
|
||||||
|
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<ModeId>(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);
|
||||||
|
}
|
||||||
6
src/http.h
Normal file
6
src/http.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef HTTP_H
|
||||||
|
#define HTTP_H
|
||||||
|
|
||||||
|
void patrixHttpSetup();
|
||||||
|
|
||||||
|
#endif
|
||||||
10
src/mode.cpp
10
src/mode.cpp
@ -32,7 +32,7 @@ void modeStep();
|
|||||||
|
|
||||||
void modeSetup() {
|
void modeSetup() {
|
||||||
wanted = config.get("mode", GAME_OF_LIFE_RANDOM_COLOR);
|
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) {
|
void modeLoop(Display& display) {
|
||||||
@ -61,11 +61,15 @@ void setMode(const ModeId newMode) {
|
|||||||
wanted = newMode;
|
wanted = newMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
double getSpeed() {
|
ModeId getModeId() {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getModeSpeed() {
|
||||||
return modeSpeed;
|
return modeSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSpeed(const double newSpeed) {
|
void setModeSpeed(const double newSpeed) {
|
||||||
modeSpeed = min(max(0.01, newSpeed), 10000.0);
|
modeSpeed = min(max(0.01, newSpeed), 10000.0);
|
||||||
config.setIfNot("speed", modeSpeed);
|
config.setIfNot("speed", modeSpeed);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,9 +12,11 @@ void modeLoop(Display& display);
|
|||||||
|
|
||||||
void setMode(ModeId newMode);
|
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);
|
void modeMove(int index, int x, int y);
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ class CountDown final : public Mode {
|
|||||||
|
|
||||||
Firework fireworks[MAX_FIREWORKS];
|
Firework fireworks[MAX_FIREWORKS];
|
||||||
|
|
||||||
time_t targetEpochSeconds = 0;
|
time_t deadlineEpoch = 0;
|
||||||
|
|
||||||
tm target{};
|
tm target{};
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void loadConfig() override {
|
void loadConfig() override {
|
||||||
targetEpochSeconds = config.get("targetEpochSeconds", 1767222000);
|
deadlineEpoch = config.get("deadlineEpoch", 1767222000);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -63,11 +63,11 @@ protected:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
localtime_r(&targetEpochSeconds, &target);
|
localtime_r(&deadlineEpoch, &target);
|
||||||
target.tm_year += 1900;
|
target.tm_year += 1900;
|
||||||
target.tm_mon += 1;
|
target.tm_mon += 1;
|
||||||
|
|
||||||
const auto diffSeconds = difftime(targetEpochSeconds, nowEpochSeconds);
|
const auto diffSeconds = difftime(deadlineEpoch, nowEpochSeconds);
|
||||||
days = static_cast<int>(floor(diffSeconds / (24 * 60 * 60)));
|
days = static_cast<int>(floor(diffSeconds / (24 * 60 * 60)));
|
||||||
hours = static_cast<int>(floor(diffSeconds / (60 * 60))) % 24;
|
hours = static_cast<int>(floor(diffSeconds / (60 * 60))) % 24;
|
||||||
minutes = static_cast<int>(floor(diffSeconds / 60)) % 60;
|
minutes = static_cast<int>(floor(diffSeconds / 60)) % 60;
|
||||||
|
|||||||
@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
class Timer2 final : public Mode {
|
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;
|
unsigned long lastMillis = 0;
|
||||||
|
|
||||||
@ -32,15 +32,15 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void loadConfig() override {
|
void loadConfig() override {
|
||||||
const auto newDurationMillis = config.get("durationMillis", DEFAULT_DURATION_MILLIS);
|
const auto newTimerMillis = config.get("timerMillis", DEFAULT_DURATION_MILLIS);
|
||||||
if (restMillis > 0) {
|
if (restMillis > 0) {
|
||||||
restMillis += newDurationMillis - durationMillis;
|
restMillis += newTimerMillis - timerMillis;
|
||||||
}
|
}
|
||||||
durationMillis = newDurationMillis;
|
timerMillis = newTimerMillis;
|
||||||
}
|
}
|
||||||
|
|
||||||
void start() override {
|
void start() override {
|
||||||
restMillis = durationMillis;
|
restMillis = timerMillis;
|
||||||
lastMillis = millis();
|
lastMillis = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user