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 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 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 @@


+
+


diff --git a/ESP32_AP-Flasher/include/language.h b/ESP32_AP-Flasher/include/language.h index c09587e6..a26b1dba 100644 --- a/ESP32_AP-Flasher/include/language.h +++ b/ESP32_AP-Flasher/include/language.h @@ -4,12 +4,12 @@ static int defaultLanguage = 0; -static String languageList[] = {"EN - English", "NL - Dutch", "DE - Deutsch"}; +static String languageList[] = {"EN - English", "NL - Nederlands", "DE - Deutsch"}; /*EN English language section*/ static String languageEnDaysShort[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"}; -static String languageEnDays[] = {"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"}; -static String languageEnMonth[] = {"january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"}; +static String languageEnDays[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; +static String languageEnMonth[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; /*END English language section END*/ /*NL Dutch language section*/ @@ -20,8 +20,8 @@ static String languageNlMonth[] = {"januari", "februari", "maart", "april", "mei /*DE German language section*/ static String languageDeDaysShort[] = {"SO", "MO", "DI", "MI", "DO", "FR", "SA"}; -static String languageDeDays[] = {"sonntag", "montag", "dienstag", "mittwoch", "donnerstag", "freitag", "samstag"}; -static String languageDeMonth[] = {"januar", "februar", "maerz", "april", "mai", "juni", "juli", "august", "september", "oktober", "november", "dezember"}; +static String languageDeDays[] = {"Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"}; +static String languageDeMonth[] = {"Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"}; /*END German language section END*/ static String* languageDaysShort[] = {languageEnDaysShort, languageNlDaysShort, languageDeDaysShort}; diff --git a/ESP32_AP-Flasher/src/newproto.cpp b/ESP32_AP-Flasher/src/newproto.cpp index baf4f40a..11621352 100644 --- a/ESP32_AP-Flasher/src/newproto.cpp +++ b/ESP32_AP-Flasher/src/newproto.cpp @@ -87,7 +87,8 @@ void prepareIdleReq(uint8_t* dst, uint16_t nextCheckin) { bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t nextCheckin) { if (nextCheckin > MIN_RESPONSE_TIME) nextCheckin = MIN_RESPONSE_TIME; - + if (wsClientCount()) nextCheckin=0; + uint8_t src[8]; *((uint64_t*)src) = swap64(*((uint64_t*)dst)); uint8_t mac[6]; From 2746e9d1142877c0b3f8181b75601a8f3aaf2011 Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Tue, 16 May 2023 19:41:24 +0200 Subject: [PATCH 03/21] Update readme and added build Information --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c1275935..8a9eea96 100644 --- a/README.md +++ b/README.md @@ -23,11 +23,11 @@ On the 2.9" tags, both the UC8151 and SSD1619 display variants are supported - High transfer speeds - It can do about 5kbyte/s in favorable RF conditions. This allows for lower power - RF-friendly - We don't need to acknowledge EVERY packet, and we don't need to transfer data we already have -The entire setup requires a few tags, and an ESP32. A (preferably, but not necessarily) broken tag is used as an 802.15.4 radio for the ESP32. You'll need a ZBS_Flasher in order to flash both the AP with its firmware, and the tags. Using the 'mac' option on ZBS_Flasher makes sure a tag flashed with a custom firmware has a valid mac address; it used the stock mac address assigned to the tag if it hasn't been flashed before. If you want to set it yourself, you can edit the mac address in the infopage. The AP expects a tag with a mac that starts with 00:00, followed by 6 bytes. The MAC-address also needs to be set on the AP-tag. +The entire setup requires a few tags, and an ESP32. A tag is used as an 802.15.4 radio for the ESP32. You'll need a ZBS_Flasher in order to flash the tags. Using the 'mac' option on ZBS_Flasher makes sure a tag flashed with a custom firmware has a valid mac address; it used the stock mac address assigned to the tag if it hasn't been flashed before. If you want to set it yourself, you can edit the mac address in the infopage. The AP expects a tag with a mac that starts with 00:00, followed by 6 bytes. -Once flashed, you can hook the AP tag up to the ESP32 by connecting the tags serial lines to some free pins. Make sure you set the pins in settings.h, so that the ESP32 can communicate with it. This can be validated by checking the ESP32 debug output; you should see 'sync burst' displayed every 30 seconds +You can hook the AP tag up to the ESP32 with mod wires or a flex pcb. The esp will flash the AP fitmware to the Tag automatically. In some case, a power off on cicle is reqired. Please check the serial console output for status information. -You can access the ESP32 with any web browser after connecting it to your WiFi Network. The file browser is located at /edit. For sending data to tags, you'll need to upload the information in 'data' to the ESP32's filesystem. After uploading, you can access the status screen at /index.html. If everything is working, you should be able to see tags synchronising to the network. After uploading a suitable .bmp file to the filesystem, this file can be sent to the tag by entering it's 6-byte mac address and filename. +You can access the ESP32 with any web browser after connecting it to your WiFi Network. The file browser is located at /edit. For sending data to tags, you'll need to upload the information in 'data' to the ESP32's filesystem or over HTTP. After uploading, you can access the status screen at /index.html. If everything is working, you should be able to see tags synchronising to the network. After uploading a suitable .bmp file to the filesystem, this file can be sent to the tag by entering it's 6-byte mac address and filename. ## The protocol explained - The tag checks in with the AP every 40+ seconds. Actual check-in interval is highly dependent on RF conditions @@ -71,3 +71,9 @@ You can access the ESP32 with any web browser after connecting it to your WiFi N - atc1441 Hats off to these legends! + +## Automated Builds +- After a PR gets merged to the main branch, the esp 32 code will automatically be compiled. This can take up to 20 minutes. +- Information about the latest builds can be found below +builds + From a5165eb946427e0347e120099b7e3fa92d6144c1 Mon Sep 17 00:00:00 2001 From: Nic Limper Date: Tue, 16 May 2023 22:16:47 +0200 Subject: [PATCH 04/21] delete temporary files --- ESP32_AP-Flasher/src/contentmanager.cpp | 1 + ESP32_AP-Flasher/src/newproto.cpp | 3 +++ ESP32_AP-Flasher/src/web.cpp | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ESP32_AP-Flasher/src/contentmanager.cpp b/ESP32_AP-Flasher/src/contentmanager.cpp index 3fc16642..17ea8ecd 100644 --- a/ESP32_AP-Flasher/src/contentmanager.cpp +++ b/ESP32_AP-Flasher/src/contentmanager.cpp @@ -112,6 +112,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { if (imageParams.hasRed) imageParams.dataType = DATATYPE_IMG_RAW_2BPP; if (prepareDataAvail(&filename, imageParams.dataType, mac, cfgobj["timetolive"].as())) { cfgobj["#fetched"] = true; + if (cfgobj["delete"].as()) LittleFS.remove("/"+cfgobj["filename"].as()); } else { wsErr("Error accessing " + filename); } diff --git a/ESP32_AP-Flasher/src/newproto.cpp b/ESP32_AP-Flasher/src/newproto.cpp index 11621352..d18aa0f4 100644 --- a/ESP32_AP-Flasher/src/newproto.cpp +++ b/ESP32_AP-Flasher/src/newproto.cpp @@ -132,6 +132,9 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t if (memcmp(md5bytes, taginfo->md5pending, 16) == 0) { wsLog("new image is the same as current or already pending image. not updating tag."); wsSendTaginfo(mac); + if (LittleFS.exists(*filename)) { + LittleFS.remove(*filename); + } return true; } diff --git a/ESP32_AP-Flasher/src/web.cpp b/ESP32_AP-Flasher/src/web.cpp index 19da034a..45320980 100644 --- a/ESP32_AP-Flasher/src/web.cpp +++ b/ESP32_AP-Flasher/src/web.cpp @@ -374,7 +374,7 @@ void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index tagRecord *taginfo = nullptr; taginfo = tagRecord::findByMAC(mac); if (taginfo != nullptr) { - taginfo->modeConfigJson = "{\"filename\":\"" + dst + ".jpg\",\"timetolive\":\"0\",\"dither\":\"" + String(dither) + "\"}"; + taginfo->modeConfigJson = "{\"filename\":\"" + dst + ".jpg\",\"timetolive\":\"0\",\"dither\":\"" + String(dither) + "\",\"delete\":\"1\"}"; taginfo->contentMode = 0; taginfo->nextupdate = 0; wsSendTaginfo(mac); From ae4311e34616ab65005aa70d6a3bbca82daf4724 Mon Sep 17 00:00:00 2001 From: Nic Limper Date: Tue, 16 May 2023 22:53:19 +0200 Subject: [PATCH 05/21] small typos --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8a9eea96..438a70fa 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,9 @@ On the 2.9" tags, both the UC8151 and SSD1619 display variants are supported The entire setup requires a few tags, and an ESP32. A tag is used as an 802.15.4 radio for the ESP32. You'll need a ZBS_Flasher in order to flash the tags. Using the 'mac' option on ZBS_Flasher makes sure a tag flashed with a custom firmware has a valid mac address; it used the stock mac address assigned to the tag if it hasn't been flashed before. If you want to set it yourself, you can edit the mac address in the infopage. The AP expects a tag with a mac that starts with 00:00, followed by 6 bytes. -You can hook the AP tag up to the ESP32 with mod wires or a flex pcb. The esp will flash the AP fitmware to the Tag automatically. In some case, a power off on cicle is reqired. Please check the serial console output for status information. +You can hook the AP tag up to the ESP32 with mod wires or a flex pcb. The esp will flash the AP firmware to the Tag automatically. In some cases, a power off/on cycle is required. Please check the serial console output for status information. -You can access the ESP32 with any web browser after connecting it to your WiFi Network. The file browser is located at /edit. For sending data to tags, you'll need to upload the information in 'data' to the ESP32's filesystem or over HTTP. After uploading, you can access the status screen at /index.html. If everything is working, you should be able to see tags synchronising to the network. After uploading a suitable .bmp file to the filesystem, this file can be sent to the tag by entering it's 6-byte mac address and filename. +You can access the ESP32 with any web browser after connecting it to your WiFi Network. The file browser is located at /edit. For sending data to tags, you'll need to upload the information in 'data' to the ESP32's filesystem or over HTTP. After uploading, you can access the status screen at /index.html. If everything is working, you should be able to see tags synchronising to the network. After uploading a suitable .jpg file to the filesystem, this file can be sent to the tag by entering it's 6-byte mac address and filename. ## The protocol explained - The tag checks in with the AP every 40+ seconds. Actual check-in interval is highly dependent on RF conditions @@ -51,12 +51,11 @@ You can access the ESP32 with any web browser after connecting it to your WiFi N ### AP: - Important! The AP needs to be able to tell a tag to try again later if it's already doing comms with another tag. The AP can't handle concurrent checkins/download due to memory constraints! - More reliable serial comms (sometimes bytes are dropped) -- Include source mac with blockrequest struct ### ESP32: - Do more with status info as sent by the tags ## Known issues: -- Some tags work better as AP's than others. Your range may suck. The boards on these tags are tiny and fragile. For instance, a dab of hot-glue on a board is enough to warp it pretty severely, and will damage the components that are soldered on there. Reportedly, segmented-display solum tags work well. +- Some tags work better as AP's than others. Your range may suck. The boards on these tags are tiny and fragile. For instance, a dab of hot-glue on a board is enough to warp it pretty severely, and will damage the components that are soldered on there. Reportedly, segmented-display Solum tags work well. ## Hints and excuses: - I'm sorry if reading this spaghetti code makes you lose your mind. 'Of all the things I've lost, I miss my mind the most' I know it is pretty unreadable. I could blame SDCC for a lot of things, but it's mostly me. @@ -73,7 +72,7 @@ You can access the ESP32 with any web browser after connecting it to your WiFi N Hats off to these legends! ## Automated Builds -- After a PR gets merged to the main branch, the esp 32 code will automatically be compiled. This can take up to 20 minutes. +- After a PR gets merged to the main branch, the ESP32 code will automatically be compiled. This can take up to 20 minutes. - Information about the latest builds can be found below builds From 61ee71353991246481436b7836def15a2f880a59 Mon Sep 17 00:00:00 2001 From: Jelmer Date: Wed, 17 May 2023 01:44:37 +0200 Subject: [PATCH 06/21] Create Getting Started.md --- .../OpenEPaperLink Mini AP/Getting Started.md | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 Hardware/OpenEPaperLink Mini AP/Getting Started.md diff --git a/Hardware/OpenEPaperLink Mini AP/Getting Started.md b/Hardware/OpenEPaperLink Mini AP/Getting Started.md new file mode 100644 index 00000000..d9027c97 --- /dev/null +++ b/Hardware/OpenEPaperLink Mini AP/Getting Started.md @@ -0,0 +1,75 @@ +# Getting started with the OpenEPaperLink and the Mini-AP # + +So you've made or bought yourself an AP and a few tags! Cool! Here's how to get started with OpenEPaperLink. We'll explain the hardware features of the Mini-AP, if you built one yourself, you'll probably be familiar enough with that hardware. This guide expects a fully flashed AP, both the ESP32 and the AP-tag inside. Also, this only works with tags running OpenEPaperLink firmware. If this is not the case, this guide will be a less-than-awesome place to start setting up OpenEPaperLink + +## Hardware ## + + +That is all! + +Now I can hear you think: 'Geewhizz mister, you're lazy!', and you're not wrong... Okay fine I'll explain a little further. + +This thing: +- is USB-C powered (5V, about 120mA or 0.6 watt) +- Powered by an ESP32-S2 (with PSRAM) +- Uses an upcycled Solum pricetag as display and radio +- Is actually multi-CPU! Neato! +- Has a WS2812B RGB Led (for the haters: don't worry, you can disable it easily) +- Has two hardware switches; one to enter usb-download mode, (GPIO 0) and one to reset the ESP32. You can reach them with a paperclip +- Uses an E-Paper display for telling you some Accesspoint status information. Who doesn't like E-Paper! + +The MiniAP uses an ESP32 microcontroller and pricetag to work as an accesspoint for OpenEPaperLink displays that support 802.15.4 (Zigbee-esque) packets. + +## Software ## + +This thing runs software from this repo: +- ESP32_AP-Flasher firmware on the ESP32 +- zbs243_AP_FW on the segmented tag-AP + +The AP can be accessed with a nice web-interface that lets you select data for the tags to display. The ESP32 connects to your network through WiFi. + +## AP Setup ## +Alright, let's get this show on the road. We'll start with the AP first, and then connect some tags to it in the next part. +* Connect your AP to a power source. This can be a computer, of course, but you can also use any 5V wall-wart you have laying around. The power consumption isn't incredibly high. +* The tag will start, and the RGB led should blink blue-green-blue. This indicates it is waiting for a wifi connection +* You'll need to set up the WiFi connection for the AP. The ESP32 inside uses the awesome WiFiManager project to set up an wifi-accesspoint that you can use to easily configure wifi +* Use your computer, phone, tablet, whatever, and connect to the 'OpenEPaperLink Setup' WiFi Network. + + + +* Usually, a browser will pop-up, and presents you a 'captive portal'. If it doesn't, simpy remain connected to the OpenEPaperLink wifi network and point your browser to http://192.168.4.1 + + + +* Click or tap on 'Configure Wifi' - this will start a scan for WiFi networks +* You should get a list of WiFi networks the AP can receive. Click on your WiFi network, enter your password, and hit 'save'. + + + +* After the AP has been successfully connected to the network, the LED on the back should be green, and will fade in and out. +* The 802.15.4/ZigBee-ish radio will now start. You can see some stuff appear on the ePaper screen +* After the 'Boot'-message, the screen will show you the AP's IP address + + + +* Due to the limited characters, the screen will show the IP address in two parts. Remember this IP address, you're going to need it... +* ... here. Connect your device back to the same network as you've just connected the AP to, and point your browser to the IP address. Forgot it already? It happens. Simply restart your AP, and it'll show the IP address again. +* You should see an awesome web interface! If you want to kill the RGB Led, now is the first convenient moment to do it. Hit up 'AP config' and check out the config there. All kinds of cool features! +* If you're aware of a zigbee network at home, you can select a different channel in the AP config. As most channels have some kind of overlap with WiFi, we've only made some channels available with little or no overlap. Now would also be a good time to select a channel if you change it, because if you change it with tags already associated, it may take them up to an hour-ish to reconnect to the new channel. + +## Adding Tags ## +* Take a (programmed) tag, and insert the batteries. This may sound easier than it is, in reality. See, these tags are cheap. They don't reset automatically when the voltage drops; if you replace batteries, you'll sometimes need to reset them (by shortening out the contacts), and you want to insert the batteries in a swift motion, so that they make contact and stay connected. You can use the battery cover to pop the batteries in, as shown in the video below. + +https://github.com/jjwbruijn/OpenEPaperLink/assets/2544995/e4c693f8-b018-4b83-94df-399dc285618a + + +* If you want to make sure you'll reset the tag properly, the easiest way to drain the internal capacitors is to shorten the them using a battery. A battery inserted in reverse will shorten the contacts. On tags with multiple batteries: don't keep batteries in the bay with one battery shorting out the contacts; they're wired in parallel. +* Now that you've successfully powered on your tag, it's time to see if it's showing up on the AP-webinterface. A few seconds after the 'Waiting for data...' screen is shown on a tag, it should show up on the accesspoint. +* Select the tag in the webinterface, choose some content, and the tag should update the next time it checks in! + +## Signal strength ## +The AP has a range of up to 25 meters line of sight, but since there is some overlap with WiFi channels, the coexistence of WiFi can reduce the system's range. Also, don't expect these signals to penetrate concrete (rebar) floors or walls very well. + +## Further reading ## +* [Troubleshooting tags](https://github.com/jjwbruijn/OpenEPaperLink/blob/master/Tags-specs/troubleshooting.md) +* [How to build a Mini AP](https://github.com/jjwbruijn/OpenEPaperLink/tree/master/Hardware/OpenEPaperLink%20Mini%20AP/README.md) From 2b3f066568fa9c1486036e88f82aa9fdfdea15ef Mon Sep 17 00:00:00 2001 From: Jelmer Date: Wed, 17 May 2023 01:45:22 +0200 Subject: [PATCH 07/21] Rename README.md to Building.md --- Hardware/OpenEPaperLink Mini AP/{README.md => Building.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Hardware/OpenEPaperLink Mini AP/{README.md => Building.md} (100%) diff --git a/Hardware/OpenEPaperLink Mini AP/README.md b/Hardware/OpenEPaperLink Mini AP/Building.md similarity index 100% rename from Hardware/OpenEPaperLink Mini AP/README.md rename to Hardware/OpenEPaperLink Mini AP/Building.md From 15bcba19f63390d1f3d47736a13f5893acd59db9 Mon Sep 17 00:00:00 2001 From: Jelmer Date: Wed, 17 May 2023 01:59:27 +0200 Subject: [PATCH 08/21] Create README.md --- Hardware/OpenEPaperLink Mini AP/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 Hardware/OpenEPaperLink Mini AP/README.md diff --git a/Hardware/OpenEPaperLink Mini AP/README.md b/Hardware/OpenEPaperLink Mini AP/README.md new file mode 100644 index 00000000..26d140ad --- /dev/null +++ b/Hardware/OpenEPaperLink Mini AP/README.md @@ -0,0 +1,15 @@ +# OpenEPaperLink Mini-AP # +![image](https://github.com/jjwbruijn/OpenEPaperLink/assets/2544995/d5e4c583-11e7-4d83-ba36-a225ad200cd5) + +A small OpenEPaperLink AP, using an ESP32-S2 and a segmented tag + +* [How to build one](https://github.com/jjwbruijn/OpenEPaperLink/blob/master/Hardware/OpenEPaperLink%20Mini%20AP/Building.md) + +* [How to use one](https://github.com/jjwbruijn/OpenEPaperLink/blob/master/Hardware/OpenEPaperLink%20Mini%20AP/Getting%20Started.md) + + + +* [Case](https://github.com/jjwbruijn/OpenEPaperLink/tree/master/Hardware/OpenEPaperLink%20Mini%20AP/Case) + +* [PCB](https://github.com/jjwbruijn/OpenEPaperLink/tree/master/Hardware/OpenEPaperLink%20Mini%20AP/PCB) + From 437c4fdcc02f9d935857399aae40af94ba372ca1 Mon Sep 17 00:00:00 2001 From: atc1441 Date: Wed, 17 May 2023 10:25:51 +0200 Subject: [PATCH 09/21] Date info added on Google Calendar content --- ESP32_AP-Flasher/platformio.ini | 4 ++-- ESP32_AP-Flasher/src/contentmanager.cpp | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ESP32_AP-Flasher/platformio.ini b/ESP32_AP-Flasher/platformio.ini index 25808b37..65196df1 100644 --- a/ESP32_AP-Flasher/platformio.ini +++ b/ESP32_AP-Flasher/platformio.ini @@ -26,8 +26,8 @@ board_build.filesystem = littlefs monitor_filters = esp32_exception_decoder monitor_speed = 115200 board_build.f_cpu = 240000000L -;upload_port = COM30 -;monitor_port = COM30 +upload_port = COM11 +monitor_port = COM11 [env:OpenEPaperLink_Mini_AP] diff --git a/ESP32_AP-Flasher/src/contentmanager.cpp b/ESP32_AP-Flasher/src/contentmanager.cpp index 17ea8ecd..db5c83b6 100644 --- a/ESP32_AP-Flasher/src/contentmanager.cpp +++ b/ESP32_AP-Flasher/src/contentmanager.cpp @@ -815,6 +815,8 @@ bool getCalFeed(String &filename, String URL, String title, tagRecord *&taginfo, time(&now); struct tm timeinfo; localtime_r(&now, &timeinfo); + static char dateString[40]; + strftime(dateString, sizeof(dateString), " - %d.%m.%Y", &timeinfo); HTTPClient http; http.begin(URL); @@ -849,6 +851,7 @@ bool getCalFeed(String &filename, String URL, String title, tagRecord *&taginfo, u8f.setBackgroundColor(PAL_WHITE); u8f.setCursor(5, 16); u8f.print(title); + u8f.print(dateString); int n = doc.size(); if (n > 7) n = 7; @@ -884,6 +887,7 @@ bool getCalFeed(String &filename, String URL, String title, tagRecord *&taginfo, u8f.setBackgroundColor(PAL_WHITE); u8f.setCursor(5, 16); u8f.print(title); + u8f.print(dateString); int n = doc.size(); if (n > 8) n = 8; From f1ed22d131af2c6599dabfbba41565f18a869cf4 Mon Sep 17 00:00:00 2001 From: atc1441 Date: Wed, 17 May 2023 10:29:33 +0200 Subject: [PATCH 10/21] Undo port settings --- ESP32_AP-Flasher/platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ESP32_AP-Flasher/platformio.ini b/ESP32_AP-Flasher/platformio.ini index 65196df1..25808b37 100644 --- a/ESP32_AP-Flasher/platformio.ini +++ b/ESP32_AP-Flasher/platformio.ini @@ -26,8 +26,8 @@ board_build.filesystem = littlefs monitor_filters = esp32_exception_decoder monitor_speed = 115200 board_build.f_cpu = 240000000L -upload_port = COM11 -monitor_port = COM11 +;upload_port = COM30 +;monitor_port = COM30 [env:OpenEPaperLink_Mini_AP] From c7e7b49d494885335e41cb73917c0954e719e4be Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Wed, 17 May 2023 20:31:04 +0200 Subject: [PATCH 11/21] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 438a70fa..72698dc9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # OpenEPaperLink + ## ⚠️⚠️⚠️THIS IS NOT PRODUCTION READY!⚠️⚠️⚠️ This is not a final, polished codebase. Not by a long shot. You'll need some knowledge on the use of these tags. A very good place to start is here: https://github.com/atc1441/ZBS_Flasher. You'll need to fix issues yourself, troubleshoot stuff. Once again: this is not for everyone. From 03647ada445b5dc9c00830cc1d03ce018e42b911 Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Wed, 17 May 2023 20:42:21 +0200 Subject: [PATCH 12/21] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 72698dc9..438a70fa 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ # OpenEPaperLink - ## ⚠️⚠️⚠️THIS IS NOT PRODUCTION READY!⚠️⚠️⚠️ This is not a final, polished codebase. Not by a long shot. You'll need some knowledge on the use of these tags. A very good place to start is here: https://github.com/atc1441/ZBS_Flasher. You'll need to fix issues yourself, troubleshoot stuff. Once again: this is not for everyone. From bf6021d6cddab35dd7078066285c05ce5bb3ed46 Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Wed, 17 May 2023 21:04:04 +0200 Subject: [PATCH 13/21] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 438a70fa..626e9f18 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ This is an alternative firmware and protocol for the ZBS243-based Electronic She board + ### Compatibility It is currently compatible with the following tags: * 4.2" From 59f44e4a829abaaf4faeff996929bf7ba423bfa7 Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Wed, 17 May 2023 21:22:03 +0200 Subject: [PATCH 14/21] Update main.cpp --- ESP32_AP-Flasher/src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ESP32_AP-Flasher/src/main.cpp b/ESP32_AP-Flasher/src/main.cpp index 495ee4b8..2b2fed49 100644 --- a/ESP32_AP-Flasher/src/main.cpp +++ b/ESP32_AP-Flasher/src/main.cpp @@ -2,7 +2,7 @@ #include #include #include - +iojddc #include "contentmanager.h" #include "flasher.h" #include "makeimage.h" @@ -133,4 +133,4 @@ void loop() { while (1) { vTaskDelay(10000 / portTICK_PERIOD_MS); } -} \ No newline at end of file +} From 0646e85a70ad8f5892151e5ebbd5548e51ac3da2 Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Wed, 17 May 2023 21:36:27 +0200 Subject: [PATCH 15/21] Update main.cpp --- ESP32_AP-Flasher/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ESP32_AP-Flasher/src/main.cpp b/ESP32_AP-Flasher/src/main.cpp index 2b2fed49..d0138fff 100644 --- a/ESP32_AP-Flasher/src/main.cpp +++ b/ESP32_AP-Flasher/src/main.cpp @@ -2,7 +2,7 @@ #include #include #include -iojddc + #include "contentmanager.h" #include "flasher.h" #include "makeimage.h" From 1a9bf422eae6de7e46e392e8ae02188ed18c527e Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Wed, 17 May 2023 22:01:43 +0200 Subject: [PATCH 16/21] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 626e9f18..438a70fa 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,6 @@ This is an alternative firmware and protocol for the ZBS243-based Electronic She board - ### Compatibility It is currently compatible with the following tags: * 4.2" From 484bb00341a6ac1209f64d2204cfa2c1a8f03db3 Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Wed, 17 May 2023 23:12:32 +0200 Subject: [PATCH 17/21] Update platformio.ini --- ESP32_AP-Flasher/platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ESP32_AP-Flasher/platformio.ini b/ESP32_AP-Flasher/platformio.ini index 25808b37..e7d172f8 100644 --- a/ESP32_AP-Flasher/platformio.ini +++ b/ESP32_AP-Flasher/platformio.ini @@ -134,7 +134,7 @@ board_upload.flash_size = 16MB [env:Simple_AP] board = esp32dev -board_build.partitions = no_ota.csv +board_build.partitions = default.csv build_flags = -DCORE_DEBUG_LEVEL=0 From 755d676f076cc20d644281310f0ab85aded5af63 Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Thu, 18 May 2023 09:01:12 +0200 Subject: [PATCH 18/21] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 438a70fa..72698dc9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # OpenEPaperLink + ## ⚠️⚠️⚠️THIS IS NOT PRODUCTION READY!⚠️⚠️⚠️ This is not a final, polished codebase. Not by a long shot. You'll need some knowledge on the use of these tags. A very good place to start is here: https://github.com/atc1441/ZBS_Flasher. You'll need to fix issues yourself, troubleshoot stuff. Once again: this is not for everyone. From 5aa71746008b6739b717eb698cb32ae0adbc3c68 Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Thu, 18 May 2023 09:28:46 +0200 Subject: [PATCH 19/21] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 72698dc9..7a4ae6a6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # OpenEPaperLink + ## ⚠️⚠️⚠️THIS IS NOT PRODUCTION READY!⚠️⚠️⚠️ This is not a final, polished codebase. Not by a long shot. You'll need some knowledge on the use of these tags. A very good place to start is here: https://github.com/atc1441/ZBS_Flasher. You'll need to fix issues yourself, troubleshoot stuff. Once again: this is not for everyone. From c52cb89be423e41494621d37cbd2e4b1d8c69a0e Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Thu, 18 May 2023 09:55:00 +0200 Subject: [PATCH 20/21] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 7a4ae6a6..438a70fa 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # OpenEPaperLink - - ## ⚠️⚠️⚠️THIS IS NOT PRODUCTION READY!⚠️⚠️⚠️ This is not a final, polished codebase. Not by a long shot. You'll need some knowledge on the use of these tags. A very good place to start is here: https://github.com/atc1441/ZBS_Flasher. You'll need to fix issues yourself, troubleshoot stuff. Once again: this is not for everyone. From 321d0111b5313863414fddab67dade0ff75a792a Mon Sep 17 00:00:00 2001 From: Jonas Niesner Date: Thu, 18 May 2023 12:38:46 +0200 Subject: [PATCH 21/21] Update main.cpp