Compare commits
2 Commits
4f9fe2a24d
...
80b83705ac
| Author | SHA1 | Date | |
|---|---|---|---|
| 80b83705ac | |||
| bee63d137f |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
.pio
|
.pio/
|
||||||
|
img/
|
||||||
CMakeListsPrivate.txt
|
CMakeListsPrivate.txt
|
||||||
cmake-build-*/
|
cmake-build-*/
|
||||||
/.idea
|
/.idea
|
||||||
41
data-pretty/Canvas.js
Normal file
41
data-pretty/Canvas.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
87
data-pretty/Drawing.js
Normal file
87
data-pretty/Drawing.js
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
data-pretty/Picker.js
Normal file
48
data-pretty/Picker.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
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})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
3
data-pretty/index.css
Normal file
3
data-pretty/index.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
15
data-pretty/index.html
Normal file
15
data-pretty/index.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!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>
|
||||||
9
data-pretty/index.js
Normal file
9
data-pretty/index.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
window.onload = function () {
|
||||||
|
const drawing = new Drawing(8, 8, 60, "canvas");
|
||||||
|
new Picker(
|
||||||
|
60,
|
||||||
|
"picker",
|
||||||
|
(color => drawing.color0 = color),
|
||||||
|
(color => drawing.color1 = color),
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,20 +1,10 @@
|
|||||||
; PlatformIO Project Configuration File
|
|
||||||
;
|
|
||||||
; Build options: build flags, source filter
|
|
||||||
; Upload options: custom upload port, speed and extra flags
|
|
||||||
; Library options: dependencies, extra library storages
|
|
||||||
; Advanced options: extra scripting
|
|
||||||
;
|
|
||||||
; Please visit documentation for the other options and examples
|
|
||||||
; https://docs.platformio.org/page/projectconf.html
|
|
||||||
|
|
||||||
[basic]
|
[basic]
|
||||||
platform = espressif32
|
platform = espressif32
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps = https://github.com/adafruit/Adafruit_NeoPixel
|
board_build.filesystem = littlefs
|
||||||
https://github.com/knolleary/pubsubclient
|
lib_deps = ../Patrix
|
||||||
build_flags =
|
build_flags = -DWIFI_SSID=\"HappyNet\" -DWIFI_PKEY=\"1Grausame!Sackratte7\" -DWIFI_HOST=\"RGBMatrixDisplay\"
|
||||||
monitor_port = /dev/ttyUSB0
|
monitor_port = /dev/ttyUSB0
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_filters = esp32_exception_decoder
|
monitor_filters = esp32_exception_decoder
|
||||||
@ -23,6 +13,7 @@ monitor_filters = esp32_exception_decoder
|
|||||||
platform = ${basic.platform}
|
platform = ${basic.platform}
|
||||||
board = ${basic.board}
|
board = ${basic.board}
|
||||||
framework = ${basic.framework}
|
framework = ${basic.framework}
|
||||||
|
board_build.filesystem = ${basic.board_build.filesystem}
|
||||||
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}
|
||||||
@ -35,6 +26,7 @@ upload_speed = 921600
|
|||||||
platform = ${basic.platform}
|
platform = ${basic.platform}
|
||||||
board = ${basic.board}
|
board = ${basic.board}
|
||||||
framework = ${basic.framework}
|
framework = ${basic.framework}
|
||||||
|
board_build.filesystem = ${basic.board_build.filesystem}
|
||||||
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}
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
#include "BASICS.h"
|
|
||||||
|
|
||||||
double doStep(double valueCurrent, double valueMin, double valueMax, long long millisecondsTotal, microseconds_t microsecondsDelta) {
|
|
||||||
double valueRange = valueMax - valueMin;
|
|
||||||
double timeRatio = (double) microsecondsDelta / ((double) millisecondsTotal * 1000.0);
|
|
||||||
double valueStep = valueRange * timeRatio;
|
|
||||||
double valueNew = max(valueMin, min(valueMax, valueCurrent + valueStep));
|
|
||||||
return valueNew;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool randomBool(int uncertainty) {
|
|
||||||
return random(uncertainty) == 0;
|
|
||||||
}
|
|
||||||
24
src/BASICS.h
24
src/BASICS.h
@ -1,24 +0,0 @@
|
|||||||
#ifndef BASICS_H
|
|
||||||
#define BASICS_H
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
#define X true
|
|
||||||
#define _ false
|
|
||||||
|
|
||||||
#define ____ 0
|
|
||||||
#define QUAR 64
|
|
||||||
#define HALF 128
|
|
||||||
#define FULL 255
|
|
||||||
|
|
||||||
#define countof(x) (sizeof(x) / sizeof(x[0]))
|
|
||||||
|
|
||||||
typedef int64_t microseconds_t;
|
|
||||||
|
|
||||||
typedef unsigned long milliseconds_t;
|
|
||||||
|
|
||||||
double doStep(double valueCurrent, double valueMin, double valueMax, microseconds_t millisecondsTotal, microseconds_t microsecondsDelta);
|
|
||||||
|
|
||||||
bool randomBool(int uncertainty);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
36
src/Node.h
Normal file
36
src/Node.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef NODE_H
|
||||||
|
#define NODE_H
|
||||||
|
|
||||||
|
#include <patrix/core/Config.h>
|
||||||
|
#include <patrix/display/DisplayMatrix.h>
|
||||||
|
#include <patrix/node/PatrixNode.h>
|
||||||
|
|
||||||
|
Config config("/test.json");
|
||||||
|
|
||||||
|
DisplayMatrix<32, 8> display(27);
|
||||||
|
|
||||||
|
class Node final : public PatrixNode {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit Node() : PatrixNode(true, true, true) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() override {
|
||||||
|
config.read();
|
||||||
|
display.setup();
|
||||||
|
display.setBrightness(6);
|
||||||
|
display.clear();
|
||||||
|
display.foreground = Blue;
|
||||||
|
display.printf("Test");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() override {
|
||||||
|
config.loop();
|
||||||
|
display.loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,7 +1,7 @@
|
|||||||
#ifndef POSITION_H
|
#ifndef POSITION_H
|
||||||
#define POSITION_H
|
#define POSITION_H
|
||||||
|
|
||||||
#include "BASICS.h"
|
#include <Arduino.h>
|
||||||
|
|
||||||
class Vector {
|
class Vector {
|
||||||
|
|
||||||
105
src/config.cpp
105
src/config.cpp
@ -1,105 +0,0 @@
|
|||||||
#include <EEPROM.h>
|
|
||||||
#include "config.h"
|
|
||||||
#include "display.h"
|
|
||||||
|
|
||||||
#define WRITE_DELAY_MS (30 * 1000)
|
|
||||||
|
|
||||||
uint32_t calculateChecksum(Config *ptr);
|
|
||||||
|
|
||||||
void config_defaults();
|
|
||||||
|
|
||||||
bool validateChecksum(uint32_t checksumEEPROM, Config &tmp);
|
|
||||||
|
|
||||||
Config config;
|
|
||||||
|
|
||||||
bool dirty = false;
|
|
||||||
|
|
||||||
bool notify = false;
|
|
||||||
|
|
||||||
milliseconds_t lastDirtyMillis = 0;
|
|
||||||
|
|
||||||
void config_setup() {
|
|
||||||
if (!config_load()) {
|
|
||||||
config_defaults();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void config_set_dirty() {
|
|
||||||
dirty = true;
|
|
||||||
lastDirtyMillis = millis();
|
|
||||||
}
|
|
||||||
|
|
||||||
void config_loop() {
|
|
||||||
if (notify && millis() - lastDirtyMillis > WRITE_DELAY_MS + 2000) {
|
|
||||||
notify = false;
|
|
||||||
}
|
|
||||||
if (notify) {
|
|
||||||
bool blink = ((millis() - lastDirtyMillis) / 100) % 2 == 0;
|
|
||||||
display.set(0, 0, blink ? MAGENTA : BLACK);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dirty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (millis() - lastDirtyMillis <= 30000) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dirty = false;
|
|
||||||
notify = true;
|
|
||||||
|
|
||||||
uint32_t checksum = calculateChecksum(&config);
|
|
||||||
|
|
||||||
EEPROM.begin(512);
|
|
||||||
EEPROM.writeBytes(0, &config, sizeof(Config));
|
|
||||||
EEPROM.writeUInt(sizeof(Config), checksum);
|
|
||||||
EEPROM.end();
|
|
||||||
|
|
||||||
Serial.printf("Config saved to EEPROM.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void configSaveNowIfDirty() {
|
|
||||||
if (dirty) {
|
|
||||||
lastDirtyMillis = millis() - 30000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool config_load() {
|
|
||||||
Config tmp{};
|
|
||||||
|
|
||||||
EEPROM.begin(512);
|
|
||||||
EEPROM.readBytes(0, &tmp, sizeof(Config));
|
|
||||||
uint32_t checksum = EEPROM.readUInt(sizeof(Config));
|
|
||||||
EEPROM.end();
|
|
||||||
|
|
||||||
bool success = validateChecksum(checksum, tmp);
|
|
||||||
if (success) {
|
|
||||||
memcpy(&config, &tmp, sizeof(Config));
|
|
||||||
Serial.printf("Config loaded from EEPROM.\n");
|
|
||||||
} else {
|
|
||||||
Serial.printf("Failed to load config from EEPROM.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool validateChecksum(uint32_t checksumEEPROM, Config &tmp) {
|
|
||||||
uint32_t calculated = calculateChecksum(&tmp);
|
|
||||||
return checksumEEPROM == calculated;
|
|
||||||
}
|
|
||||||
|
|
||||||
void config_defaults() {
|
|
||||||
config.mode = COUNT_DOWN_SLEEP;
|
|
||||||
config.speed = 1.0;
|
|
||||||
config.brightness = 16;
|
|
||||||
config.date = {0};
|
|
||||||
Serial.printf("Config DEFAULTS loaded.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t calculateChecksum(Config *ptr) {
|
|
||||||
uint32_t checksum = 0;
|
|
||||||
for (auto *b = (uint8_t *) ptr; b < (uint8_t *) ptr + sizeof(Config); b++) {
|
|
||||||
checksum += *b;
|
|
||||||
}
|
|
||||||
return checksum;
|
|
||||||
}
|
|
||||||
25
src/config.h
25
src/config.h
@ -1,25 +0,0 @@
|
|||||||
#ifndef RGBMATRIXDISPLAY_CONFIG_H
|
|
||||||
#define RGBMATRIXDISPLAY_CONFIG_H
|
|
||||||
|
|
||||||
#include "mode/Mode.h"
|
|
||||||
|
|
||||||
struct Config {
|
|
||||||
ModeId mode;
|
|
||||||
double speed;
|
|
||||||
uint8_t brightness;
|
|
||||||
tm date;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern Config config;
|
|
||||||
|
|
||||||
void config_setup();
|
|
||||||
|
|
||||||
void config_loop();
|
|
||||||
|
|
||||||
void configSaveNowIfDirty();
|
|
||||||
|
|
||||||
bool config_load();
|
|
||||||
|
|
||||||
void config_set_dirty();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
#include "display.h"
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
Display display(32, 8);
|
|
||||||
|
|
||||||
void setBrightness(int brightness) {
|
|
||||||
brightness = max(1, min(brightness, 255));
|
|
||||||
if (display.getBrightness() == brightness) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
config.brightness = brightness;
|
|
||||||
display.setBrightness(brightness);
|
|
||||||
config_set_dirty();
|
|
||||||
|
|
||||||
Serial.printf("Setting brightness to %5.1f%%\n", brightness / 2.55);
|
|
||||||
}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
#ifndef RGBMATRIXDISPLAY_DISPLAY_H
|
|
||||||
#define RGBMATRIXDISPLAY_DISPLAY_H
|
|
||||||
|
|
||||||
#include "display/Display.h"
|
|
||||||
|
|
||||||
extern Display display;
|
|
||||||
|
|
||||||
void setBrightness(int brightness);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
#include "Color.h"
|
|
||||||
|
|
||||||
const Color BLACK = {____, ____, ____};
|
|
||||||
|
|
||||||
const Color WHITE = {FULL, FULL, FULL};
|
|
||||||
|
|
||||||
const Color RED = {FULL, ____, ____};
|
|
||||||
|
|
||||||
const Color GREEN = {____, FULL, ____};
|
|
||||||
|
|
||||||
const Color ORANGE = {FULL, QUAR, ____};
|
|
||||||
|
|
||||||
const Color BLUE = {____, ____, FULL};
|
|
||||||
|
|
||||||
const Color YELLOW = {FULL, FULL, ____};
|
|
||||||
|
|
||||||
const Color MAGENTA = {FULL, ____, FULL};
|
|
||||||
|
|
||||||
const Color VIOLET = {HALF, ____, FULL};
|
|
||||||
|
|
||||||
const Color TURQUOISE = {____, FULL, FULL};
|
|
||||||
|
|
||||||
Color gray(uint8_t brightness) {
|
|
||||||
return {brightness, brightness, brightness};
|
|
||||||
}
|
|
||||||
|
|
||||||
Color randomColor() {
|
|
||||||
return {(uint8_t) random(255), (uint8_t) random(255), (uint8_t) random(255)};
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
#ifndef PIXEL_H
|
|
||||||
#define PIXEL_H
|
|
||||||
|
|
||||||
#include "BASICS.h"
|
|
||||||
|
|
||||||
struct Color {
|
|
||||||
uint8_t r;
|
|
||||||
uint8_t g;
|
|
||||||
uint8_t b;
|
|
||||||
};
|
|
||||||
|
|
||||||
Color gray(uint8_t brightness);
|
|
||||||
|
|
||||||
Color randomColor();
|
|
||||||
|
|
||||||
extern const Color BLACK;
|
|
||||||
|
|
||||||
extern const Color WHITE;
|
|
||||||
|
|
||||||
extern const Color RED;
|
|
||||||
|
|
||||||
extern const Color GREEN;
|
|
||||||
|
|
||||||
extern const Color ORANGE;
|
|
||||||
|
|
||||||
extern const Color BLUE;
|
|
||||||
|
|
||||||
extern const Color YELLOW;
|
|
||||||
|
|
||||||
extern const Color MAGENTA;
|
|
||||||
|
|
||||||
extern const Color VIOLET;
|
|
||||||
|
|
||||||
extern const Color TURQUOISE;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,180 +0,0 @@
|
|||||||
#include "Display.h"
|
|
||||||
|
|
||||||
bool SYMBOLS[SYMBOL_COUNT][DISPLAY_CHAR_WIDTH * DISPLAY_CHAR_HEIGHT] = {
|
|
||||||
{
|
|
||||||
X, X, X,
|
|
||||||
X, _, X,
|
|
||||||
X, _, X,
|
|
||||||
X, _, X,
|
|
||||||
X, X, X,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_, _, X,
|
|
||||||
_, X, X,
|
|
||||||
X, _, X,
|
|
||||||
_, _, X,
|
|
||||||
_, _, X,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
X, X, X,
|
|
||||||
_, _, X,
|
|
||||||
X, X, X,
|
|
||||||
X, _, _,
|
|
||||||
X, X, X,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
X, X, X,
|
|
||||||
_, _, X,
|
|
||||||
_, X, X,
|
|
||||||
_, _, X,
|
|
||||||
X, X, X,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
X, _, X,
|
|
||||||
X, _, X,
|
|
||||||
X, X, X,
|
|
||||||
_, _, X,
|
|
||||||
_, _, X,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
X, X, X,
|
|
||||||
X, _, _,
|
|
||||||
X, X, X,
|
|
||||||
_, _, X,
|
|
||||||
X, X, X,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
X, X, X,
|
|
||||||
X, _, _,
|
|
||||||
X, X, X,
|
|
||||||
X, _, X,
|
|
||||||
X, X, X,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
X, X, X,
|
|
||||||
_, _, X,
|
|
||||||
_, X, _,
|
|
||||||
X, _, _,
|
|
||||||
X, _, _,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
X, X, X,
|
|
||||||
X, _, X,
|
|
||||||
X, X, X,
|
|
||||||
X, _, X,
|
|
||||||
X, X, X,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
X, X, X,
|
|
||||||
X, _, X,
|
|
||||||
X, X, X,
|
|
||||||
_, _, X,
|
|
||||||
X, X, X,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_, _, _,
|
|
||||||
_, X, _,
|
|
||||||
_, _, _,
|
|
||||||
_, X, _,
|
|
||||||
_, _, _,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_, _, X,
|
|
||||||
_, _, X,
|
|
||||||
_, _, X,
|
|
||||||
X, _, X,
|
|
||||||
_, X, _,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
X, _, X,
|
|
||||||
X, X, X,
|
|
||||||
_, X, _,
|
|
||||||
X, X, X,
|
|
||||||
X, _, X,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_, _, _,
|
|
||||||
_, _, _,
|
|
||||||
X, X, X,
|
|
||||||
_, _, _,
|
|
||||||
_, _, _,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
X, _, _,
|
|
||||||
_, _, X,
|
|
||||||
_, X, _,
|
|
||||||
X, _, _,
|
|
||||||
_, _, X,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
X, X, X,
|
|
||||||
_, X, _,
|
|
||||||
_, X, _,
|
|
||||||
_, X, _,
|
|
||||||
_, X, _,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_, X, _,
|
|
||||||
X, _, X,
|
|
||||||
X, X, X,
|
|
||||||
X, _, X,
|
|
||||||
X, _, X,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_, X, X,
|
|
||||||
X, _, _,
|
|
||||||
X, X, X,
|
|
||||||
X, _, X,
|
|
||||||
_, X, _,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
X, X, X,
|
|
||||||
X, _, _,
|
|
||||||
X, X, X,
|
|
||||||
X, _, _,
|
|
||||||
X, X, X,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
X, _, X,
|
|
||||||
X, _, X,
|
|
||||||
X, X, X,
|
|
||||||
X, _, X,
|
|
||||||
X, _, X,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
X, X, _,
|
|
||||||
X, _, X,
|
|
||||||
X, X, _,
|
|
||||||
X, X, _,
|
|
||||||
X, _, X,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
X, _, _,
|
|
||||||
X, _, _,
|
|
||||||
X, _, _,
|
|
||||||
X, _, _,
|
|
||||||
X, _, _,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
X, _, _,
|
|
||||||
X, _, _,
|
|
||||||
X, _, _,
|
|
||||||
X, _, _,
|
|
||||||
X, X, X,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_, _, _,
|
|
||||||
_, _, _,
|
|
||||||
_, _, _,
|
|
||||||
_, _, _,
|
|
||||||
_, _, _,
|
|
||||||
},
|
|
||||||
// this must always be the last symbol (fallback)
|
|
||||||
{
|
|
||||||
X, X, X,
|
|
||||||
X, X, X,
|
|
||||||
X, X, X,
|
|
||||||
X, X, X,
|
|
||||||
X, X, X,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@ -1,329 +0,0 @@
|
|||||||
#ifndef DISPLAY_H
|
|
||||||
#define DISPLAY_H
|
|
||||||
|
|
||||||
#include "Color.h"
|
|
||||||
#include "Adafruit_NeoPixel.h"
|
|
||||||
#include "Vector.h"
|
|
||||||
|
|
||||||
#define SYMBOL_COUNT 26
|
|
||||||
|
|
||||||
#define SYMBOL_J 11
|
|
||||||
#define SYMBOL_X 12
|
|
||||||
#define SYMBOL_DASH 13
|
|
||||||
#define SYMBOL_PERCENT 14
|
|
||||||
#define SYMBOL_T 15
|
|
||||||
#define SYMBOL_A 16
|
|
||||||
#define SYMBOL_G 17
|
|
||||||
#define SYMBOL_E 18
|
|
||||||
#define SYMBOL_H 19
|
|
||||||
#define SYMBOL_R 20
|
|
||||||
#define SYMBOL_I 21
|
|
||||||
#define SYMBOL_L 22
|
|
||||||
#define SYMBOL_SPACE 23
|
|
||||||
|
|
||||||
#define DISPLAY_CHAR_WIDTH 3
|
|
||||||
#define DISPLAY_CHAR_HEIGHT 5
|
|
||||||
|
|
||||||
extern bool SYMBOLS[SYMBOL_COUNT][DISPLAY_CHAR_WIDTH * DISPLAY_CHAR_HEIGHT];
|
|
||||||
|
|
||||||
class Display {
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
const uint8_t width;
|
|
||||||
|
|
||||||
const uint8_t height;
|
|
||||||
|
|
||||||
const size_t pixelCount;
|
|
||||||
|
|
||||||
const size_t pixelByteCount;
|
|
||||||
|
|
||||||
bool fpsShow = false;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
Adafruit_NeoPixel leds;
|
|
||||||
|
|
||||||
milliseconds_t fpsLastMillis = 0;
|
|
||||||
|
|
||||||
int fps = 0;
|
|
||||||
|
|
||||||
Color *buffer = nullptr;
|
|
||||||
|
|
||||||
uint8_t brightness = 10;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Display(uint8_t width, uint8_t height) : width(width), height(height),
|
|
||||||
pixelCount(width * height),
|
|
||||||
pixelByteCount(pixelCount * sizeof(Color)),
|
|
||||||
leds(pixelCount, GPIO_NUM_13) {
|
|
||||||
buffer = (Color *) malloc(pixelByteCount);
|
|
||||||
if (buffer == nullptr) {
|
|
||||||
Serial.print("+-----------------------------------------------+\n");
|
|
||||||
Serial.print("| OUT OF MEMORY: Cannot allocate double-buffer! |\n");
|
|
||||||
Serial.print("+-----------------------------------------------+\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~Display() {
|
|
||||||
if (buffer == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
free(buffer);
|
|
||||||
buffer = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
leds.begin();
|
|
||||||
clear();
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
calculateFPS();
|
|
||||||
drawFpsBorder();
|
|
||||||
if (isDirty()) {
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setBrightness(uint8_t value) {
|
|
||||||
brightness = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t getBrightness() const {
|
|
||||||
return brightness;
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear() {
|
|
||||||
if (buffer == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
memset(buffer, 0, pixelByteCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ALIGN {
|
|
||||||
LEFT, RIGHT
|
|
||||||
};
|
|
||||||
|
|
||||||
uint8_t print2(int x, int y, double valueDbl, Color colorPositive, Color colorZero, Color colorNegative, ALIGN align = RIGHT) {
|
|
||||||
const Color color = valueDbl == 0 ? colorZero : (valueDbl < 0 ? colorNegative : colorPositive);
|
|
||||||
return print2(x, y, valueDbl, color, align);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t print2(int x, int y, double valueDbl, Color color, ALIGN align = RIGHT) {
|
|
||||||
if (isnan(valueDbl)) {
|
|
||||||
x -= 3 * (DISPLAY_CHAR_WIDTH + 1) - 1;
|
|
||||||
x += print(x, y, SYMBOL_DASH, color, true) + 1;
|
|
||||||
x += print(x, y, SYMBOL_DASH, color, true) + 1;
|
|
||||||
x += print(x, y, SYMBOL_DASH, color, true) + 1;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int value = (int) round(abs(valueDbl));
|
|
||||||
const bool negative = valueDbl < 0;
|
|
||||||
|
|
||||||
const int digitCount = (int) max(1.0, floor(log10(value)) + 1); // log10 is -inf for value==0, hence the max(1.0, ...)
|
|
||||||
if (align == RIGHT) {
|
|
||||||
x -= ((negative ? 1 : 0) + digitCount) * (DISPLAY_CHAR_WIDTH + 1) - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int divider = (int) pow(10, digitCount - 1);
|
|
||||||
if (negative) {
|
|
||||||
x += print(x, y, SYMBOL_DASH, color, true) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool showIfZero = false;
|
|
||||||
// Serial.printf("x=%d, y=%d, value=%d, align=%s, digitCount=%d, divider=%d\n", x, y, value, align == LEFT ? "LEFT" : "RIGHT", digitCount, divider);
|
|
||||||
for (int digitPos = 0; digitPos < digitCount; ++digitPos) {
|
|
||||||
const int digitVal = value / divider % 10;
|
|
||||||
showIfZero |= digitVal != 0 || (digitPos == digitCount - 1);
|
|
||||||
x += print(x, y, digitVal, color, showIfZero) + 1;
|
|
||||||
// Serial.printf(" digitPos=%d, x=%d, y=%d, digitVal=%d, showIfZero=%s, divider=%d\n", digitPos, x, y, digitVal, showIfZero ? "true" : "false", divider);
|
|
||||||
divider /= 10;
|
|
||||||
}
|
|
||||||
// Serial.println();
|
|
||||||
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t print(uint8_t xPos, uint8_t yPos, uint8_t index, Color color, bool showIfZero) {
|
|
||||||
if (index == 0 && !showIfZero) {
|
|
||||||
return DISPLAY_CHAR_WIDTH;
|
|
||||||
}
|
|
||||||
if (index >= SYMBOL_COUNT) {
|
|
||||||
Serial.printf("Cannot print2 symbol #%u.\n", index);
|
|
||||||
index = SYMBOL_COUNT - 1;
|
|
||||||
}
|
|
||||||
bool *symbolBit = SYMBOLS[index];
|
|
||||||
for (uint8_t y = 0; y < DISPLAY_CHAR_HEIGHT; ++y) {
|
|
||||||
for (uint8_t x = 0; x < DISPLAY_CHAR_WIDTH; ++x) {
|
|
||||||
if (*(symbolBit++)) {
|
|
||||||
set(xPos + x, yPos + y, color);
|
|
||||||
} else {
|
|
||||||
set(xPos + x, yPos + y, BLACK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return DISPLAY_CHAR_WIDTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO REMOVE QUICK & DIRTY
|
|
||||||
uint8_t printM(uint8_t xPos, uint8_t yPos, Color color) {
|
|
||||||
bool sym[5][5] = {
|
|
||||||
{X,_,_,_,X},
|
|
||||||
{X,X,_,X,X},
|
|
||||||
{X,_,X,_,X},
|
|
||||||
{X,_,_,_,X},
|
|
||||||
{X,_,_,_,X},
|
|
||||||
};
|
|
||||||
for (int y = 0; y < countof(sym); ++y) {
|
|
||||||
for (int x = 0; x < countof(sym[0]); ++x) {
|
|
||||||
if (sym[y][x]) {
|
|
||||||
set(xPos + x, yPos + y, color);
|
|
||||||
} else {
|
|
||||||
set(xPos + x, yPos + y, BLACK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return countof(sym[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool doCreeperBlink() {
|
|
||||||
const auto now = millis();
|
|
||||||
static auto blink = false;
|
|
||||||
static auto last = now;
|
|
||||||
if (!blink) {
|
|
||||||
if (now - last >= 3000) {
|
|
||||||
last = now;
|
|
||||||
if (random(3) == 0) {
|
|
||||||
blink = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (now - last >= 500) {
|
|
||||||
last = now;
|
|
||||||
blink = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return blink;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO REMOVE QUICK & DIRTY
|
|
||||||
uint8_t printCreeper(uint8_t xPos, uint8_t yPos) {
|
|
||||||
const auto creeperBlink = doCreeperBlink();
|
|
||||||
bool sym[8][8] = {
|
|
||||||
{X, X, X, X, X, X, X, X},
|
|
||||||
{X, X, X, X, X, X, X, X},
|
|
||||||
{X, _, _, X, X, _, _, X},
|
|
||||||
{X, _, _, X, X, _, _, X},
|
|
||||||
{X, X, X, _, _, X, X, X},
|
|
||||||
{X, X, _, _, _, _, X, X},
|
|
||||||
{X, X, _, _, _, _, X, X},
|
|
||||||
{X, X, _, X, X, _, X, X},
|
|
||||||
};
|
|
||||||
for (int y = 0; y < countof(sym); ++y) {
|
|
||||||
for (int x = 0; x < countof(sym[0]); ++x) {
|
|
||||||
if (creeperBlink && ((x == 1 || x == 2) && y == 2)) {
|
|
||||||
set(xPos + x, yPos + y, GREEN);
|
|
||||||
} else {
|
|
||||||
set(xPos + x, yPos + y, sym[y][x] ? GREEN : BLACK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return countof(sym[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO REMOVE QUICK & DIRTY
|
|
||||||
uint8_t printI(uint8_t xPos, uint8_t yPos, Color color) {
|
|
||||||
for (int y = 0; y < 5; ++y) {
|
|
||||||
set(xPos, yPos + y, color);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set(Vector pos, Color color) {
|
|
||||||
set((uint8_t) round(pos.x), (uint8_t) round(pos.y), color);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set(uint8_t x, uint8_t y, Color color) {
|
|
||||||
if (x >= width || y >= height) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ((y % 2) != 0) {
|
|
||||||
x = width - x - 1;
|
|
||||||
}
|
|
||||||
set(y * width + x, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set(uint16_t index, Color color) {
|
|
||||||
if (buffer == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
buffer[index] = {
|
|
||||||
// yes, correct order is GRB !!!
|
|
||||||
(uint8_t) (color.g * brightness >> 8),
|
|
||||||
(uint8_t) (color.r * brightness >> 8),
|
|
||||||
(uint8_t) (color.b * brightness >> 8)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void flush() {
|
|
||||||
if (buffer == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
memcpy(leds.getPixels(), buffer, pixelByteCount);
|
|
||||||
leds.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isDirty() const {
|
|
||||||
if (buffer == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return memcmp(leds.getPixels(), buffer, pixelByteCount) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void calculateFPS() {
|
|
||||||
fps = (int) round(1000.0 / (millis() - fpsLastMillis));
|
|
||||||
fpsLastMillis = millis();
|
|
||||||
}
|
|
||||||
|
|
||||||
void drawFpsBorder() {
|
|
||||||
if (!fpsShow) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int frames = fps;
|
|
||||||
|
|
||||||
Color color = RED;
|
|
||||||
if (frames > 3 * 76) {
|
|
||||||
frames -= 3 * 76;
|
|
||||||
color = WHITE;
|
|
||||||
} else if (frames > 2 * 76) {
|
|
||||||
frames -= 2 * 76;
|
|
||||||
color = BLUE;
|
|
||||||
} else if (frames > 76) {
|
|
||||||
frames -= 76;
|
|
||||||
color = GREEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int x = 0; x <= width - 1 && frames-- > 0; x++) {
|
|
||||||
set(x, 0, color);
|
|
||||||
}
|
|
||||||
for (int y = 0; y <= height - 1 && frames-- > 0; y++) {
|
|
||||||
set(width - 1, y, color);
|
|
||||||
}
|
|
||||||
for (int x = width - 1; x >= 0 && frames-- > 0; x--) {
|
|
||||||
set(x, height - 1, color);
|
|
||||||
}
|
|
||||||
for (int y = height - 1; y >= 0 && frames-- > 0; y--) {
|
|
||||||
set(0, y, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
37
src/main.cpp
37
src/main.cpp
@ -1,32 +1,19 @@
|
|||||||
#include "serial.h"
|
#include "Node.h"
|
||||||
#include "config.h"
|
|
||||||
#include "wifi.h"
|
|
||||||
|
|
||||||
#include "server.h"
|
// ReSharper disable once CppUnusedIncludeDirective
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
#include "mode.h"
|
// ReSharper disable once CppUnusedIncludeDirective
|
||||||
#include "display.h"
|
#include <ArduinoOTA.h>
|
||||||
#include "mqtt.h"
|
|
||||||
|
|
||||||
void setup() {
|
// ReSharper disable once CppUnusedIncludeDirective
|
||||||
delay(500);
|
#include <PubSubClient.h>
|
||||||
|
|
||||||
serial_setup();
|
// ReSharper disable once CppUnusedIncludeDirective
|
||||||
config_setup();
|
#include <Adafruit_NeoPixel.h>
|
||||||
wifi_setup();
|
|
||||||
|
|
||||||
server_setup();
|
auto node = Node();
|
||||||
|
|
||||||
display.setup();
|
PatrixNode &patrixGetNode() {
|
||||||
display.setBrightness(config.brightness);
|
return node;
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
serial_loop();
|
|
||||||
wifi_loop();
|
|
||||||
mqtt_loop();
|
|
||||||
server_loop();
|
|
||||||
mode_loop();
|
|
||||||
config_loop();
|
|
||||||
display.loop();
|
|
||||||
}
|
}
|
||||||
|
|||||||
142
src/mode.cpp
142
src/mode.cpp
@ -1,142 +0,0 @@
|
|||||||
#include "mode.h"
|
|
||||||
#include "config.h"
|
|
||||||
#include "mode/Border/Border.h"
|
|
||||||
#include "mode/Clock/Clock.h"
|
|
||||||
#include "mode/GameOfLife/GameOfLife.h"
|
|
||||||
#include "mode/Pong/Pong.h"
|
|
||||||
#include "mode/SpaceInvaders/SpaceInvaders.h"
|
|
||||||
#include "mode/CountDown/CountDown.h"
|
|
||||||
#include "mode/Starfield/Starfield.h"
|
|
||||||
#include "mode/Matrix/Matrix.h"
|
|
||||||
#include "display.h"
|
|
||||||
#include "mode/Power/Power.h"
|
|
||||||
#include "mode/Energy/Energy.h"
|
|
||||||
#include "mode/Timer/Timer.h"
|
|
||||||
|
|
||||||
ModeId currentModeId = NONE;
|
|
||||||
|
|
||||||
microseconds_t lastMicros = 0;
|
|
||||||
|
|
||||||
Mode *mode = nullptr;
|
|
||||||
|
|
||||||
void unloadOldMode();
|
|
||||||
|
|
||||||
void loadNewMode();
|
|
||||||
|
|
||||||
void mode_step();
|
|
||||||
|
|
||||||
void mode_loop() {
|
|
||||||
if (currentModeId != config.mode) {
|
|
||||||
unloadOldMode();
|
|
||||||
loadNewMode();
|
|
||||||
}
|
|
||||||
mode_step();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setMode(ModeId value) {
|
|
||||||
if (config.mode == value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
config.mode = value;
|
|
||||||
config_set_dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void modeMove(int index, int x, int y) {
|
|
||||||
if (mode != nullptr) {
|
|
||||||
mode->move(index, x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void modeFire(int index) {
|
|
||||||
if (mode != nullptr) {
|
|
||||||
mode->fire(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSpeed(double speed) {
|
|
||||||
speed = min(max(0.01, speed), 10000.0);
|
|
||||||
if (config.speed == speed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
config.speed = speed;
|
|
||||||
config_set_dirty();
|
|
||||||
Serial.printf("Setting speed to %6.2fx\n", config.speed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void unloadOldMode() {
|
|
||||||
if (mode != nullptr) {
|
|
||||||
delete mode;
|
|
||||||
mode = nullptr;
|
|
||||||
}
|
|
||||||
display.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void loadNewMode() {
|
|
||||||
currentModeId = config.mode;
|
|
||||||
lastMicros = 0;
|
|
||||||
switch (currentModeId) {
|
|
||||||
case BORDER:
|
|
||||||
mode = new Border(display);
|
|
||||||
break;
|
|
||||||
case CLOCK:
|
|
||||||
mode = new Clock(display);
|
|
||||||
break;
|
|
||||||
case GAME_OF_LIFE_BLACK_WHITE:
|
|
||||||
mode = new GameOfLife(display, BLACK_WHITE);
|
|
||||||
break;
|
|
||||||
case GAME_OF_LIFE_GRAYSCALE:
|
|
||||||
mode = new GameOfLife(display, GRAYSCALE);
|
|
||||||
break;
|
|
||||||
case GAME_OF_LIFE_COLOR_FADE:
|
|
||||||
mode = new GameOfLife(display, COLOR_FADE);
|
|
||||||
break;
|
|
||||||
case GAME_OF_LIFE_RANDOM_COLOR:
|
|
||||||
mode = new GameOfLife(display, RANDOM_COLOR);
|
|
||||||
break;
|
|
||||||
case PONG:
|
|
||||||
mode = new Pong(display);
|
|
||||||
break;
|
|
||||||
case SPACE_INVADERS:
|
|
||||||
mode = new SpaceInvaders(display);
|
|
||||||
break;
|
|
||||||
case COUNT_DOWN:
|
|
||||||
mode = new CountDown(display, false, false);
|
|
||||||
break;
|
|
||||||
case COUNT_DOWN_BARS:
|
|
||||||
mode = new CountDown(display, true, false);
|
|
||||||
break;
|
|
||||||
case COUNT_DOWN_SLEEP:
|
|
||||||
mode = new CountDown(display, false, true);
|
|
||||||
break;
|
|
||||||
case STARFIELD:
|
|
||||||
mode = new Starfield(display);
|
|
||||||
break;
|
|
||||||
case MATRIX:
|
|
||||||
mode = new Matrix(display);
|
|
||||||
break;
|
|
||||||
case POWER:
|
|
||||||
mode = new Power(display);
|
|
||||||
break;
|
|
||||||
case ENERGY:
|
|
||||||
mode = new Energy(display);
|
|
||||||
break;
|
|
||||||
case TIMER:
|
|
||||||
mode = new Timer2(display);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Serial.print("No mode loaded.\n");
|
|
||||||
display.clear();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Serial.printf("Mode: %s\n", mode == nullptr ? "None" : mode->getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
void mode_step() {
|
|
||||||
if (mode == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto currentMicros = (int64_t) micros();
|
|
||||||
microseconds_t microseconds = (microseconds_t) min(1000000.0, max(1.0, (double) (currentMicros - lastMicros) * config.speed));
|
|
||||||
lastMicros = currentMicros;
|
|
||||||
mode->loop(microseconds);
|
|
||||||
}
|
|
||||||
16
src/mode.h
16
src/mode.h
@ -1,16 +0,0 @@
|
|||||||
#ifndef RGBMATRIXDISPLAY_MODE_H
|
|
||||||
#define RGBMATRIXDISPLAY_MODE_H
|
|
||||||
|
|
||||||
#include "mode/Mode.h"
|
|
||||||
|
|
||||||
void mode_loop();
|
|
||||||
|
|
||||||
void setMode(ModeId value);
|
|
||||||
|
|
||||||
void setSpeed(double speed);
|
|
||||||
|
|
||||||
void modeMove(int index, int x, int y);
|
|
||||||
|
|
||||||
void modeFire(int index);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
141
src/mqtt.cpp
141
src/mqtt.cpp
@ -1,141 +0,0 @@
|
|||||||
#include <WiFiClient.h>
|
|
||||||
#include <WiFi.h>
|
|
||||||
#include "mqtt.h"
|
|
||||||
#include "PubSubClient.h"
|
|
||||||
#include "wifi.h"
|
|
||||||
|
|
||||||
#define MQTT_CONNECT_TIMEOUT_MILLISECONDS 5000
|
|
||||||
|
|
||||||
#define MQTT_MAX_MESSAGE_AGE_MILLIS 11000
|
|
||||||
|
|
||||||
#define PHOTOVOLTAIC_POWER_W "openDTU/pv/ac/power"
|
|
||||||
#define PHOTOVOLTAIC_ENERGY_KWH "openDTU/pv/ac/yieldtotal"
|
|
||||||
#define GRID_POWER_W "electricity/grid/power/signed/w"
|
|
||||||
#define GRID_IMPORT_WH "electricity/grid/energy/import/wh"
|
|
||||||
#define GRID_EXPORT_WH "electricity/grid/energy/export/wh"
|
|
||||||
|
|
||||||
WiFiClient espClient;
|
|
||||||
|
|
||||||
PubSubClient mqtt(espClient);
|
|
||||||
|
|
||||||
bool mqttConnected = false;
|
|
||||||
|
|
||||||
unsigned long mqttLastConnectTry = 0;
|
|
||||||
|
|
||||||
double photovoltaicPowerW = NAN;
|
|
||||||
|
|
||||||
unsigned long photovoltaicPowerWLast = 0;
|
|
||||||
|
|
||||||
double photovoltaicEnergyKWh = NAN;
|
|
||||||
|
|
||||||
unsigned long photovoltaicEnergyKWhLast = 0;
|
|
||||||
|
|
||||||
double gridPowerW = NAN;
|
|
||||||
|
|
||||||
unsigned long gridPowerWLast = 0;
|
|
||||||
|
|
||||||
double gridImportKWh = NAN;
|
|
||||||
|
|
||||||
unsigned long gridImportKWhLast = 0;
|
|
||||||
|
|
||||||
double gridExportKWh = NAN;
|
|
||||||
|
|
||||||
unsigned long gridExportKWhLast = 0;
|
|
||||||
|
|
||||||
void mqttDisconnect();
|
|
||||||
|
|
||||||
void mqttCallback(char *topic, uint8_t *payload, unsigned int length) {
|
|
||||||
char message[128];
|
|
||||||
if (length > sizeof message - 1) {
|
|
||||||
Serial.printf("MQTT: received too long message: topic=%s, length=%d", topic, length);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
memcpy(message, payload, length);
|
|
||||||
message[length] = 0;
|
|
||||||
if (strcmp(PHOTOVOLTAIC_POWER_W, topic) == 0) {
|
|
||||||
photovoltaicPowerW = strtod(message, nullptr);
|
|
||||||
photovoltaicPowerWLast = millis();
|
|
||||||
} else if (strcmp(PHOTOVOLTAIC_ENERGY_KWH, topic) == 0) {
|
|
||||||
photovoltaicEnergyKWh = strtod(message, nullptr);
|
|
||||||
photovoltaicEnergyKWhLast = millis();
|
|
||||||
} else if (strcmp(GRID_POWER_W, topic) == 0) {
|
|
||||||
gridPowerW = strtod(message, nullptr);
|
|
||||||
gridPowerWLast = millis();
|
|
||||||
} else if (strcmp(GRID_IMPORT_WH, topic) == 0) {
|
|
||||||
gridImportKWh = strtod(message, nullptr) / 1000;
|
|
||||||
gridImportKWhLast = millis();
|
|
||||||
} else if (strcmp(GRID_EXPORT_WH, topic) == 0) {
|
|
||||||
gridExportKWh = strtod(message, nullptr) / 1000;
|
|
||||||
gridExportKWhLast = millis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void mqtt_loop() {
|
|
||||||
if (!wifiIsConnected()) {
|
|
||||||
mqttDisconnect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!mqtt.loop() && (mqttLastConnectTry == 0 || millis() - mqttLastConnectTry > MQTT_CONNECT_TIMEOUT_MILLISECONDS)) {
|
|
||||||
mqttLastConnectTry = millis();
|
|
||||||
mqtt.setServer("10.0.0.50", 1883);
|
|
||||||
mqttConnected = mqtt.connect(WiFiClass::getHostname());
|
|
||||||
if (mqttConnected) {
|
|
||||||
Serial.printf("Successfully connected mqtt broker at %s:%d\n", "10.0.0.50", 1883);
|
|
||||||
mqtt.setCallback(mqttCallback);
|
|
||||||
mqtt.subscribe(PHOTOVOLTAIC_POWER_W);
|
|
||||||
mqtt.subscribe(PHOTOVOLTAIC_ENERGY_KWH);
|
|
||||||
mqtt.subscribe(GRID_POWER_W);
|
|
||||||
mqtt.subscribe(GRID_IMPORT_WH);
|
|
||||||
mqtt.subscribe(GRID_EXPORT_WH);
|
|
||||||
} else {
|
|
||||||
Serial.printf("ERROR: Failed to connect MQTT broker at %s:%d\n", "10.0.0.50", 1883);
|
|
||||||
mqttDisconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void mqttDisconnect() {
|
|
||||||
if (mqttConnected) {
|
|
||||||
mqtt.disconnect();
|
|
||||||
mqttConnected = false;
|
|
||||||
photovoltaicPowerW = NAN;
|
|
||||||
photovoltaicPowerWLast = 0;
|
|
||||||
gridPowerW = NAN;
|
|
||||||
gridPowerWLast = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double getPhotovoltaicPowerW() {
|
|
||||||
if (millis() - photovoltaicPowerWLast > MQTT_MAX_MESSAGE_AGE_MILLIS) {
|
|
||||||
return NAN;
|
|
||||||
}
|
|
||||||
return photovoltaicPowerW;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getPhotovoltaicEnergyKWh() {
|
|
||||||
if (millis() - photovoltaicEnergyKWhLast > MQTT_MAX_MESSAGE_AGE_MILLIS) {
|
|
||||||
return NAN;
|
|
||||||
}
|
|
||||||
return photovoltaicEnergyKWh;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getGridPowerW() {
|
|
||||||
if (millis() - gridPowerWLast > MQTT_MAX_MESSAGE_AGE_MILLIS) {
|
|
||||||
return NAN;
|
|
||||||
}
|
|
||||||
return gridPowerW;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getGridImportKWh() {
|
|
||||||
if (millis() - gridImportKWhLast > MQTT_MAX_MESSAGE_AGE_MILLIS) {
|
|
||||||
return NAN;
|
|
||||||
}
|
|
||||||
return gridImportKWh;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getGridExportKWh() {
|
|
||||||
if (millis() - gridExportKWhLast > MQTT_MAX_MESSAGE_AGE_MILLIS) {
|
|
||||||
return NAN;
|
|
||||||
}
|
|
||||||
return gridExportKWh;
|
|
||||||
}
|
|
||||||
16
src/mqtt.h
16
src/mqtt.h
@ -1,16 +0,0 @@
|
|||||||
#ifndef SENSOR2_MQTT_H
|
|
||||||
#define SENSOR2_MQTT_H
|
|
||||||
|
|
||||||
void mqtt_loop();
|
|
||||||
|
|
||||||
double getPhotovoltaicPowerW();
|
|
||||||
|
|
||||||
double getPhotovoltaicEnergyKWh();
|
|
||||||
|
|
||||||
double getGridPowerW();
|
|
||||||
|
|
||||||
double getGridImportKWh();
|
|
||||||
|
|
||||||
double getGridExportKWh();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
#include <Arduino.h>
|
|
||||||
#include "serial.h"
|
|
||||||
#include "display.h"
|
|
||||||
#include "config.h"
|
|
||||||
#include "mode.h"
|
|
||||||
|
|
||||||
void serial_setup() {
|
|
||||||
Serial.begin(115200);
|
|
||||||
Serial.println("\n\n\nStartup!");
|
|
||||||
}
|
|
||||||
|
|
||||||
void serial_loop() {
|
|
||||||
if (Serial.available()) {
|
|
||||||
int input = Serial.read();
|
|
||||||
switch (input) {
|
|
||||||
case '0':
|
|
||||||
case '1':
|
|
||||||
case '2':
|
|
||||||
case '3':
|
|
||||||
case '4':
|
|
||||||
case '5':
|
|
||||||
case '6':
|
|
||||||
case '7':
|
|
||||||
case '8':
|
|
||||||
case '9':
|
|
||||||
setMode((ModeId) (input - '0'));
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
case 'b':
|
|
||||||
setMode((ModeId) (input - 'a' + 10));
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
setSpeed(1.0);
|
|
||||||
break;
|
|
||||||
case '+':
|
|
||||||
setBrightness(display.getBrightness() + 10);
|
|
||||||
break;
|
|
||||||
case '-':
|
|
||||||
setBrightness(display.getBrightness() - 10);
|
|
||||||
break;
|
|
||||||
case ',':
|
|
||||||
setSpeed(config.speed / 1.1);
|
|
||||||
break;
|
|
||||||
case '.':
|
|
||||||
setSpeed(config.speed * 1.1);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Serial.printf("Unknown command: %c\n", input);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#ifndef RGBMATRIXDISPLAY_SERIAL_H
|
|
||||||
#define RGBMATRIXDISPLAY_SERIAL_H
|
|
||||||
|
|
||||||
void serial_loop();
|
|
||||||
|
|
||||||
void serial_setup();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
315
src/server.cpp
315
src/server.cpp
@ -1,315 +0,0 @@
|
|||||||
#include <WebServer.h>
|
|
||||||
#include <cmath>
|
|
||||||
#include "server.h"
|
|
||||||
#include "mode/Mode.h"
|
|
||||||
#include "mode.h"
|
|
||||||
#include "display.h"
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
static const char *const 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 char *const script = R"(
|
|
||||||
<script>
|
|
||||||
function get(path){
|
|
||||||
var r = new XMLHttpRequest();
|
|
||||||
r.open("GET", path, true);
|
|
||||||
r.send();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
)";
|
|
||||||
|
|
||||||
WebServer server(80);
|
|
||||||
|
|
||||||
void web_index();
|
|
||||||
|
|
||||||
void web_player();
|
|
||||||
|
|
||||||
void web_player_move();
|
|
||||||
|
|
||||||
void web_player_fire();
|
|
||||||
|
|
||||||
void web_setMode();
|
|
||||||
|
|
||||||
void web_brighter();
|
|
||||||
|
|
||||||
void web_darker();
|
|
||||||
|
|
||||||
void web_faster();
|
|
||||||
|
|
||||||
void web_slower();
|
|
||||||
|
|
||||||
void web_fps_on();
|
|
||||||
|
|
||||||
void web_fps_off();
|
|
||||||
|
|
||||||
void web_config_date();
|
|
||||||
|
|
||||||
void web_config_save();
|
|
||||||
|
|
||||||
void server_setup() {
|
|
||||||
server.on("/", web_index);
|
|
||||||
server.on("/player", web_player);
|
|
||||||
server.on("/player/move", web_player_move);
|
|
||||||
server.on("/player/fire", web_player_fire);
|
|
||||||
server.on("/mode", web_setMode);
|
|
||||||
server.on("/brighter", web_brighter);
|
|
||||||
server.on("/darker", web_darker);
|
|
||||||
server.on("/faster", web_faster);
|
|
||||||
server.on("/slower", web_slower);
|
|
||||||
server.on("/fps/on", web_fps_on);
|
|
||||||
server.on("/fps/off", web_fps_off);
|
|
||||||
server.on("/config/date", web_config_date);
|
|
||||||
server.on("/config/save", web_config_save);
|
|
||||||
server.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
void server_loop() {
|
|
||||||
server.handleClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
void web_index() {
|
|
||||||
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
|
|
||||||
server.send(200, "text/html", "");
|
|
||||||
server.sendContent(style);
|
|
||||||
server.sendContent(script);
|
|
||||||
|
|
||||||
server.sendContent(R"(<p>)");
|
|
||||||
server.sendContent(R"(<a href="/player?index=0">Player 0</a><br>)");
|
|
||||||
server.sendContent(R"(<a href="/player?index=1">Player 1</a><br>)");
|
|
||||||
server.sendContent(R"(</p>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(<p>)");
|
|
||||||
server.sendContent(R"(<a onclick="get('/mode?mode=0');">NONE</a><br>)");
|
|
||||||
server.sendContent(R"(<a onclick="get('/mode?mode=1');">BORDER</a><br>)");
|
|
||||||
server.sendContent(R"(<a onclick="get('/mode?mode=2');">CLOCK</a><br>)");
|
|
||||||
server.sendContent(R"(<a onclick="get('/mode?mode=3');">GAME_OF_LIFE_BLACK_WHITE</a><br>)");
|
|
||||||
server.sendContent(R"(<a onclick="get('/mode?mode=4');">GAME_OF_LIFE_GRAYSCALE</a><br>)");
|
|
||||||
server.sendContent(R"(<a onclick="get('/mode?mode=5');">GAME_OF_LIFE_COLOR_FADE</a><br>)");
|
|
||||||
server.sendContent(R"(<a onclick="get('/mode?mode=6');">GAME_OF_LIFE_RANDOM_COLOR</a><br>)");
|
|
||||||
server.sendContent(R"(<a onclick="get('/mode?mode=7');">PONG</a><br>)");
|
|
||||||
server.sendContent(R"(<a onclick="get('/mode?mode=8');">SPACE_INVADERS</a><br>)");
|
|
||||||
server.sendContent(R"(<a onclick="get('/mode?mode=9');">COUNT_DOWN</a><br>)");
|
|
||||||
server.sendContent(R"(<a onclick="get('/mode?mode=10');">COUNT_DOWN_BARS</a><br>)");
|
|
||||||
server.sendContent(R"(<a onclick="get('/mode?mode=11');">COUNT_DOWN_SLEEP</a><br>)");
|
|
||||||
server.sendContent(R"(<a onclick="get('/mode?mode=12');">STARFIELD</a><br>)");
|
|
||||||
server.sendContent(R"(<a onclick="get('/mode?mode=13');">MATRIX</a><br>)");
|
|
||||||
server.sendContent(R"(<a onclick="get('/mode?mode=14');">POWER</a><br>)");
|
|
||||||
server.sendContent(R"(<a onclick="get('/mode?mode=15');">ENERGY</a><br>)");
|
|
||||||
server.sendContent(R"(<a onclick="get('/mode?mode=16');">TIMER</a><br>)");
|
|
||||||
server.sendContent(R"(</p>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(<p>)");
|
|
||||||
server.sendContent(R"(Helligkeit: <a onclick="get('/brighter');">+</a> / <a onclick="get('/darker');">-</a><br>)");
|
|
||||||
server.sendContent(R"(Geschwindigkeit: <a onclick="get('/faster');">+</a> / <a onclick="get('/slower');">-</a><br>)");
|
|
||||||
server.sendContent(R"(FPS: <a onclick="get('/fps/on');">EIN</a> / <a onclick="get('/fps/off');">AUS</a><br>)");
|
|
||||||
server.sendContent(R"(</p>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(<p>)");
|
|
||||||
server.sendContent(R"(<input type="number" min="1900" max="3000" step="1" name="year" id="year">)");
|
|
||||||
server.sendContent(R"(<input type="number" min="1" max="12" step="1" name="month" id="month">)");
|
|
||||||
server.sendContent(R"(<input type="number" min="1" max="31" step="1" name="day" id="day">)");
|
|
||||||
server.sendContent(R"(<button onclick="get('/config/date?year=' + document.getElementById('year').value + '&month=' + document.getElementById('month').value + '&day=' + document.getElementById('day').value);">Datum setzen</button>)");
|
|
||||||
server.sendContent(R"(</p>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(<p>)");
|
|
||||||
server.sendContent(R"(<button onclick="get('/config/save');">Speichern erzwingen</button>)");
|
|
||||||
server.sendContent(R"(</p>)");
|
|
||||||
|
|
||||||
server.client().flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
void web_player() {
|
|
||||||
char buffer[128];
|
|
||||||
|
|
||||||
if (!server.hasArg("index")) {
|
|
||||||
server.send(400, "text/plain", "Missing 'index'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
double value = strtod(server.arg("index").c_str(), nullptr);
|
|
||||||
int index = (int) value;
|
|
||||||
|
|
||||||
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
|
|
||||||
server.send(200, "text/html", "");
|
|
||||||
server.sendContent(style);
|
|
||||||
server.sendContent(script);
|
|
||||||
|
|
||||||
server.sendContent(R"(<meta name="viewport" content= "width=device-width, user-scalable=no">)");
|
|
||||||
|
|
||||||
server.sendContent(R"(<table>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(<tr>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(<td><a href='/'>←</td>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(<td>)");
|
|
||||||
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/move?index=%d&x=0&y=-1');">↑</button><br>)", index);
|
|
||||||
server.sendContent(buffer);
|
|
||||||
server.sendContent(R"(</td>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(<td> </td>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(</tr>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(<tr>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(<td>)");
|
|
||||||
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/move?index=%d&x=-1&y=0');">←</button><br>)", index);
|
|
||||||
server.sendContent(buffer);
|
|
||||||
server.sendContent(R"(</td>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(<td>)");
|
|
||||||
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/fire?index=%d');">X</button><br>)", index);
|
|
||||||
server.sendContent(buffer);
|
|
||||||
server.sendContent(R"(</td>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(<td>)");
|
|
||||||
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/move?index=%d&x=+1&y=0');">→</button><br>)", index);
|
|
||||||
server.sendContent(buffer);
|
|
||||||
server.sendContent(R"(</td>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(</tr>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(<tr>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(<td> </td>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(<td>)");
|
|
||||||
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/move?index=%d&x=0&y=+1');">↓</button><br>)", index);
|
|
||||||
server.sendContent(buffer);
|
|
||||||
server.sendContent(R"(</td>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(<td> </td>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(</tr>)");
|
|
||||||
|
|
||||||
server.sendContent(R"(</table>)");
|
|
||||||
server.client().flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
void web_player_move() {
|
|
||||||
double value;
|
|
||||||
|
|
||||||
if (!server.hasArg("index")) {
|
|
||||||
server.send(400, "text/plain", "Missing 'index'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
value = strtod(server.arg("index").c_str(), nullptr);
|
|
||||||
int index = (int) value;
|
|
||||||
|
|
||||||
if (!server.hasArg("x")) {
|
|
||||||
server.send(400, "text/plain", "Missing 'x'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
value = strtod(server.arg("x").c_str(), nullptr);
|
|
||||||
int x = (int) value;
|
|
||||||
|
|
||||||
if (!server.hasArg("y")) {
|
|
||||||
server.send(400, "text/plain", "Missing 'y'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
value = strtod(server.arg("y").c_str(), nullptr);
|
|
||||||
int y = (int) value;
|
|
||||||
|
|
||||||
modeMove(index, x, y);
|
|
||||||
|
|
||||||
server.send(200, "application/json", "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
void web_player_fire() {
|
|
||||||
double value;
|
|
||||||
|
|
||||||
if (!server.hasArg("index")) {
|
|
||||||
server.send(400, "text/plain", "Missing 'index'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
value = strtod(server.arg("index").c_str(), nullptr);
|
|
||||||
int index = (int) value;
|
|
||||||
|
|
||||||
modeFire(index);
|
|
||||||
|
|
||||||
server.send(200, "application/json", "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
void web_setMode() {
|
|
||||||
if (!server.hasArg("mode")) {
|
|
||||||
server.send(400, "text/plain", "Missing 'mode'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
double value = strtod(server.arg("mode").c_str(), nullptr);
|
|
||||||
if (isnan(value)) {
|
|
||||||
server.send(400, "text/plain", "'mode' not a number");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setMode((ModeId) value);
|
|
||||||
server.send(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
void web_brighter() {
|
|
||||||
setBrightness(display.getBrightness() + 10);
|
|
||||||
server.send(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
void web_darker() {
|
|
||||||
setBrightness(display.getBrightness() - 10);
|
|
||||||
server.send(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
void web_faster() {
|
|
||||||
setSpeed(config.speed * 1.1);
|
|
||||||
server.send(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
void web_slower() {
|
|
||||||
setSpeed(config.speed / 1.1);
|
|
||||||
server.send(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
void web_fps_on() {
|
|
||||||
display.fpsShow = true;
|
|
||||||
server.send(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
void web_fps_off() {
|
|
||||||
display.fpsShow = false;
|
|
||||||
server.send(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
void web_config_save() {
|
|
||||||
configSaveNowIfDirty();
|
|
||||||
server.send(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
void web_config_date() {
|
|
||||||
double year = strtod(server.arg("year").c_str(), nullptr);
|
|
||||||
double month = strtod(server.arg("month").c_str(), nullptr);
|
|
||||||
double day = strtod(server.arg("day").c_str(), nullptr);
|
|
||||||
if (!isnan(year)) {
|
|
||||||
config.date.tm_year = (int) year;
|
|
||||||
config.date.tm_mon = (int) month;
|
|
||||||
config.date.tm_mday = (int) day;
|
|
||||||
config.date.tm_hour = 0;
|
|
||||||
config.date.tm_min = 0;
|
|
||||||
config.date.tm_sec = 0;
|
|
||||||
server.send(200);
|
|
||||||
}
|
|
||||||
server.send(400);
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#ifndef RGBMATRIXDISPLAY_SERVER_H
|
|
||||||
#define RGBMATRIXDISPLAY_SERVER_H
|
|
||||||
|
|
||||||
void server_setup();
|
|
||||||
|
|
||||||
void server_loop();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
104
src/wifi.cpp
104
src/wifi.cpp
@ -1,104 +0,0 @@
|
|||||||
#include "wifi.h"
|
|
||||||
#include "display.h"
|
|
||||||
#include "mqtt.h"
|
|
||||||
|
|
||||||
#include <WiFi.h>
|
|
||||||
#include <ArduinoOTA.h>
|
|
||||||
#include <esp_sntp.h>
|
|
||||||
|
|
||||||
bool wifiConnected = false;
|
|
||||||
|
|
||||||
void onConnect();
|
|
||||||
|
|
||||||
uint32_t ip2int(const IPAddress &ip);
|
|
||||||
|
|
||||||
void timeSyncCallback(struct timeval *tv);
|
|
||||||
|
|
||||||
char *calculateGateway(char *calculatedGateway, size_t size);
|
|
||||||
|
|
||||||
void wifi_setup() {
|
|
||||||
WiFiClass::setHostname("RGBMatrixDisplay");
|
|
||||||
WiFi.begin("HappyNet", "1Grausame!Sackratte7");
|
|
||||||
yield();
|
|
||||||
|
|
||||||
ArduinoOTA.onStart([]() {
|
|
||||||
Serial.print("\n\nOTA Update: ");
|
|
||||||
display.clear();
|
|
||||||
display.loop();
|
|
||||||
});
|
|
||||||
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
|
|
||||||
double ratio = (double) progress / (double) total;
|
|
||||||
Serial.printf("\rOTA Update: %3.0f%%", ratio * 100);
|
|
||||||
|
|
||||||
auto index = (uint16_t) round(ratio * (double) display.pixelCount);
|
|
||||||
auto color = (uint8_t) round(ratio * 255.0);
|
|
||||||
display.set(index, {(uint8_t) (255 - color), color, 0});
|
|
||||||
display.loop();
|
|
||||||
});
|
|
||||||
ArduinoOTA.onEnd([]() {
|
|
||||||
Serial.println("\nOTA Success!\n");
|
|
||||||
display.clear();
|
|
||||||
display.loop();
|
|
||||||
});
|
|
||||||
ArduinoOTA.onError([](int error) {
|
|
||||||
Serial.println("\nOTA Failure!\n");
|
|
||||||
display.clear();
|
|
||||||
display.loop();
|
|
||||||
});
|
|
||||||
ArduinoOTA.begin();
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
void wifi_loop() {
|
|
||||||
ArduinoOTA.handle();
|
|
||||||
bool hasIp = (uint32_t) WiFi.localIP() != 0;
|
|
||||||
if (!wifiConnected) {
|
|
||||||
if (hasIp) {
|
|
||||||
wifiConnected = true;
|
|
||||||
onConnect();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!hasIp) {
|
|
||||||
wifiConnected = false;
|
|
||||||
Serial.println("WiFi disconnected!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onConnect() {
|
|
||||||
Serial.printf("WiFi connected: %s\n", WiFi.localIP().toString().c_str());
|
|
||||||
char calculatedGateway[16] = {0};
|
|
||||||
calculateGateway(calculatedGateway, sizeof(calculatedGateway));
|
|
||||||
sntp_set_time_sync_notification_cb(timeSyncCallback);
|
|
||||||
Serial.printf("configTime(%s / %s / %s)\n", WiFi.gatewayIP().toString().c_str(), calculatedGateway, "pool.ntp.org");
|
|
||||||
configTime(3600, 3600, "pool.ntp.org", WiFi.gatewayIP().toString().c_str(), calculatedGateway);
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool wifiIsConnected() {
|
|
||||||
return wifiConnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *calculateGateway(char *calculatedGateway, size_t size) {
|
|
||||||
uint32_t local = ip2int(WiFi.localIP());
|
|
||||||
uint32_t netmask = ip2int(WiFi.subnetMask());
|
|
||||||
uint32_t gateway = local & netmask + 1;
|
|
||||||
snprintf(
|
|
||||||
calculatedGateway,
|
|
||||||
size,
|
|
||||||
"%u.%u.%u.%u",
|
|
||||||
(gateway >> 24) & 0xFF,
|
|
||||||
(gateway >> 16) & 0xFF,
|
|
||||||
(gateway >> 8) & 0xFF,
|
|
||||||
(gateway >> 0) & 0xFF
|
|
||||||
);
|
|
||||||
return calculatedGateway;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t ip2int(const IPAddress &ip) {
|
|
||||||
return ((ip[0] * 256 + ip[1]) * 256 + ip[2]) * 256 + ip[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
void timeSyncCallback(struct timeval *tv) {
|
|
||||||
Serial.printf("timeSyncCallback: %ld\n", tv->tv_sec);
|
|
||||||
}
|
|
||||||
10
src/wifi.h
10
src/wifi.h
@ -1,10 +0,0 @@
|
|||||||
#ifndef RGBMATRIXDISPLAY_WIFI_H
|
|
||||||
#define RGBMATRIXDISPLAY_WIFI_H
|
|
||||||
|
|
||||||
void wifi_setup();
|
|
||||||
|
|
||||||
void wifi_loop();
|
|
||||||
|
|
||||||
bool wifiIsConnected();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
Loading…
Reference in New Issue
Block a user