html svg display + websocket

This commit is contained in:
Patrick Haßel 2025-01-09 13:53:44 +01:00
parent 0e93eafb7e
commit 12c089f7b9
5 changed files with 205 additions and 72 deletions

View File

@ -5,68 +5,166 @@
<meta name="viewport" content="width=device-width, user-scalable=no">
<title>Sporttafel</title>
<style>
body {
font-family: sans-serif;
font-size: 8vw;
margin: 0;
}
body {
font-family: sans-serif;
font-size: 8vw;
margin: 0;
}
button {
width: 33vmin;
height: 33vmin;
font-size: 9vw;
}
button {
width: 33vmin;
font-size: 9vw;
}
button.cancel {
width: 15vmin;
height: 15vmin;
}
button.cancel {
width: 15vmin;
height: 15vmin;
}
table {
border-collapse: collapse;
}
table {
border-collapse: collapse;
}
td {
text-align: center;
}
td {
text-align: center;
}
svg {
border: 1px solid black;
width: 100%;
height: calc(11 / 27 * 100%);
}
</style>
<script>
function get(path) {
var r = new XMLHttpRequest();
r.open("GET", path, true);
r.send();
}
</script>
</head>
<body>
<table>
<tr>
<td>
<button onclick="get('/action/cancel')" class="cancel">&cross;</button>
</td>
<td>
<button onclick="get('/action/up')">&uarr;</button>
</td>
<td>&nbsp;</td>
</tr>
<tr>
<td>
<button onclick="get('/action/left')">&larr;</button>
</td>
<td>
<button onclick="get('/action/confirm')">&check;</button>
</td>
<td>
<button onclick="get('/action/right')">&rarr;</button>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>
<button onclick="get('/action/down')">&darr;</button>
</td>
<td>&nbsp;</td>
</tr>
</table>
<svg width="800" height="325" id="display">
</svg>
<table>
<tr>
<td>
<button onclick="get('/action/cancel')" class="cancel">&cross;</button>
</td>
<td>
<button onclick="get('/action/up')">&uarr;</button>
</td>
<td>&nbsp;</td>
</tr>
<tr>
<td>
<button onclick="get('/action/left')">&larr;</button>
</td>
<td>
<button onclick="get('/action/confirm')">&check;</button>
</td>
<td>
<button onclick="get('/action/right')">&rarr;</button>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>
<button onclick="get('/action/down')">&darr;</button>
</td>
<td>&nbsp;</td>
</tr>
<!--suppress PointlessArithmeticExpressionJS -->
<script>
const S = 100 / 27;
const display = document.getElementById("display");
const segments = [];
function get(path) {
const request = new XMLHttpRequest();
request.open("GET", "http://10.0.0.119" + path, true);
request.send();
}
function drawDigit(x, y) {
drawPixel(x, y, 0, 3);
drawPixel(x, y, 0, 2);
drawPixel(x, y, 0, 1);
drawPixel(x, y, 1, 0);
drawPixel(x, y, 2, 0);
drawPixel(x, y, 3, 0);
drawPixel(x, y, 4, 1);
drawPixel(x, y, 4, 2);
drawPixel(x, y, 4, 3);
drawPixel(x, y, 4, 5);
drawPixel(x, y, 4, 6);
drawPixel(x, y, 4, 7);
drawPixel(x, y, 3, 8);
drawPixel(x, y, 2, 8);
drawPixel(x, y, 1, 8);
drawPixel(x, y, 0, 7);
drawPixel(x, y, 0, 6);
drawPixel(x, y, 0, 5);
drawPixel(x, y, 1, 4);
drawPixel(x, y, 2, 4);
drawPixel(x, y, 3, 4);
}
function drawPixel(offsetRasterX, offsetRasterY, innerRasterX, innerRasterY) {
const segment = document.createElementNS("http://www.w3.org/2000/svg", "rect");
const x = (offsetRasterX + innerRasterX) * S;
const y = (offsetRasterY + innerRasterY) * S;
segment.setAttribute("x", x + "vw");
segment.setAttribute("y", y + "vw");
segment.setAttribute("width", S + "vw");
segment.setAttribute("height", S + "vw");
segment.setAttribute("fill", "#555");
segment.setAttribute("id", "segment" + segments.length);
display.appendChild(segment);
segments.push(segment);
}
function drawDisplay(rasterX, rasterY) {
drawDigit(rasterX, rasterY);
rasterX += 6;
drawDigit(rasterX, rasterY);
rasterX += 6;
drawPixel(rasterX, rasterY, 0, 0);
drawPixel(rasterX, rasterY + 2.5, 0, 0);
drawPixel(rasterX, rasterY + 5.5, 0, 0);
drawPixel(rasterX, rasterY + 8, 0, 0);
rasterX += 2;
drawDigit(rasterX, rasterY);
rasterX += 6;
drawDigit(rasterX, rasterY);
}
function connect() {
console.log("connecting websocket...");
const host = "10.0.0.119";
const port = "80";
const url = `ws://${host}:${port}/ws`;
console.log(url);
const socket = new WebSocket(url);
socket.timeout = 1;
socket.addEventListener('open', _ => console.log('websocket connected'));
socket.addEventListener('message', event => {
event.data.split(',').filter((color, index) => color !== '').forEach((color, index) => segments[index].setAttribute("fill", "#" + color));
});
socket.addEventListener('close', _ => {
console.log('websocket disconnected');
connect();
});
}
drawDisplay(1, 1);
connect();
</script>
</table>
</body>
</html>

View File

@ -8,6 +8,8 @@ AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
auto websocketStarted = false;
void httpIndex(AsyncWebServerRequest *request) {
info(request->url().c_str());
request->send(LittleFS, "/index.html", "text/html");
@ -90,8 +92,15 @@ void httpSetup() {
server.on("/action/cancel", HTTP_GET, httpActionCancel);
server.on("/action/confirm", HTTP_GET, httpActionConfirm);
server.begin();
websocketStarted = true;
}
void httpLoop() {
ws.cleanupClients();
}
void websocketSend(const char *message) {
if (websocketStarted) {
ws.textAll(message);
}
}

View File

@ -5,4 +5,6 @@ void httpSetup();
void httpLoop();
void websocketSend(const char *message);
#endif

View File

@ -59,13 +59,14 @@ void logLoop() {
const auto c = static_cast<char>(Serial.read());
switch (c) {
case 13:
Serial.print(c);
execute(cmd);
write = cmd;
*write = 0;
break;
case 8:
Serial.print(c);
if (write > cmd) {
Serial.print(c);
*write-- = 0;
}
break;

View File

@ -7,6 +7,7 @@
#include "Color.h"
#include "font.h"
#include "core/http.h"
#define PIXELS_PER_SEGMENT 3
#define SEGMENTS_PER_DIGIT 7
@ -24,7 +25,7 @@ class Display {
// Adafruit_NeoPixel leds;
Color buffer[PIXEL_COUNT] = {};
Color pixels[PIXEL_COUNT] = {};
Color color = WHITE;
@ -46,12 +47,28 @@ public:
}
void clear() {
memset(buffer, 0, PIXEL_BYTE_COUNT);
memset(pixels, 0, PIXEL_BYTE_COUNT);
}
void flush() {
// memcpy(leds.getPixels(), buffer, PIXEL_BYTE_COUNT);
// leds.show();
const auto now = millis();
static auto last = now;
if (now - last >= 500) {
last = now;
send();
}
}
void send() {
char buffer[512];
char *b = buffer;
for (const auto& pixel: pixels) {
b += snprintf(b, sizeof buffer - (b - buffer), "%02X%02X%02X,", pixel.r, pixel.g, pixel.b);
}
websocketSend(buffer);
}
int printf(const char *format, ...) {
@ -78,12 +95,18 @@ public:
case '\'':
case '"':
case '`':
case '.': return printDots(pixel, false, false, false, true);
case ',': return printDots(pixel, false, false, true, true);
case ':': return printDots(pixel, false, true, true, false);
case ';': return printDots(pixel, false, true, true, true);
case '|': return printDots(pixel, true, true, true, true);
default: return printCharacter(pixel, character);
case '.':
return printDots(pixel, false, false, false, true);
case ',':
return printDots(pixel, false, false, true, true);
case ':':
return printDots(pixel, false, true, true, false);
case ';':
return printDots(pixel, false, true, true, true);
case '|':
return printDots(pixel, true, true, true, true);
default:
return printCharacter(pixel, character);
}
}
@ -93,19 +116,19 @@ public:
return 0;
}
if (dot0) {
buffer[pixel] = color;
pixels[pixel] = color;
}
pixel++;
if (dot1) {
buffer[pixel] = color;
pixels[pixel] = color;
}
pixel++;
if (dot2) {
buffer[pixel] = color;
pixels[pixel] = color;
}
pixel++;
if (dot3) {
buffer[pixel] = color;
pixels[pixel] = color;
}
return pixel;
}
@ -115,9 +138,9 @@ public:
const auto symbol = getSymbol(character);
for (auto s = *symbol; s < *symbol + SYMBOL_SIZE; s++) {
if (*s) {
buffer[pixel++] = color;
buffer[pixel++] = color;
buffer[pixel++] = color;
pixels[pixel++] = color;
pixels[pixel++] = color;
pixels[pixel++] = color;
} else {
pixel += 3;
}