From 28f8667baaa2d7ec3d7c321cd6159299903d747f Mon Sep 17 00:00:00 2001
From: Nic Limper
Date: Tue, 16 May 2023 15:34:51 +0200
Subject: [PATCH 01/21] Painter
- new paint option for freehand drawing and quick texts
- added gamma correction on rendering
- option to turn dither on/off on jpg upload
- python version for packagebinaries (same output as the php version)
---
ESP32_AP-Flasher/data/www/index.html | 1 +
ESP32_AP-Flasher/data/www/main.css | 84 +++++-
ESP32_AP-Flasher/data/www/main.js | 38 +++
ESP32_AP-Flasher/data/www/painter.js | 360 ++++++++++++++++++++++++
ESP32_AP-Flasher/src/contentmanager.cpp | 2 +-
ESP32_AP-Flasher/src/makeimage.cpp | 27 +-
ESP32_AP-Flasher/src/web.cpp | 6 +-
zbs243_AP_FW/packagebinaries.py | 57 ++++
8 files changed, 567 insertions(+), 8 deletions(-)
create mode 100644 ESP32_AP-Flasher/data/www/painter.js
create mode 100644 zbs243_AP_FW/packagebinaries.py
diff --git a/ESP32_AP-Flasher/data/www/index.html b/ESP32_AP-Flasher/data/www/index.html
index 9fb22b36..b6f07e69 100644
--- a/ESP32_AP-Flasher/data/www/index.html
+++ b/ESP32_AP-Flasher/data/www/index.html
@@ -38,6 +38,7 @@
+
diff --git a/ESP32_AP-Flasher/data/www/main.css b/ESP32_AP-Flasher/data/www/main.css
index be547bdc..c0459d9f 100644
--- a/ESP32_AP-Flasher/data/www/main.css
+++ b/ESP32_AP-Flasher/data/www/main.css
@@ -92,14 +92,17 @@ input {
border-radius: 0px;
}
-input[type=button] {
+input[type=button], button {
border: 0px;
padding: 4px 10px;
cursor:pointer;
}
-input[type=button]:hover {
+
+input[type=button]:hover,
+button:hover {
background-color:#aaaaaa;
}
+
select {
padding: 4px;
border-radius: 0px;
@@ -128,7 +131,7 @@ select {
#configbox input, #apconfigbox input {
border: solid 1px #666666;
- padding: 4px;
+ /*padding: 4px;*/
}
#configbox label, #apconfigbox label {
@@ -326,6 +329,81 @@ ul.messages li.new {
color: red;
}
+#paintbutton {
+ padding: 1px 3px;
+ border: 1px solid black;
+ font-size: 1.3em;
+ vertical-align: top;
+ margin-left:12px;
+ cursor: pointer;
+}
+
+#paintbutton:hover {
+ background-color: #aaaaaa;
+}
+
+/* painter */
+
+#canvasdiv {
+ padding: 5px;
+}
+
+#canvasdiv canvas {
+ border: 1px solid black;
+}
+
+#buttonbar {
+ padding: 5px;
+ display: flex;
+ gap: 5px;
+}
+
+#buttonbar button,
+#layersdiv button {
+ padding: 1px 2px;
+ border: 1px solid #cccccc;
+ background-color: #dddddd;
+ width: 40px;
+}
+#buttonbar button {
+ font-size: 1.2em;
+ font-weight: bold;
+}
+
+#buttonbar .active {
+ background-color: #ffffff;
+ cursor: pointer;
+}
+
+#buttonbar button:hover,
+#layersdiv button:hover {
+ background-color: #cccccc;
+ cursor: pointer;
+}
+
+#layersdiv {
+ padding: 0px 5px;
+}
+
+#layersdiv>div {
+ display: flex;
+ gap: 5px;
+ margin-bottom: 5px;
+}
+
+#layersdiv input,
+#layersdiv select {
+ padding: 2px;
+}
+
+#font-select {
+ width: 150px;
+}
+
+#savebar button {
+ border: solid 1px #666666;
+}
+
@media(max-width: 460px) {
.messages li div, ul.messages li div.date, ul.messages li div.message {
diff --git a/ESP32_AP-Flasher/data/www/main.js b/ESP32_AP-Flasher/data/www/main.js
index 3b7dbd6f..9049b2ab 100644
--- a/ESP32_AP-Flasher/data/www/main.js
+++ b/ESP32_AP-Flasher/data/www/main.js
@@ -31,6 +31,7 @@ contentModeOptions[12] = [];
const imageQueue = [];
let isProcessing = false;
let servertimediff = 0;
+let paintLoaded = false, paintShow = false;
let socket;
connect();
@@ -343,6 +344,40 @@ $('#apcfgsave').onclick = function () {
$('#apconfigbox').style.display = 'none';
}
+$('#paintbutton').onclick = function () {
+ if (paintShow) {
+ paintShow = false;
+ $('#cfgsave').parentNode.style.display = 'block';
+ contentselected();
+ } else {
+ paintShow = true;
+ $('#cfgsave').parentNode.style.display = 'none';
+ $('#customoptions').innerHTML = "
";
+ const mac = $('#cfgmac').dataset.mac
+ const hwtype = $('#tag' + mac).dataset.hwtype;
+ var [width, height] = displaySizeLookup[hwtype] || [0, 0];
+ if (height > width) [width, height] = [height, width];
+ if (paintLoaded) {
+ startPainter(mac, width, height);
+ } else {
+ loadScript('painter.js', function () {
+ startPainter(mac, width, height);
+ });
+ }
+ }
+}
+
+function loadScript(url, callback) {
+ var script = document.createElement('script');
+ script.src = url;
+ script.onload = function () {
+ if (callback) {
+ callback();
+ }
+ };
+ document.head.appendChild(script);
+}
+
function contentselected() {
let contentMode = $('#cfgcontent').value;
let extraoptions = contentModeOptions[contentMode];
@@ -352,6 +387,7 @@ function contentselected() {
obj = JSON.parse($('#cfgcontent').dataset.json);
}
if (contentMode) {
+ $('#paintbutton').style.display = (contentMode == 0 ? 'inline-block' : 'none');
extraoptions.forEach(element => {
var label = document.createElement("label");
label.innerHTML = element;
@@ -366,6 +402,8 @@ function contentselected() {
$('#customoptions').appendChild(p);
});
}
+ paintShow = false;
+ $('#cfgsave').parentNode.style.display = 'block';
}
function showMessage(message,iserr) {
diff --git a/ESP32_AP-Flasher/data/www/painter.js b/ESP32_AP-Flasher/data/www/painter.js
new file mode 100644
index 00000000..1244dff2
--- /dev/null
+++ b/ESP32_AP-Flasher/data/www/painter.js
@@ -0,0 +1,360 @@
+function startPainter(mac, width, height) {
+ let isDrawing = false;
+ let lastX = 0;
+ let lastY = 0;
+ let color = 'black';
+ let linewidth = 3;
+ let cursor = 'auto';
+ let isAddingText = false;
+ let layerDiv, intervalId, showCursor, input, textX, textY, font, sizeSelect, isDragging;
+
+ var fonts = ['Roboto', 'Open Sans', 'Lato', 'Montserrat', 'PT Sans', 'Barlow Condensed', 'Headland One', 'Sofia Sans Extra Condensed', 'Mynerve', 'Lilita One', 'Passion One', 'Big Shoulders Display'];
+
+ loadGoogleFonts(fonts);
+
+ const canvas = document.createElement('canvas');
+ canvas.width = width;
+ canvas.height = height;
+
+ const ctx = canvas.getContext('2d');
+
+ ctx.fillStyle = 'white';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ ctx.imageSmoothingEnabled = false;
+
+ $("#canvasdiv").appendChild(canvas);
+ canvas.style.imageRendering = 'pixelated';
+
+ canvas.addEventListener('mousedown', startDrawing);
+ canvas.addEventListener('mouseup', stopDrawing);
+ canvas.addEventListener('mousemove', draw);
+
+ canvas.addEventListener('touchstart', startDrawing, { passive: true });
+ canvas.addEventListener('touchend', stopDrawing);
+ canvas.addEventListener('touchmove', draw, { passive: true });
+
+ const bgCanvas = document.createElement('canvas');
+ bgCanvas.width = canvas.width;
+ bgCanvas.height = canvas.height;
+ const bgCtx = bgCanvas.getContext('2d');
+
+ bgCtx.fillStyle = '#ffffff';
+ bgCtx.fillRect(0, 0, bgCanvas.width, bgCanvas.height);
+
+ const txtButton = document.createElement('button');
+ txtButton.innerHTML = 'tT';
+ txtButton.style.fontStyle = 'italic';
+ txtButton.addEventListener('click', addText);
+
+ const blackButton = document.createElement('button');
+ blackButton.innerHTML = '﹏🖌';
+ blackButton.style.color = 'black';
+ blackButton.addEventListener('click', () => {
+ color = 'black';
+ linewidth = 3;
+ cursor = 'url("data:image/svg+xml;utf8,") 2 2, auto';
+ blackButton.classList.add('active');
+ redButton.classList.remove('active');
+ whiteButton.classList.remove('active');
+ });
+ blackButton.classList.add('active');
+
+ const redButton = document.createElement('button');
+ redButton.innerHTML = '﹏🖌';
+ redButton.style.color = 'red';
+ redButton.addEventListener('click', () => {
+ color = 'red';
+ linewidth = 3;
+ cursor = 'url("data:image/svg+xml;utf8,") 2 2, auto';
+ blackButton.classList.remove('active');
+ redButton.classList.add('active');
+ whiteButton.classList.remove('active');
+ });
+
+ const whiteButton = document.createElement('button');
+ whiteButton.innerHTML = '⬤';
+ whiteButton.style.color = 'white';
+ whiteButton.addEventListener('click', () => {
+ color = 'white';
+ linewidth = 20;
+ cursor = 'url("data:image/svg+xml;utf8,") 10 10, auto';
+ blackButton.classList.remove('active');
+ redButton.classList.remove('active');
+ whiteButton.classList.add('active');
+ });
+
+ const clearButton = document.createElement('button');
+ clearButton.innerHTML = '🖵';
+ clearButton.addEventListener('click', () => {
+ if (isAddingText) handleFinish(false);
+ ctx.fillStyle = 'white';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ });
+
+ const uploadButton = document.createElement('button');
+ uploadButton.innerHTML = 'Upload';
+ uploadButton.addEventListener('click', () => {
+ if (isAddingText) handleFinish(true);
+ const dataURL = canvas.toDataURL('image/jpeg');
+ const binaryImage = dataURLToBlob(dataURL);
+ const formData = new FormData();
+ formData.append('mac', mac);
+ formData.append('dither', '0');
+ formData.append('file', binaryImage, 'image.jpg');
+ const xhr = new XMLHttpRequest();
+ xhr.open('POST', '/imgupload');
+ xhr.send(formData);
+ $('#configbox').style.display = 'none';
+ });
+
+ $("#buttonbar").appendChild(blackButton);
+ $("#buttonbar").appendChild(redButton);
+ $("#buttonbar").appendChild(whiteButton);
+ $("#buttonbar").appendChild(txtButton);
+ $("#buttonbar").appendChild(clearButton);
+ $("#savebar").appendChild(uploadButton);
+
+ canvas.addEventListener('mouseenter', function () {
+ if (!isAddingText) {
+ canvas.style.cursor = cursor;
+ } else {
+ canvas.style.cursor = 'move';
+ }
+ });
+
+ canvas.addEventListener('mouseleave', function () {
+ canvas.style.cursor = 'auto';
+ });
+
+ function startDrawing(e) {
+ if (isAddingText) return;
+ isDrawing = true;
+ var rect = canvas.getBoundingClientRect();
+ lastX = e.pageX - rect.left - window.pageXOffset;
+ lastY = e.pageY - rect.top - window.pageYOffset;
+ }
+
+ function stopDrawing() {
+ if (isAddingText) return;
+ isDrawing = false;
+ }
+
+ function draw(e) {
+ if (isAddingText) return;
+ if (!isDrawing) return;
+ var rect = canvas.getBoundingClientRect();
+ ctx.beginPath();
+ ctx.moveTo(lastX, lastY);
+ ctx.lineTo(e.pageX - rect.left - window.pageXOffset, e.pageY - rect.top - window.pageYOffset);
+ ctx.strokeStyle = color;
+ ctx.lineWidth = linewidth;
+ ctx.lineCap = "round";
+ ctx.stroke();
+ lastX = e.pageX - rect.left - window.pageXOffset;
+ lastY = e.pageY - rect.top - window.pageYOffset;
+ }
+
+ function addText() {
+ if (isAddingText) {
+ handleFinish(true);
+ return;
+ }
+ txtButton.classList.add('active');
+ bgCtx.drawImage(canvas, 0, 0);
+
+ const defaultX = 5;
+ const defaultY = 40;
+ isDragging = false;
+ let startX, startY;
+ showCursor = true;
+
+ textX = defaultX;
+ textY = defaultY;
+ font = '24px ' + fonts[0];
+
+ input = document.createElement('textarea');
+ input.type = 'text';
+ input.placeholder = 'Type text here';
+ input.style.opacity = '0';
+ input.style.position = 'absolute';
+ input.style.left = '-200px'
+
+ input.addEventListener('input', () => {
+ drawText(input.value, textX, textY);
+ });
+ input.addEventListener('keyup', () => {
+ input.selectionStart = input.selectionEnd = input.value.length;
+ });
+
+ input.addEventListener('blur', function () {
+ input.focus();
+ });
+
+ intervalId = setInterval(function () {
+ showCursor = !showCursor;
+ drawText(input.value, textX, textY);
+ }, 300);
+
+ canvas.addEventListener('mouseup', handleMouseUp);
+ canvas.addEventListener('mousedown', handleMouseDown);
+ canvas.addEventListener('mousemove', handleMouseMove);
+
+ canvas.addEventListener('touchstart', handleTouchStart, { passive: true });
+ canvas.addEventListener('touchend', handleTouchEnd);
+ canvas.addEventListener('touchmove', handleTouchMove, { passive: true });
+
+ var sizes = [10,11,12,13,14,16,18,20,24,28,32,36,40,48,56,64,72,84];
+
+ const fontSelect = document.createElement('select');
+ fontSelect.id = 'font-select';
+ for (var i = 0; i < fonts.length; i++) {
+ const option = document.createElement('option');
+ option.value = fonts[i];
+ option.text = fonts[i];
+ option.style.fontFamily = fonts[i];
+ fontSelect.appendChild(option);
+ }
+
+ sizeSelect = document.createElement('select');
+ sizeSelect.id = 'size-select';
+ for (var i = 0; i < sizes.length; i++) {
+ const option = document.createElement('option');
+ option.value = sizes[i];
+ option.text = sizes[i] + ' px';
+ sizeSelect.appendChild(option);
+ }
+
+ function updateFont() {
+ var selectedFont = fontSelect.value;
+ var selectedSize = sizeSelect.value;
+ fontSelect.style.fontFamily = selectedFont;
+ font = selectedSize + 'px ' + selectedFont;
+ drawText(input.value, textX, textY);
+ }
+
+ fontSelect.value = fonts[0];
+ sizeSelect.value = '24';
+ fontSelect.addEventListener('change', updateFont);
+ sizeSelect.addEventListener('change', updateFont);
+
+ const finishButton = document.createElement('button');
+ finishButton.innerHTML = '✔';
+ finishButton.addEventListener('click', clickHandleFinish);
+
+ layerDiv = document.createElement('div');
+
+ layerDiv.appendChild(input);
+ layerDiv.appendChild(fontSelect);
+ layerDiv.appendChild(sizeSelect);
+ layerDiv.appendChild(finishButton);
+ $("#layersdiv").appendChild(layerDiv);
+ input.focus();
+
+ isAddingText = true;
+ //cursor = 'move';
+ blackButton.innerHTML = 'aA'
+ redButton.innerHTML = 'aA'
+ if (color=='white') {
+ whiteButton.classList.remove('active');
+ blackButton.classList.add('active');
+ color='black';
+ }
+ }
+
+ function handleFinish(apply) {
+ canvas.removeEventListener('mousedown', handleMouseDown);
+ canvas.removeEventListener('mouseup', handleMouseUp);
+ canvas.removeEventListener('mousemove', handleMouseMove);
+
+ canvas.removeEventListener('touchstart', handleTouchStart);
+ canvas.removeEventListener('touchend', handleTouchEnd);
+ canvas.removeEventListener('touchmove', handleTouchMove);
+ isAddingText = false;
+ cursor = 'auto';
+ layerDiv.remove();
+ clearInterval(intervalId);
+ showCursor = false;
+ if (apply) drawText(input.value, textX, textY);
+ txtButton.classList.remove('active');
+ blackButton.innerHTML = '﹏🖌'
+ redButton.innerHTML = '﹏🖌'
+ }
+
+ function drawText(text, x, y) {
+ ctx.drawImage(bgCanvas, 0, 0);
+ ctx.save();
+ ctx.translate(x, y);
+ ctx.font = font;
+ ctx.fillStyle = color;
+ const lines = text.split('\n');
+ lines.forEach((line, index) => {
+ ctx.fillText(line + (showCursor && index === lines.length - 1 ? '|' : ''), 0, index * (sizeSelect.value * 1.1));
+ });
+ ctx.restore();
+ }
+
+ function handleMouseDown(e) {
+ isDragging = true;
+ startX = textX;
+ startY = textY;
+ ({ clientX: lastMouseX, clientY: lastMouseY } = e);
+ }
+
+ function handleMouseMove(e) {
+ if (isDragging) {
+ const { clientX, clientY } = e;
+ textX = startX + clientX - lastMouseX;
+ textY = startY + clientY - lastMouseY;
+ drawText(input.value, textX, textY);
+ }
+ }
+
+ function handleTouchStart(e) {
+ isDragging = true;
+ startX = textX;
+ startY = textY;
+ ({ clientX: lastTouchX, clientY: lastTouchY } = e.touches[0]);
+ }
+
+ function handleTouchMove(e) {
+ if (isDragging) {
+ const { clientX, clientY } = e.touches[0];
+ textX = startX + clientX - lastTouchX;
+ textY = startY + clientY - lastTouchY;
+ drawText(input.value, textX, textY);
+ }
+ }
+
+ function handleMouseUp(e) {
+ isDragging = false;
+ }
+
+ function handleTouchEnd(e) {
+ isDragging = false;
+ }
+
+ function clickHandleFinish() {
+ handleFinish(true);
+ }
+
+}
+
+function loadGoogleFonts(fonts) {
+ var link = document.createElement('link');
+ link.rel = 'stylesheet';
+ link.href = 'https://fonts.googleapis.com/css?family=' + fonts.join('|');
+ document.head.appendChild(link);
+}
+
+function dataURLToBlob(dataURL) {
+ const byteString = atob(dataURL.split(',')[1]);
+ const mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0];
+ const arrayBuffer = new ArrayBuffer(byteString.length);
+ const uint8Array = new Uint8Array(arrayBuffer);
+ for (let i = 0; i < byteString.length; i++) {
+ uint8Array[i] = byteString.charCodeAt(i);
+ }
+ return new Blob([arrayBuffer], { type: mimeString });
+}
+
+paintLoaded = true;
diff --git a/ESP32_AP-Flasher/src/contentmanager.cpp b/ESP32_AP-Flasher/src/contentmanager.cpp
index a41e6b1a..3fc16642 100644
--- a/ESP32_AP-Flasher/src/contentmanager.cpp
+++ b/ESP32_AP-Flasher/src/contentmanager.cpp
@@ -107,7 +107,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
case Image:
if (cfgobj["filename"].as() && cfgobj["filename"].as() != "null" && !cfgobj["#fetched"].as()) {
- if (cfgobj["dither"] && cfgobj["dither"].as() == false) imageParams.dither = false;
+ if (cfgobj["dither"] && cfgobj["dither"] == "0") imageParams.dither = false;
jpg2buffer(cfgobj["filename"].as(), filename, imageParams);
if (imageParams.hasRed) imageParams.dataType = DATATYPE_IMG_RAW_2BPP;
if (prepareDataAvail(&filename, imageParams.dataType, mac, cfgobj["timetolive"].as())) {
diff --git a/ESP32_AP-Flasher/src/makeimage.cpp b/ESP32_AP-Flasher/src/makeimage.cpp
index 743729b0..5578e84a 100644
--- a/ESP32_AP-Flasher/src/makeimage.cpp
+++ b/ESP32_AP-Flasher/src/makeimage.cpp
@@ -62,10 +62,31 @@ struct Error {
float b;
};
+// Gamma brightness lookup table
+// gamma = 1.50 steps = 256 range = 0-255
+const uint8_t gamma_lut[256] PROGMEM = {
+ 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4,
+ 4, 4, 5, 5, 6, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11,
+ 11, 12, 12, 13, 14, 14, 15, 15, 16, 16, 17, 18, 18, 19, 20, 20,
+ 21, 21, 22, 23, 23, 24, 25, 26, 26, 27, 28, 28, 29, 30, 31, 31,
+ 32, 33, 34, 34, 35, 36, 37, 37, 38, 39, 40, 41, 41, 42, 43, 44,
+ 45, 46, 46, 47, 48, 49, 50, 51, 52, 53, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 62, 63, 64, 65, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 88, 89, 90,
+ 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 112, 113, 114, 115, 116, 117, 119, 120, 121, 122, 123, 124, 126,
+ 127, 128, 129, 130, 132, 133, 134, 135, 136, 138, 139, 140, 141, 142, 144, 145,
+ 146, 147, 149, 150, 151, 152, 154, 155, 156, 158, 159, 160, 161, 163, 164, 165,
+ 167, 168, 169, 171, 172, 173, 174, 176, 177, 178, 180, 181, 182, 184, 185, 187,
+ 188, 189, 191, 192, 193, 195, 196, 197, 199, 200, 202, 203, 204, 206, 207, 209,
+ 210, 211, 213, 214, 216, 217, 218, 220, 221, 223, 224, 226, 227, 228, 230, 231,
+ 233, 234, 236, 237, 239, 240, 242, 243, 245, 246, 248, 249, 251, 252, 254, 255,
+};
+
uint32_t colorDistance(const Color &c1, const Color &c2, const Error &e1) {
- float r_diff = c1.r + e1.r - c2.r;
- float g_diff = c1.g + e1.g - c2.g;
- float b_diff = c1.b + e1.b - c2.b;
+ float r_diff = gamma_lut[c1.r] + e1.r - gamma_lut[c2.r];
+ float g_diff = gamma_lut[c1.g] + e1.g - gamma_lut[c2.g];
+ float b_diff = gamma_lut[c1.b] + e1.b - gamma_lut[c2.b];
return round(r_diff * r_diff + g_diff * g_diff + b_diff * b_diff);
}
diff --git a/ESP32_AP-Flasher/src/web.cpp b/ESP32_AP-Flasher/src/web.cpp
index 427092b5..19da034a 100644
--- a/ESP32_AP-Flasher/src/web.cpp
+++ b/ESP32_AP-Flasher/src/web.cpp
@@ -365,12 +365,16 @@ void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index
request->_tempFile.close();
if (request->hasParam("mac", true)) {
String dst = request->getParam("mac", true)->value();
+ bool dither = true;
+ if (request->hasParam("dither", true)) {
+ if (request->getParam("dither", true)->value() == "0") dither = false;
+ }
uint8_t mac[6];
if (sscanf(dst.c_str(), "%02X%02X%02X%02X%02X%02X", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) == 6) {
tagRecord *taginfo = nullptr;
taginfo = tagRecord::findByMAC(mac);
if (taginfo != nullptr) {
- taginfo->modeConfigJson = "{\"filename\":\"" + dst + ".jpg\",\"timetolive\":\"0\"}";
+ taginfo->modeConfigJson = "{\"filename\":\"" + dst + ".jpg\",\"timetolive\":\"0\",\"dither\":\"" + String(dither) + "\"}";
taginfo->contentMode = 0;
taginfo->nextupdate = 0;
wsSendTaginfo(mac);
diff --git a/zbs243_AP_FW/packagebinaries.py b/zbs243_AP_FW/packagebinaries.py
new file mode 100644
index 00000000..ba1fd18f
--- /dev/null
+++ b/zbs243_AP_FW/packagebinaries.py
@@ -0,0 +1,57 @@
+import os
+import json
+
+types = {
+ 0x00: "AP_FW_1.54.bin",
+ 0x01: "AP_FW_2.9.bin",
+ 0xF0: "AP_FW_Segmented_UK.bin",
+ 0xFF: "AP_FW_Nodisplay.bin"
+}
+
+binpath = "../binaries/"
+tocmaxsize = 512
+
+toc = []
+output = bytearray(tocmaxsize)
+
+# Read version from main.c
+with open("main.c") as file:
+ lines = file.readlines()
+ version_line = next(line for line in lines if "version" in line and "uint16_t" in line)
+ _, version = version_line.split("= 0x")
+ version = int(version.strip().split(";")[0], 16)
+
+binaries = [file for file in os.listdir(binpath) if file.startswith("AP_FW") and not file.endswith("Pack")]
+for file in binaries:
+ file = file.strip()
+ type_id = -1
+ for typeid, typefile in types.items():
+ if typefile == file:
+ type_id = typeid
+ break
+ if type_id == -1:
+ raise ValueError("We don't recognize filetype <{}>, sorry...".format(file))
+ with open(os.path.join(binpath, file), "rb") as binary_file:
+ binary = binary_file.read()
+ length = len(binary)
+ offset = len(output)
+ subarr = {
+ 'type': type_id,
+ 'version': version,
+ 'name': file,
+ 'offset': offset,
+ 'length': length
+ }
+ toc.append(subarr)
+ output.extend(binary)
+
+jtoc = json.dumps(toc)
+tocsize = len(jtoc)
+if tocsize > tocmaxsize:
+ raise ValueError("TOC is too big! Adjust size and try again.")
+output[:len(jtoc)] = jtoc.encode()
+with open(os.path.join(binpath, "AP_FW_Pack.bin"), "wb") as output_file:
+ output_file.write(bytes(output))
+
+print(toc)
+print("All done.")
From 19a08de207e853340694d19310754118e1ff6a71 Mon Sep 17 00:00:00 2001
From: Nic Limper
Date: Tue, 16 May 2023 16:51:23 +0200
Subject: [PATCH 02/21] re-rendered fonts for uppercase characters in dates
---
ESP32_AP-Flasher/data/fonts/bahnschrift80.vlw | Bin 28768 -> 0 bytes
ESP32_AP-Flasher/data/fonts/calibrib30.vlw | Bin 8628 -> 23633 bytes
ESP32_AP-Flasher/data/fonts/calibrib35.vlw | Bin 11284 -> 0 bytes
ESP32_AP-Flasher/data/fonts/calibrib40.vlw | Bin 14772 -> 0 bytes
ESP32_AP-Flasher/data/fonts/calibrib50.vlw | Bin 22807 -> 62152 bytes
ESP32_AP-Flasher/data/fonts/calibrib62.vlw | Bin 34393 -> 93218 bytes
ESP32_AP-Flasher/data/www/index.html | 2 +-
ESP32_AP-Flasher/data/www/upload-test.html | 2 ++
ESP32_AP-Flasher/include/language.h | 10 +++++-----
ESP32_AP-Flasher/src/newproto.cpp | 3 ++-
10 files changed, 10 insertions(+), 7 deletions(-)
delete mode 100644 ESP32_AP-Flasher/data/fonts/bahnschrift80.vlw
delete mode 100644 ESP32_AP-Flasher/data/fonts/calibrib35.vlw
delete mode 100644 ESP32_AP-Flasher/data/fonts/calibrib40.vlw
diff --git a/ESP32_AP-Flasher/data/fonts/bahnschrift80.vlw b/ESP32_AP-Flasher/data/fonts/bahnschrift80.vlw
deleted file mode 100644
index 84d11fff808788e0df36e34cbc75fdb3166890a1..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 28768
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
EKLX_8djJ3c
diff --git a/ESP32_AP-Flasher/data/fonts/calibrib30.vlw b/ESP32_AP-Flasher/data/fonts/calibrib30.vlw
index f2da8ab37dafca686bcf7db8a3f48b0704f443d0..6c81348999f1945ac3e317b8de97ff888581095c 100644
GIT binary patch
literal 23633
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
T1CKoP@_Qb>@!*Bg-H9Frk>*sk
delta 70
zcmcb(gK>+q1Oo$u3J`Mxu^bSCfCvz?Zr;Nv&A3^b{RY$I3~Rp0eI}fn6-0YDC%@t0
SocxE6W3z+HP2tI=F){$-xDfUL
diff --git a/ESP32_AP-Flasher/data/fonts/calibrib35.vlw b/ESP32_AP-Flasher/data/fonts/calibrib35.vlw
deleted file mode 100644
index f6a1d70d314364aaa465e66f0e6d02b74e9de6a6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 11284
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
JaeKc^?|&%**$w~z
diff --git a/ESP32_AP-Flasher/data/fonts/calibrib50.vlw b/ESP32_AP-Flasher/data/fonts/calibrib50.vlw
index 6c0b0dd1fd3e197c8ce9193855862b4e90c5988d..b2b327fa71a23cd317ffeb0d1792ab0f2a4eee02 100644
GIT binary patch
literal 62152
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
diff --git a/ESP32_AP-Flasher/data/fonts/calibrib62.vlw b/ESP32_AP-Flasher/data/fonts/calibrib62.vlw
index 97ea7ad38cf5cfd10b09571014ee5c99229cf9d4..fb235b10add7fd4d5cad472a4e2873da9e798025 100644
GIT binary patch
literal 93218
zcmeHt3A?3DQdFZNjEuV?u6)A`DhjR$?hA_w;=b>IxQvdXxUav~f1!_KWo1TWWLDKV
z=dOKk_qoq=x-ugoBeU{-&+Y!^nGYX6{Pc$pAHHLJ{^a=B&+i%^%fB){zk7VXG(Nv>
zd<_4#@v)!p8J}M@KEHN+e)IU4?w5>@{d{$Ne#iLy*75o7h`(i=_Vcyz`9tILo5shu
z-!neHXngGFd&lSd#>e{n!1(;m@j=~h58Qmee|$`bI>T%ho9$POkIrHqKQunSe0*$H
z<9_k@*pGRfIGf?S;$w4aoyEUxe16OL8282aU{0Iu*No4X$H!)}-GQ?^{nGLImE&Wx
zm=1TaJ($)$zdBCr^Y@P2=KPK0W4ksV{QSNV*gY_d-4~v}f5f&MtHX1E=N}lDY4IE^
z#$Nv5h|P1y{ougObN6iRxvHn>;A!)lr_FD(XulsGxoOeQV%*^mkN9-IKN6gJqVA6l
z&Sw15_}D%z{?_rapFcJ}_O97G0S?dTj}OeAEoS-3h~fDsMr_XvoSnFzeRp~txIZ;G
zduM*b_}CowZh`yLBerK~AG@pV1<&ZujM#Qzv)cP_F@Anz1eV)#|7ygj`_Ylx&a9Ws
zZ=SfrkB!*s47bxhsQdBAZBDz3a(_1R?;NM~RPN7>-1?!H_WSdp#rvmqKM~xwkJFxm
zaz8n8^Re&HuMch<^XD&&KTT`*`<{q#ho6ev=CtRdo_{fN`>xshsN7$Q{EKngU6lLF
zf!TAgXQ%i3E1|V_5Ie;ze|2E?-8DaZ{#y6fMvgo5=l9p6#^$#>YTe(6I(t6Y=?Le~
z-yDCM_KP_64u5N4mVa%0>>d{5=Wma|a@&c$gBE|w_}I_i86V577j_Nq?*{k9IPIgJ
ze=l;|3*IHXgMWWu_PfHqfA;r^?dDs@$A137_}Co!yAJ*S;lO(xzDNIPaNjjPwlC}j
zb^mx^_Iuax@BZdDzxjX0iJzYy!7m#h`+eR2e)ySzSq*-F+C6ZGpB=HyjpuIf1)lpq
z8L`!U=lIw&)ViM=d9Q=#KOLOanWvpv_s>Rdb+#|;Y=raYpN~K79kV*Szt!RH|6;^8
zo6Yf+h%x8aM{adr8Xx;R$ztsDUyk?($H)FYweR5fjlKQ9aoW$n8Xxnty;v{3`!^#0
z_Hml0a{qedrnh_8-#->(mVXmD=2Y(I2WR`RyV)I#!_NNgh;42>2d(>e8xD29FgVlN
z?;U$iTKDfqZvE`{4(^D$|1dE7{@A->bzd7F?D{{B*nDsYn-}-{Pb0P*&&Xob{paAm
zf1LKaNxAw0-v(|u?qD(Q@ZU%LL*rw2H{SY!`yV6z;qm#l
z@v$1?zGHms=YNh*ufzNDzXtc^@v&KKcV8VJc>eDZ+cU!V$Yuuje}lu$jPsq17jFD~
z_~7S=-hL1xjw|@AFA)sS1S+Vj-+sAPCb1w`^<@@C60Y7F*B`m!+=RJm)40{WOYK8xgM9eTa0ix<_ux)^Js#;i)u&slfPE}2-Qp72i}mnnlMAAz5pE_Jzcd|dtr{BgXEKjYol4&&3#Kb;pq!aFC+kZ`{jK|S;@>@&o(
zb@*7ku@v~A7I=pGyTc6y_K=Ox@cB-5hvnmXVHYuPJ@i(}L`#J{J`pDzGryoyv~GW)
zjfM}N=?y#GzQ5zOKc#iN#fN==M81J%{}z3W#m2X2Z7m2cdVCpOAQk+b#8RnEj1fu+
zXyBwG88^<-N+NS%va0Odn6;NF@RPOjr>xIFvfieWB^KA2AkA~vSLJqv4rfxB-yD$4dK>CaP6Uc`y^VKeB8l#2X{H7FGE57*73&0d6mOM3-a9b)vmj@&Nk8
z*S+3H1z)~CE(zZB)r0d07?vXe@RRTilB>97!L3A4JHn}J%yd@K04(J+$xVj1ymToB
z%OU_gUxuMQf@2#JZRo^F2;?Em`vWJgJFyZWdX11vA95R}cn*J&Xtx
zMbFD{5n;;<)mKy|wT$=-=u}~5oGhC_?nb{-O6Kxf<7`UHgE6j}ism=w|Z)O3PR*ZsTE`d`5WY{Tj
znIV-yC5e@F7(B*L3U6
z3G*e|SkI!}U?4W!N2YkOoC6nIDKfWE*c6H^*SKY*YB0yDP-HRihT~Nhj*K*)A|Ymo
z%EW=Z#WSaj2tytTJe(AuWMPg(dzxvL%T;W~*UcVL@*E>yAM820oLBZb{h!k1Iqv_I
z{oj~&(
zStGDn84(COJNXhEm$bIa1tymf*c?*1L_ONv29n)PgNqA8sV8@#;Ec)4K#~uw_$?YGR~2j4K&l(NaQt~Wx|Q7Q45n*D+LZ`-aiL9SAlu{Dse=Ubwn`o
z>&oj$jNMDz!u5s}T%?Yc^Fh$V0!sZEl{AFGl;crU6v^i3d7Yv(gLBb!W)3lB;fIoo
zUzDj$r9|!`0K>%}Oc3J$5@!-KBD=%0y1TG!8$gZv=KWIXoCQ7Q?c2`S!PCqzwV|VC
z&d+)w{PEt%6u0o6&TsAyzmSiG%gyY$l47mSHxVlGI97WiLUcODt+v#>?S_dhEDW~O
z-H5k7f)}RX!aNPnajVaD951fdrG7+WRq$jQEH!6F?i?cNb78wfRZX?*O>uS13v0|p
zwyRBtxLmZjc{bUI5rV8NAz6>TKvlAU^D
z+mfSt5Np9DwQ;TDn!FVhd0HJ{?0;>`uR@_{lg<(?>;j0`Lhu12?JLMCC>+^kWEhxg
zCPj|KD34WhicPuGr%P=$oz)*#X4k3r0qUP~1oUFljeV3|(8Jgyh~0>}1SMqB_0koV
zK0(dUk7thHax|zJ|93@$rPY#=Oi@PDbDs(-;klcLBz!&nyb4!w^LjWd1A4e);PG@b
zTlFRZKTf&2Fp7$}h3RB^b!|dVMYGW_`pzIRx5|7QRS~+!$qLJnG!5}N)Ja6uB`1o8)zOH|MG;k(oG3yw?6q7m
z$%qPz1gcA((3CR_3YN)aGp&)46_M;lZZ=RGsS8fn49RhVH^AZgm+r7~s46coK@aA2
z8y2Z=;^CbC6~C_=sLZ)))?JH6o`}zuYW^pKzUflxA*$@`7fAzQ*6c{`bl9Yc-K%U&!P-KC!&$Fiby<3$xCRY0`3%0C4&Mu
z&MXve;%9CuSy<~lyF^~%a-6r}`uKU?Yo&ar^ASA5BYT4Hu9Oq?|4!?_xAPM7iC8MN
zuy&}EDs*K*B=If+Xy|0^3m)7!0pS#ChQKlNQn0RqB@&3Opf!Z1qMC(5p%%NMfhC;^
zAZS@`W#A%6Xi$5BN-}1P8Nhc~Aglty`S;dC1+>WEU~yG|8Ts^SL?!|f2=0QpiN1zq
zlMq~e7AFA=uD&p*S1t|qErj0*NbkQFLeINv-sAnoQswVf<~@pcufxcL&OLzelk^BG
zGcqMDUIZT-Xai!LPYlMj=6QGuuLFN#i+$L2%42xh9z%M(?!M*MdqE8vc9p|}!
z6fcf`%KLqtyTv1_&3Yz}x>F+;Ho_uWX+tR4)2TS}VR7bNpgfuavmCLEG-VUNz&Vk=M&5r_r%1v-FC*U0y_;zZhFi8H{%D}#{bOj9Os
zgsjdWMZka{j}tJ?J3Tjmb>MuzYrzfP-}!!bexLjWzC`cu1^&1b<0Zrt_uMz|ne7AD
z9cTGc_J3+z$@AZh({Nacas0Ci$bQd;Qo+Er%I*G_W5~xH5bg?B5^9?eF)ESI#UNQo
z0~(36Ep(Pvu2q7JCM87@yDXKR+mc~grbQt+sRW0HLj#C`aKti5S7LM!?qQ_1WF-S-
z*$csz+?Aq0uAH?ylB|%cv62hBC&Rdqy|j{zJ(1J6k+rmwg*}tQcp-^wNnzK@B*Dc>
z`~^xdB7Lux+Cq}OqrSWQ9^U63_#JrBfM1u=MbY~HAQQQY>;Go45lh_+A<45VN}jTa
z3fq&Xsk>Xtn|WiAVmVtiNQn8*oT?7`s4l-cp~9KoK-3)8;GU}1Q7(9+U5-LAD47J%
z!_+y-MelB&VlX-hFpP4WI!1w!6(hzu4aJQLB_MXcN2zBie9qpO&lI~pa9D5lAaxO=
zQS%1e1_69f_pcIV7hBGSp$MD)1
zGiZFnoy}jReXf8*sjZf$&>{hwwWCt-s1|1f~8ezY1!7E}`LAr!KroQsGf$XVxW_
zH`z|oWhafAoi(WvO-vz343>xt+Ck=`8DW7-BD#Vj5#B=(V=4)|3{;A`V&u;+f@6P{
zZS;_BM#`aJbr(J3GIv(F=!}-7Cza8oL@2-b`a)zKG}ey5=aov5ss=OFHFPj@{(2Ay
z-U2NlG&%bs(E)hFu1cR7b=vYKv9!xJk_n}*DU$Fg>CVjD=`H2s?WsBYi5i7BP%)S&
zS;d)2t`w2fux#Oyc_S2uMvxN{VE_&Q|AQAV5Q*D1pK_0ee}^Ks&US
z6JZ2H0vX(?+(oWev4~?)8>T_n==S#zwW;!mL=C#k@~W8GPPUVtblvoNjJJws(tgN6?EmLKqdYaPCEIFhLu>vk#@eN|q^+AgeZOaM1fSEX*d5tDpA<2xD
zw1q-W8_DO}3^_=#N?Vf2?ntt4g_O%l=(ho2uRG$nCsO~qXVxN9H;CzUVpf8sNwPs5
zY{8~pqh>qPP=al+xAqGMVhA@-j&l
z3vVl5VQ+E5jSUX7=s76`7++>6EANoifSL6)*%A{xF)TI1nUp`)HlV?*MK>^OD;6!3
zFe60!c~(lf#%SFIid&^7+Yv&LR4)@IAC#9Bs*sIQ&rT)x0ER_N&6612>~!XOW+2T#
z@Nai;NpKQE5tL0SIXpDYxXhApj;ol(n5Gpb+zZ}Ap3`$p0!fI=2qfX1m1W#Fsmr);
z7kiUU=^5e37p4F%1{Kv%#;cK
zebWKSTj(=2FjI`2NQpq#87L8Yqp0l6SSdH>U
zkbVNK=W}VeUo)&NGSg;>wn#*c$uu>ISrm8$fi7dE%Zv_Hy{uJLI?3t=d_N1=zPZ48
zuLt$ITD5h{Dy4J4IwJ5~{R66OLt=o10h%Yklo|rb$~v>+T9qThr&j@=J|!tWlgVBo
zbl_06j{Xc)-4vF6hL-QsZiXE9JXAr3<%aBH)gVu9QCD3&J5`}oGfj)yEAiUPFCc6w
z6Wq%->?AfgR%5*Sczmjwc>E!zYM^cf_p&QhHr6dwS7xX*Ju95O0`GJigrhgy%TDYG
zug&7p9OPN5=vI3zi9O*@snQX366vKdWAa31u!DhB
zCBGP;^(#E2fEg5?>@|AFXK}WXzk8Kl%L|({rmJ-Fe)+~G?d3t|qM)BNlBBaf7ba}d
zhkrN2k+c@;ZkEBOkmVlK;-$RPGTo?O*cYXrG?ZtIpz6e_N4SbF;TCC-HFPmV;i@}z@%2Ti}&k64QegnsMBjj(9$4twak9(o=
zOY$1jvTHP52)@AU_X9O6^Lj26SlxrR_
z3qejmh2A!irI5a(G-Vh*u}vXKa5;|E6ZIf1XVlnto0iLHqOmUqcT{G^Sd}Io2ct>E
zvR@(=ph=wI8az^GR@|8#KACGoG^8vh!W=o^KrlqDNhzW*Lz?H!1ji{A9UZxOZ(t*~
z@Fju;7&rhp0q)j`Ly5v<=sknPj}^5bH{bDK1a6keoQZ`AV9c5279qC_D8tkUQl7-0
zAupIGFuGH=fm^px_bF8f$2m8OJgq09y&%CUV*ujWY>1DbQndl^HFbp(%5eLED+9mE
zmYN0g4^;6?Dvg5t^eJp1ug*KK6!*KKf&)|k1Is*<$&gqCqH0tc^lI^L<;Fmrkr_{>
z{J{YVnpFzxXuuV-c;X$L@CurEizb+MDb?0l4|M2Osgtlx>4>)AyG7d*zWH+F2|=3{
z1Ihj9qg9k;R+kAQTQ(C0hA!{~8ywg?P_3B8!7GILLg}4~2Qhb5w}GsDfs1Y`_#}1DhR1E?Q+#s;^U=^P>c~gS_&*1_mOK!Q-eWHmfs^5ym_iWF~VyM+%i9i%eA!
zW@11rf=TV`oZ6UzXto7bP6%laR3L0Zp}-1s1Lpm8id^MDVTU2?d|?dsagw6^e
z;Z;tw8jdYV{@G6
zv?-F4P_#7u%GI<&2@4~E`PdU#mfwZjx#<8xc89!{!eFoWPd~A8F83GKd=H2vCAN
zYlFFgV7p}{WXe4(f$c0oQdGtiLbE+FN-7~~Ba%50!2MEX=Ah=dI&+xPn-=%rQ5(nw
zk7!vswq2e;3QZpd+1!9MN*r26RI2#Yo&dO$Ux56DMa?lN#WH&o!LQMoD*pSFwdf&i(1K0q+78V5Pm?RL7v&w{+6l)4$6qrT~
zYC!-LM*5Z-pS4K6K8Opn#t5c&x1k8yvW=>PZ
zlCerKS}JD4WG-P`mYh&=8zKyHK`0}I8toRMq^M?ja)59q^RBCKojiFlKyp8D8|uRI
z@)s91%R5HGbdr>CbS`-&HWM-i6PVMIN)^6HLV=tmY6u|DQkAep@0yC~>y2s__p6{~
zxOi;V(z(HB5ri`PX{F$QDY0-L!&na=A7C(21tS%g99
z0T0C@{#2>hsor>A6}uh;uI^{g^VO-khiBGGnzhCA>rG?K3;;Pj?tpLL`hsFpi;$~y
zrrN0*G0!1o%-{fm7kgGaW`D{rjs%^U23t^m3r>sL%1eORNfQMVt|-)dwB3L2Oq3Va
zA&jNDNE0m}v|F(H($xaj7pNz&F8B`2(Nb#unazkJ5+@>J!JvB`2;E-qw0oi3>)i3v
z>4@*4tZ+i{W@D{>&gI?)@qtWvJQXbsNS_>NQq^S8cjmL)0bYzeH!oC>&ZJWQS!9$D
z!YFESkxddvbuei%h-QJks~*JnDH4Bx!W5ZLUrRd%BjR)bBi|Fu@JNc~YzCuj`Y@_H
zrr+XtBUK%>s5#zD!ExR!s&Gp&kx-TLd6f456hJtJYSd
z9^gFH@3E9RR=?udsVpONya^Q}DD&@rQKq;_^EuIE)q>hEgg7UIpt9jn%OO@Rkh~*Y
zz~ps-3`K;i7Og}eWm!4FD1U&qcu5^665lO7Te?Vjjd3}@$`vid=IbUa!VN|jGY+kY
zgPa3WFiR(sP>H6z`xZs>&5ll}wo9SKZwYG#4Nk-6PXM7+h&7=c2dd|}(Wa7GFL46B
zvOEel#0FZ$E5e7iblhkIS{V>B1Ry^*#0FZ7VUk)PJV6n_(&c7HO>TWDPYU048V@ZT
z!9_k@z`13~kjQtAV}i4?h~0r{eoI)STr0Xrp@TL>x5g2+0Qz|rrgAq^7Yqx9MNi|(
zT%v?S5%0hm$>bftohe~@*_kR#Ejv)-0*w&>Ox}_R0$gOA0ZFis(4?VB;DvK38?WP;
z%lfr0x$G!T5_)X_mR#aGS+pggPw~ev(yn*hyLe$OtHU%-$!5vX%(Ry&a^x;O6>@&@
z_!Cw*l?)7rQW$Sn#z~zlWtD4GG3}g^7r2Fzjt}I}aZ?5guT7C`_vMNU)Fn%l?b?AW
zBSdn6yFgu<1YGR~Gd-bHjIZFcf-(zy1XBfN+K&Nsl#m2EfWextu|H48
zHy#bxLYRl#sS>_4;
d{PK_f%s0ONv%m25FMj0bzwvV)ko~~S{y$NCI>G<|
delta 85
zcmZ24JFxB(xRJ
diff --git a/ESP32_AP-Flasher/data/www/index.html b/ESP32_AP-Flasher/data/www/index.html
index b6f07e69..ea9c7520 100644
--- a/ESP32_AP-Flasher/data/www/index.html
+++ b/ESP32_AP-Flasher/data/www/index.html
@@ -81,7 +81,7 @@
diff --git a/ESP32_AP-Flasher/data/www/upload-test.html b/ESP32_AP-Flasher/data/www/upload-test.html
index dc460b2f..a21e671a 100644
--- a/ESP32_AP-Flasher/data/www/upload-test.html
+++ b/ESP32_AP-Flasher/data/www/upload-test.html
@@ -10,6 +10,8 @@