Currently active tags:
+
⬤ loading
AP config
diff --git a/ESP32_AP-Flasher/data/www/main.css b/ESP32_AP-Flasher/data/www/main.css
index ef22168f..2657d249 100644
--- a/ESP32_AP-Flasher/data/www/main.css
+++ b/ESP32_AP-Flasher/data/www/main.css
@@ -70,6 +70,7 @@ label {
text-decoration: none;
color: black;
cursor: pointer;
+ white-space: nowrap;
}
.columns div {
@@ -112,8 +113,8 @@ select {
#configbox, #apconfigbox, #apupdatebox {
display: none;
position: fixed;
- top: 80px;
- left: 50px;
+ top: 65px;
+ left: 15px;
width: 380px;
padding: 15px;
background-color: #f0e6d3;
@@ -168,7 +169,10 @@ select {
}
#apupdatebox {
- background-color: #f0e0d0;
+ background-color: #f0d0c8;
+ width: 700px;
+ padding-bottom: 20px;
+ border: 1px solid #d0b0a8;
}
#cfgdelete {
@@ -414,6 +418,54 @@ ul.messages li.new {
border: solid 1px #666666;
}
+/* updatescreens */
+
+#releasetable {
+ margin: 10px 0px;
+}
+
+#releasetable table {
+ border-spacing: 1px;
+}
+
+#releasetable th {
+ text-align: left;
+ background-color: #ffffff;
+ padding: 1px 5px;
+}
+
+#releasetable td {
+ background-color: #ffffff;
+ padding: 1px 5px;
+ min-width: 70px;
+}
+
+#releasetable button {
+ padding: 3px 10px;
+ background-color: #e0e0e0;
+}
+
+#releasetable button:hover {
+ background-color: #a0a0a0;
+}
+
+.console {
+ width: 100%;
+ background-color: black;
+ font-family: 'lucida console','ui-monospace';
+ color: white;
+ padding: 5px 10px;
+ margin: 20px 0px;
+ padding-bottom: 25px;
+ height: 400px;
+ overflow-y: scroll;
+ white-space: break-spaces;
+}
+.console div {
+ word-break: break-all;
+}
+
+/* media */
@media(max-width: 460px) {
.messages li div, ul.messages li div.date, ul.messages li div.message {
@@ -452,7 +504,7 @@ ul.messages li.new {
/* styles for mobile devices in portrait mode */
body {
- font-size: 14px;
+ font-size: 13px;
}
.tagcard {
@@ -489,4 +541,7 @@ ul.messages li.new {
text-align: center;
}
-}
\ No newline at end of file
+ .actionbox>div {
+ gap: 5px;
+ }
+}
diff --git a/ESP32_AP-Flasher/data/www/main.js b/ESP32_AP-Flasher/data/www/main.js
index 0a979ae6..fda0f506 100644
--- a/ESP32_AP-Flasher/data/www/main.js
+++ b/ESP32_AP-Flasher/data/www/main.js
@@ -15,26 +15,39 @@ const displaySizeLookup = { 0: [152, 152], 1: [128, 296], 2: [400, 300] };
displaySizeLookup[17] = [128, 296];
const colorTable = { 0: [255, 255, 255], 1: [0, 0, 0], 2: [255, 0, 0], 3: [150, 150, 150] };
+const apstate = [
+ { state: "offline", color: "red" },
+ { state: "online", color: "green" },
+ { state: "flashing", color: "orange" },
+ { state: "wait for reset", color: "blue" },
+ { state: "requires power cycle", color: "purple" },
+ { state: "failed", color: "red" },
+ { state: "coming online", color: "yellow" }
+];
+
const imageQueue = [];
let isProcessing = false;
let servertimediff = 0;
let paintLoaded = false, paintShow = false;
var cardconfig;
+let otamodule;
window.addEventListener("load", function () {
- fetch("/get_ap_list")
+ fetch("/get_ap_config")
.then(response => response.json())
.then(data => {
if (data.alias) {
$(".logo").innerHTML = data.alias;
this.document.title = data.alias;
}
- })
- fetch('/content_cards.json')
+ });
+ fetch('/content_cards.json')
.then(response => response.json())
.then(data => {
cardconfig = data;
loadTags(0);
+ connect();
+ setInterval(updatecards, 1000);
})
.catch(error => {
console.error('Error:', error);
@@ -43,8 +56,6 @@ window.addEventListener("load", function () {
});
let socket;
-connect();
-setInterval(updatecards, 1000);
function loadTags(pos) {
fetch("/get_db?pos=" + pos)
@@ -77,6 +88,10 @@ function connect() {
}
if (msg.sys) {
$('#sysinfo').innerHTML = 'free heap: ' + msg.sys.heap + ' bytes ┇ db size: ' + msg.sys.dbsize + ' bytes ┇ db record count: ' + msg.sys.recordcount + ' ┇ littlefs free: ' + msg.sys.littlefsfree + ' bytes';
+ if (msg.sys.apstate) {
+ $("#apstatecolor").style.color = apstate[msg.sys.apstate].color;
+ $("#apstate").innerHTML = apstate[msg.sys.apstate].state;
+ }
servertimediff = (Date.now() / 1000) - msg.sys.currtime;
}
if (msg.apitem) {
@@ -87,6 +102,16 @@ function connect() {
row.insertCell(3).innerHTML = msg.apitem.channel;
row.insertCell(4).innerHTML = msg.apitem.version;
}
+ if (msg.console) {
+ console.log(otamodule);
+ if (otamodule && typeof(otamodule.print) === "function") {
+ let color = "#c0c0c0";
+ if (msg.console.startsWith("Fail") || msg.console.startsWith("Err")) {
+ color = "red";
+ }
+ otamodule.print(msg.console, color);
+ }
+ }
});
socket.addEventListener("close", (event) => {
@@ -346,7 +371,7 @@ $('#apconfigbutton').onclick = function () {
for (var i = rowCount - 1; i > 0; i--) {
table.deleteRow(i);
}
- fetch("/get_ap_list")
+ fetch("/get_ap_config")
.then(response => response.json())
.then(data => {
$('#apcfgalias').value = data.alias;
@@ -381,7 +406,12 @@ $('#apcfgsave').onclick = function () {
$('#updatebutton').onclick = function () {
$('#apconfigbox').style.display = 'none';
$('#apupdatebox').style.display = 'block';
- //https://api.github.com/repos/jjwbruijn/OpenEPaperLink/commits
+ loadOTA();
+}
+
+async function loadOTA() {
+ otamodule = await import('./ota.js?v=' + Date.now());
+ otamodule.initUpdate();
}
$('#paintbutton').onclick = function () {
@@ -578,3 +608,4 @@ function sortGrid() {
});
gridItems.forEach((item) => sortableGrid.appendChild(item));
}
+
diff --git a/ESP32_AP-Flasher/data/www/ota.js b/ESP32_AP-Flasher/data/www/ota.js
new file mode 100644
index 00000000..b46c7f11
--- /dev/null
+++ b/ESP32_AP-Flasher/data/www/ota.js
@@ -0,0 +1,292 @@
+const repoUrl = 'https://api.github.com/repos/jonasniesner/OpenEPaperLink/releases';
+
+const $ = document.querySelector.bind(document);
+
+let running = false;
+let errors = 0;
+let env = '';
+let buttonState = false;
+
+export function initUpdate() {
+ if (!$("#updateconsole")) {
+ const consoleDiv = document.createElement('div');
+ consoleDiv.classList.add('console');
+ consoleDiv.id = "updateconsole";
+ $('#apupdatebox').appendChild(consoleDiv);
+ }
+ $("#updateconsole").innerHTML = "";
+
+ fetch("/sysinfo")
+ .then(response => {
+ if (response.status != 200) {
+ print("Error fetching sysinfo: " + response.status, "red");
+ if (response.status == 404) {
+ print("Your current firmware version is not yet capable of updating OTA.");
+ print("Update it manually one last time.");
+ disableButtons(true);
+ }
+ return "{}";
+ } else {
+ return response.json();
+ }
+ })
+ .then(data => {
+ if (data.env) {
+ print(`env: ${data.env}`);
+ print(`build date: ${formatEpoch(data.buildtime)}`);
+ print(`version: ${data.buildversion}`);
+ print(`sha: ${data.sha}`);
+ print(`psram size: ${data.psramsize}`);
+ print(`flash size: ${data.flashsize}`);
+ print("--------------------------","gray");
+ env = data.env;
+ if (data.rollback) $("#rollbackOption").display = 'block';
+ }
+ })
+ .catch(error => {
+ print('Error fetching sysinfo: ' + error, "red");
+ });
+
+ fetch(repoUrl)
+ .then(response => response.json())
+ .then(data => {
+ const releaseDetails = data.map(release => {
+ const assets = release.assets;
+ let fileUrl = null;
+ const filesJsonAsset = assets.find(asset => asset.name === 'files.json');
+ if (filesJsonAsset) {
+ fileUrl = filesJsonAsset.browser_download_url;
+ return {
+ html_url: release.html_url,
+ tag_name: release.tag_name,
+ name: release.name,
+ date: formatDateTime(release.published_at),
+ author: release.author.login,
+ file_url: fileUrl
+ }
+ };
+ });
+
+ const table = document.createElement('table');
+ const tableHeader = document.createElement('tr');
+ tableHeader.innerHTML = '
Release | Date | Name | Author | Update: | ';
+ table.appendChild(tableHeader);
+
+ releaseDetails.forEach(release => {
+ const tableRow = document.createElement('tr');
+ tableRow.innerHTML = `
${release.tag_name} | ${release.date} | ${release.name} | ${release.author} | | | `;
+ table.appendChild(tableRow);
+ });
+
+ $('#releasetable').innerHTML = "";
+ $('#releasetable').appendChild(table);
+ disableButtons(buttonState);
+ })
+ .catch(error => {
+ print('Error fetching releases:' + error, "red");
+ });
+}
+
+export function updateWebpage(fileUrl) {
+ if (running) return;
+ if (!confirm("Confirm updating the littleFS storage")) return;
+
+ disableButtons(true);
+ running = true;
+ errors = 0;
+ consoleDiv.scrollTop = consoleDiv.scrollHeight;
+
+ print("Updating littleFS partition...");
+
+ fetch("/getexturl?url=" + fileUrl)
+ .then(response => response.json())
+ .then(data => {
+ checkfiles(data.files);
+ })
+ .catch(error => {
+ print('Error fetching data:' + error, "red");
+ });
+
+ const checkfiles = async (files) => {
+ for (const file of files) {
+ try {
+ const url = "/check_file?path=" + encodeURIComponent(file.path);
+ const response = await fetch(url);
+ if (response.ok) {
+ const data = await response.json();
+ if (data.filesize == file.size && data.md5 == file.md5) {
+ print(`file ${file.path} is up to date`, "green");
+ } else if (data.filesize == 0) {
+ await fetchAndPost(file.url, file.name, file.path);
+ } else {
+ await fetchAndPost(file.url, file.name, file.path);
+ }
+ } else {
+ print(`error checking file ${file.path}: ${response.status}`, "red");
+ errors++;
+ }
+ } catch (error) {
+ console.error(`error checking file ${file.path}:` + error, "red");
+ errors++;
+ }
+ }
+ running = false;
+ if (errors) {
+ print("------", "gray");
+ print(`Finished updating with ${errors} errors.`, "red");
+ } else {
+ print("------", "gray");
+ print("Update succesfull.");
+ }
+ disableButtons(false);
+
+ };
+}
+
+export async function updateESP(fileUrl) {
+ if (running) return;
+ if (!confirm("Confirm updating the microcontroller")) return;
+
+ disableButtons(true);
+ running = true;
+ errors = 0;
+ consoleDiv.scrollTop = consoleDiv.scrollHeight;
+
+ print("Updating firmware...");
+
+ let binurl, binmd5, binsize;
+
+ try {
+ const response = await fetch("/getexturl?url=" + fileUrl);
+ const data = await response.json();
+ const file = data.binaries.find((entry) => entry.name == env + '.bin');
+ if (file) {
+ binurl = file.url;
+ binmd5 = file.md5;
+ binsize = file.size;
+ console.log(`URL for "${file.name}": ${binurl}`);
+
+ try {
+ const response = await fetch('/update_ota', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ },
+ body: new URLSearchParams({
+ url: binurl,
+ md5: binmd5,
+ size: binsize
+ })
+ });
+
+ if (response.ok) {
+ const result = await response.text();
+ print('OTA update initiated.');
+ } else {
+ print('Failed to initiate OTA update: ' + response.status, "red");
+ }
+ } catch (error) {
+ print('Error during OTA update: ' + error, "red");
+ }
+ } else {
+ print(`File "${fileName}" not found.`, "red");
+ }
+ } catch (error) {
+ print('Error: ' + error, "red");
+ }
+
+ running = false;
+ disableButtons(false);
+}
+
+$('#rollbackBtn').onclick = function () {
+ if (running) return;
+ if (!confirm("Confirm switching to previeous firmware")) return;
+
+ disableButtons(true);
+ running = true;
+ errors = 0;
+ consoleDiv.scrollTop = consoleDiv.scrollHeight;
+
+ print("Rolling back...");
+
+ fetch("/rollback", {
+ method: "POST",
+ body: formData
+ })
+
+ running = false;
+ disableButtons(false);
+
+}
+
+export function print(line, color = "white") {
+ const consoleDiv = document.getElementById('updateconsole');
+ if (consoleDiv) {
+ const isScrolledToBottom = consoleDiv.scrollHeight - consoleDiv.clientHeight <= consoleDiv.scrollTop;
+ const newLine = document.createElement('div');
+ newLine.style.color = color;
+ newLine.textContent = line;
+ consoleDiv.appendChild(newLine);
+ if (isScrolledToBottom) {
+ consoleDiv.scrollTop = consoleDiv.scrollHeight;
+ }
+ }
+}
+
+function formatEpoch(epochTime) {
+ const date = new Date(epochTime * 1000); // Convert seconds to milliseconds
+
+ const year = date.getFullYear();
+ const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are zero-based
+ const day = String(date.getDate()).padStart(2, '0');
+ const hours = String(date.getHours()).padStart(2, '0');
+ const minutes = String(date.getMinutes()).padStart(2, '0');
+
+ return `${year}-${month}-${day} ${hours}:${minutes}`;
+}
+
+function formatDateTime(utcDateString) {
+ const date = new Date(utcDateString);
+
+ const year = date.getUTCFullYear();
+ const month = String(date.getUTCMonth() + 1).padStart(2, '0');
+ const day = String(date.getUTCDate()).padStart(2, '0');
+ const hours = String(date.getUTCHours()).padStart(2, '0');
+ const minutes = String(date.getUTCMinutes()).padStart(2, '0');
+
+ const formattedDate = `${year}-${month}-${day} ${hours}:${minutes}`;
+ return formattedDate;
+}
+
+const fetchAndPost = async (url, name, path) => {
+ try {
+ print("updating " + path);
+ const response = await fetch(url);
+ const fileContent = await response.blob();
+
+ const formData = new FormData();
+ formData.append('path', path);
+ formData.append('file', fileContent, name);
+
+ const uploadResponse = await fetch('/littlefs_put', {
+ method: 'POST',
+ body: formData
+ });
+
+ if (!uploadResponse.ok) {
+ print(`${response.status} ${response.body}`, "red");
+ errors++;
+ }
+ } catch (error) {
+ print('error: ' + error, "red");
+ errors++;
+ }
+};
+
+function disableButtons(active) {
+ $("#apupdatebox").querySelectorAll('button').forEach(button => {
+ button.disabled = active;
+ });
+ buttonState = active;
+}
diff --git a/ESP32_AP-Flasher/data/www/upload-demo.html b/ESP32_AP-Flasher/data/www/upload-demo.html
new file mode 100644
index 00000000..8e45ec2b
--- /dev/null
+++ b/ESP32_AP-Flasher/data/www/upload-demo.html
@@ -0,0 +1,38 @@
+
+
+
+
+
+
Image Upload Form
+
+
+
+
demo upload form
+
You can use this as an example how to push images to a tag by an external server/script.
+
+
+
+
+
+
diff --git a/ESP32_AP-Flasher/data/www/upload-test.html b/ESP32_AP-Flasher/data/www/upload-test.html
deleted file mode 100644
index 5e2164ca..00000000
--- a/ESP32_AP-Flasher/data/www/upload-test.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
Image Upload Form
-
-
-
-
-
-
-
diff --git a/ESP32_AP-Flasher/esp32_squeeze_ota.csv b/ESP32_AP-Flasher/esp32_squeeze_ota.csv
new file mode 100644
index 00000000..f3bcf0db
--- /dev/null
+++ b/ESP32_AP-Flasher/esp32_squeeze_ota.csv
@@ -0,0 +1,7 @@
+# Name, Type, SubType, Offset, Size, Flags
+nvs, data, nvs, 0x9000, 0x5000,
+otadata, data, ota, 0xe000, 0x2000,
+app0, app, ota_0, 0x10000, 0x150000,
+app1, app, ota_1, 0x160000,0x150000,
+spiffs, data, spiffs, 0x2B0000,0x140000,
+coredump, data, coredump,0x3F0000,0x10000,
\ No newline at end of file
diff --git a/ESP32_AP-Flasher/include/contentmanager.h b/ESP32_AP-Flasher/include/contentmanager.h
index 2af05e3e..8de24db7 100644
--- a/ESP32_AP-Flasher/include/contentmanager.h
+++ b/ESP32_AP-Flasher/include/contentmanager.h
@@ -1,6 +1,5 @@
#include
#include
-#define DISABLE_ALL_LIBRARY_WARNINGS
#include
#include "makeimage.h"
diff --git a/ESP32_AP-Flasher/include/ota.h b/ESP32_AP-Flasher/include/ota.h
new file mode 100644
index 00000000..443d3733
--- /dev/null
+++ b/ESP32_AP-Flasher/include/ota.h
@@ -0,0 +1,12 @@
+#include
+
+#include "web.h"
+
+void handleSysinfoRequest(AsyncWebServerRequest* request);
+void handleCheckFile(AsyncWebServerRequest* request);
+void handleGetExtUrl(AsyncWebServerRequest* request);
+void handleLittleFSUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final);
+void handleUpdateOTA(AsyncWebServerRequest* request);
+void firmwareUpdateTask(void* parameter);
+void updateFirmware(const char* url, const char* expectedMd5, size_t size);
+void handleRollback(AsyncWebServerRequest* request);
diff --git a/ESP32_AP-Flasher/include/web.h b/ESP32_AP-Flasher/include/web.h
index c6c07728..8ee39c39 100644
--- a/ESP32_AP-Flasher/include/web.h
+++ b/ESP32_AP-Flasher/include/web.h
@@ -12,9 +12,9 @@ void wsErr(String text);
void wsSendTaginfo(uint8_t *mac, uint8_t syncMode);
void wsSendSysteminfo();
void wsSendAPitem(struct APlist* apitem);
+void wsSerial(String text);
uint8_t wsClientCount();
extern AsyncWebSocket ws;
extern SemaphoreHandle_t wsMutex;
-extern TaskHandle_t websocketUpdater;
\ No newline at end of file
diff --git a/ESP32_AP-Flasher/platformio.ini b/ESP32_AP-Flasher/platformio.ini
index d0c54f8d..dcc2263d 100644
--- a/ESP32_AP-Flasher/platformio.ini
+++ b/ESP32_AP-Flasher/platformio.ini
@@ -29,10 +29,6 @@ board_build.f_cpu = 240000000L
;upload_port = COM30
;monitor_port = COM30
-build_flags =
- -D BUILD_ENV_NAME=$PIOENV
- -D BUILD_TIME=$UNIX_TIME
-
[env:OpenEPaperLink_Mini_AP]
platform = https://github.com/platformio/platform-espressif32.git
board=lolin_s2_mini
@@ -40,6 +36,8 @@ board_build.partitions = default.csv
build_unflags =
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
build_flags =
+ -D BUILD_ENV_NAME=$PIOENV
+ -D BUILD_TIME=$UNIX_TIME
-D OPENEPAPERLINK_MINI_AP_PCB
-D ARDUINO_USB_MODE=0
-D CONFIG_SPIRAM_USE_MALLOC=1
@@ -62,6 +60,11 @@ build_flags =
-D FLASHER_LED=15
-D FLASHER_RGB_LED=33
+ -D USER_SETUP_LOADED
+ -D DISABLE_ALL_LIBRARY_WARNINGS
+ -D ILI9341_DRIVER
+ -D SMOOTH_FONT
+ -D LOAD_FONT2
build_src_filter =
+<*>--
@@ -86,6 +89,8 @@ build_unflags =
-D ARDUINO_USB_MODE=1
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
build_flags =
+ -D BUILD_ENV_NAME=$PIOENV
+ -D BUILD_TIME=$UNIX_TIME
-D OPENEPAPERLINK_PCB
-D ARDUINO_USB_MODE=0
-D CONFIG_ESP32S3_SPIRAM_SUPPORT=1
@@ -127,6 +132,12 @@ build_flags =
-D FLASHER_LED=21
-D FLASHER_RGB_LED=48
+ -D USER_SETUP_LOADED
+ -D DISABLE_ALL_LIBRARY_WARNINGS
+ -D ILI9341_DRIVER
+ -D SMOOTH_FONT
+ -D LOAD_FONT2
+
board_build.flash_mode=qio
board_build.arduino.memory_type = qio_opi
board_build.psram_type=qspi_opi
@@ -140,6 +151,8 @@ board = esp32dev
board_build.partitions = default.csv
build_flags =
+ -D BUILD_ENV_NAME=$PIOENV
+ -D BUILD_TIME=$UNIX_TIME
-D CORE_DEBUG_LEVEL=0
-D SIMPLE_AP
@@ -153,9 +166,14 @@ build_flags =
-D FLASHER_AP_TEST=-1
-D FLASHER_AP_TXD=17
-D FLASHER_AP_RXD=16
-
-D FLASHER_LED=22
+ -D USER_SETUP_LOADED
+ -D DISABLE_ALL_LIBRARY_WARNINGS
+ -D ILI9341_DRIVER
+ -D SMOOTH_FONT
+ -D LOAD_FONT2
+
build_src_filter =
+<*>--
@@ -168,6 +186,8 @@ board = esp32dev
board_build.partitions = no_ota.csv
build_flags =
+ -D BUILD_ENV_NAME=$PIOENV
+ -D BUILD_TIME=$UNIX_TIME
-D ALTERNATIVE_PCB
-D FLASHER_AP_SS=22
-D FLASHER_AP_CLK=13
diff --git a/ESP32_AP-Flasher/src/contentmanager.cpp b/ESP32_AP-Flasher/src/contentmanager.cpp
index 4db48a59..37a3f6b0 100644
--- a/ESP32_AP-Flasher/src/contentmanager.cpp
+++ b/ESP32_AP-Flasher/src/contentmanager.cpp
@@ -30,9 +30,12 @@
#include "web.h"
#include "language.h"
-#define PAL_BLACK 0
-#define PAL_WHITE 9
-#define PAL_RED 2
+// #define PAL_BLACK 0
+// #define PAL_WHITE 9
+// #define PAL_RED 2
+#define PAL_BLACK TFT_BLACK
+#define PAL_WHITE TFT_WHITE
+#define PAL_RED TFT_RED
enum contentModes {
Image,
@@ -105,7 +108,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
imgParam imageParams;
imageParams.hasRed = false;
imageParams.dataType = DATATYPE_IMG_RAW_1BPP;
- imageParams.dither = true;
+ imageParams.dither = false;
if (taginfo->hasCustomLUT) imageParams.grayLut = true;
imageParams.invert = false;
@@ -115,7 +118,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"] == "0") imageParams.dither = false;
+ if (cfgobj["dither"] && cfgobj["dither"] == "1") imageParams.dither = true;
jpg2buffer(cfgobj["filename"].as(), filename, imageParams);
if (imageParams.hasRed) imageParams.dataType = DATATYPE_IMG_RAW_2BPP;
if (prepareDataAvail(&filename, imageParams.dataType, mac, cfgobj["timetolive"].as())) {
@@ -283,13 +286,16 @@ void drawString(TFT_eSprite &spr, String content, uint16_t posx, uint16_t posy,
}
void initSprite(TFT_eSprite &spr, int w, int h) {
- spr.setColorDepth(4); // 4 bits per pixel, uses indexed color
+ // spr.setColorDepth(4); // 4 bits per pixel, uses indexed color
+ spr.setColorDepth(8);
spr.createSprite(w, h);
+ /*
uint16_t cmap[16];
cmap[PAL_BLACK] = TFT_BLACK;
cmap[PAL_RED] = TFT_RED;
cmap[PAL_WHITE] = TFT_WHITE;
spr.createPalette(cmap, 16);
+ */
if (spr.getPointer() == nullptr) {
wsErr("Failed to create sprite");
}
@@ -815,7 +821,7 @@ bool getRssFeed(String &filename, String URL, String title, tagRecord *&taginfo,
struct tm timeInfo;
char header[32];
getLocalTime(&timeInfo);
- sprintf(header, "%02d-%02d-%04d %02d:%02d", timeInfo.tm_mday, timeInfo.tm_mon + 1, timeInfo.tm_year + 1900, timeInfo.tm_hour, timeInfo.tm_min);
+ //sprintf(header, "%02d-%02d-%04d %02d:%02d", timeInfo.tm_mday, timeInfo.tm_mon + 1, timeInfo.tm_year + 1900, timeInfo.tm_hour, timeInfo.tm_min);
const char *url = URL.c_str();
const char *tag = "title";
@@ -1161,7 +1167,7 @@ void prepareNFCReq(uint8_t *dst, const char *url) {
void prepareLUTreq(uint8_t *dst, String input) {
const char *delimiters = ", \t";
- const int maxValues = 70;
+ const int maxValues = 76;
uint8_t waveform[maxValues];
char *ptr = strtok(const_cast(input.c_str()), delimiters);
int i = 0;
diff --git a/ESP32_AP-Flasher/src/main.cpp b/ESP32_AP-Flasher/src/main.cpp
index 597a2cf9..4babd32b 100644
--- a/ESP32_AP-Flasher/src/main.cpp
+++ b/ESP32_AP-Flasher/src/main.cpp
@@ -27,12 +27,12 @@ void timeTask(void* parameter) {
if (!getLocalTime(&tm)) {
Serial.println("Waiting for valid time from NTP-server");
} else {
- if (now % 5 == 0) {
+ if (now % 5 == 0 || apInfo.state != AP_STATE_ONLINE) {
wsSendSysteminfo();
}
if (now % 300 == 6) saveDB("/current/tagDB.json");
- contentRunner();
+ if (apInfo.isOnline) contentRunner();
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
@@ -76,6 +76,7 @@ void setup() {
heap_caps_malloc_extmem_enable(64);
#endif
+ /*
Serial.println("\n\n##################################");
Serial.printf("Internal Total heap %d, internal Free Heap %d\n", ESP.getHeapSize(), ESP.getFreeHeap());
Serial.printf("SPIRam Total heap %d, SPIRam Free Heap %d\n", ESP.getPsramSize(), ESP.getFreePsram());
@@ -99,6 +100,7 @@ void setup() {
p->type, p->subtype, p->address, p->size, p->label);
} while (pi = (esp_partition_next(pi)));
}
+ */
#ifdef HAS_USB
// We'll need to start the 'usbflasher' task for boards with a second (USB) port. This can be used as a 'flasher' interface, using a python script on the host
diff --git a/ESP32_AP-Flasher/src/makeimage.cpp b/ESP32_AP-Flasher/src/makeimage.cpp
index 168af98f..b27ff2d8 100644
--- a/ESP32_AP-Flasher/src/makeimage.cpp
+++ b/ESP32_AP-Flasher/src/makeimage.cpp
@@ -30,7 +30,7 @@ void jpg2buffer(String filein, String fileout, imgParam &imageParams) {
}
Serial.println("jpeg conversion " + String(w) + "x" + String(h));
- spr.setColorDepth(8);
+ spr.setColorDepth(16);
spr.createSprite(w, h);
if (spr.getPointer() == nullptr) {
//no heap space for 8bpp, fallback to 1bpp
@@ -62,32 +62,11 @@ 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 = 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(0.26 * r_diff * r_diff + 0.70 * g_diff * g_diff + 0.04 * b_diff * b_diff);
+ int32_t r_diff = c1.r + e1.r - c2.r;
+ int32_t g_diff = c1.g + e1.g - c2.g;
+ int32_t b_diff = c1.b + e1.b - c2.b;
+ return 3 * r_diff * r_diff + 6 * g_diff * g_diff + 1 * b_diff * b_diff;
}
void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
@@ -117,14 +96,14 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
{255, 0, 0} // Red
};
if (imageParams.grayLut) {
- Color newColor = {150, 150, 150};
+ Color newColor = {160, 160, 160};
palette.push_back(newColor);
Serial.println("rendering with gray");
}
int num_colors = palette.size();
Color color;
- Error *error_bufferold = new Error[bufw];
- Error *error_buffernew = new Error[bufw];
+ Error *error_bufferold = new Error[bufw + 4];
+ Error *error_buffernew = new Error[bufw + 4];
memset(error_bufferold, 0, bufw * sizeof(Error));
for (uint16_t y = 0; y < bufh; y++) {
@@ -146,7 +125,7 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
}
}
- uint16_t bitIndex = 7 - (x % 8);
+ uint8_t bitIndex = 7 - (x % 8);
uint16_t byteIndex = (y * bufw + x) / 8;
// this looks a bit ugly, but it's performing better than shorter notations
@@ -164,46 +143,42 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
imageParams.hasRed = true;
break;
}
- /*
- alt 1:
-
- if (best_color_index & 1) {
- blackBuffer[byteIndex] |= (1 << bitIndex);
- }
- if (best_color_index & 2) {
- imageParams.hasRed = true;
- redBuffer[byteIndex] |= (1 << bitIndex);
- }
-
- alt 2:
-
- blackBuffer[byteIndex] |= ((best_color_index & 1) << bitIndex);
- redBuffer[byteIndex] |= ((best_color_index & 2) << bitIndex);
- imageParams.hasRed |= (best_color_index & 2);
- */
if (imageParams.dither) {
Error error = {
- ((float)color.r + error_bufferold[x].r - palette[best_color_index].r) / 16.0f,
- ((float)color.g + error_bufferold[x].g - palette[best_color_index].g) / 16.0f,
- ((float)color.b + error_bufferold[x].b - palette[best_color_index].b) / 16.0f};
+ static_cast(color.r) + error_bufferold[x].r - static_cast(palette[best_color_index].r),
+ static_cast(color.g) + error_bufferold[x].g - static_cast(palette[best_color_index].g),
+ static_cast(color.b) + error_bufferold[x].b - static_cast(palette[best_color_index].b) };
- error_buffernew[x].r += error.r * 5.0f;
- error_buffernew[x].g += error.g * 5.0f;
- error_buffernew[x].b += error.b * 5.0f;
+ // Burkes Dithering
+ error_buffernew[x].r += error.r / 4.0f;
+ error_buffernew[x].g += error.g / 4.0f;
+ error_buffernew[x].b += error.b / 4.0f;
if (x > 0) {
- error_buffernew[x - 1].r += error.r * 3.0f;
- error_buffernew[x - 1].g += error.g * 3.0f;
- error_buffernew[x - 1].b += error.b * 3.0f;
+ error_buffernew[x - 1].r += error.r / 8.0f;
+ error_buffernew[x - 1].g += error.g / 8.0f;
+ error_buffernew[x - 1].b += error.b / 8.0f;
}
- if (x < bufw - 1) {
- error_buffernew[x + 1].r += error.r * 1.0f;
- error_buffernew[x + 1].g += error.g * 1.0f;
- error_buffernew[x + 1].b += error.b * 1.0f;
- error_bufferold[x + 1].r += error.r * 7.0f;
- error_bufferold[x + 1].g += error.g * 7.0f;
- error_bufferold[x + 1].b += error.b * 7.0f;
+ if (x > 1) {
+ error_buffernew[x - 2].r += error.r / 16.0f;
+ error_buffernew[x - 2].g += error.g / 16.0f;
+ error_buffernew[x - 2].b += error.b / 16.0f;
}
+ error_buffernew[x + 1].r += error.r / 8.0f;
+ error_buffernew[x + 1].g += error.g / 8.0f;
+ error_buffernew[x + 1].b += error.b / 8.0f;
+
+ error_bufferold[x + 1].r += error.r / 4.0f;
+ error_bufferold[x + 1].g += error.g / 4.0f;
+ error_bufferold[x + 1].b += error.b / 4.0f;
+
+ error_buffernew[x + 2].r += error.r / 16.0f;
+ error_buffernew[x + 2].g += error.g / 16.0f;
+ error_buffernew[x + 2].b += error.b / 16.0f;
+
+ error_bufferold[x + 2].r += error.r / 8.0f;
+ error_bufferold[x + 2].g += error.g / 8.0f;
+ error_bufferold[x + 2].b += error.b / 8.0f;
}
}
memcpy(error_bufferold, error_buffernew, bufw * sizeof(Error));
diff --git a/ESP32_AP-Flasher/src/ota.cpp b/ESP32_AP-Flasher/src/ota.cpp
new file mode 100644
index 00000000..dfb78626
--- /dev/null
+++ b/ESP32_AP-Flasher/src/ota.cpp
@@ -0,0 +1,244 @@
+#include "ota.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "tag_db.h"
+#include "web.h"
+
+#ifndef BUILD_ENV_NAME
+#define BUILD_ENV_NAME unknown
+#endif
+#ifndef BUILD_TIME
+#define BUILD_TIME 0
+#endif
+#ifndef BUILD_VERSION
+#define BUILD_VERSION custom
+#endif
+#ifndef SHA
+#define SHA 0
+#endif
+
+#define STR_IMPL(x) #x
+#define STR(x) STR_IMPL(x)
+
+void handleSysinfoRequest(AsyncWebServerRequest* request) {
+ StaticJsonDocument<250> doc;
+ doc["alias"] = config.alias;
+ doc["env"] = STR(BUILD_ENV_NAME);
+ doc["buildtime"] = STR(BUILD_TIME);
+ doc["buildversion"] = STR(BUILD_VERSION);
+ doc["sha"] = STR(SHA);
+ doc["psramsize"] = ESP.getPsramSize();
+ doc["flashsize"] = ESP.getFlashChipSize();
+ doc["rollback"] = Update.canRollBack();
+
+ size_t bufferSize = measureJson(doc) + 1;
+ AsyncResponseStream *response = request->beginResponseStream("application/json", bufferSize);
+ serializeJson(doc, *response);
+ request->send(response);
+};
+
+void handleCheckFile(AsyncWebServerRequest *request) {
+ if (!request->hasParam("path")) {
+ request->send(400);
+ return;
+ }
+
+ String filePath = request->getParam("path")->value();
+ File file = LittleFS.open(filePath, "r");
+ if (!file) {
+ StaticJsonDocument<64> doc;
+ doc["filesize"] = 0;
+ doc["md5"] = "";
+ String jsonResponse;
+ serializeJson(doc, jsonResponse);
+ request->send(200, "application/json", jsonResponse);
+ return;
+ }
+
+ size_t fileSize = file.size();
+
+ MD5Builder md5;
+ md5.begin();
+ md5.addStream(file, fileSize);
+ md5.calculate();
+ String md5Hash = md5.toString();
+
+ file.close();
+
+ StaticJsonDocument<128> doc;
+ doc["filesize"] = fileSize;
+ doc["md5"] = md5Hash;
+ String jsonResponse;
+ serializeJson(doc, jsonResponse);
+
+ request->send(200, "application/json", jsonResponse);
+}
+
+void handleGetExtUrl(AsyncWebServerRequest* request) {
+ if (request->hasParam("url")) {
+ String url = request->getParam("url")->value();
+ HTTPClient http;
+ http.begin(url);
+ http.setConnectTimeout(4000);
+ http.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
+ int httpResponseCode = http.GET();
+ if (httpResponseCode > 0) {
+ Serial.println(httpResponseCode);
+ String contentType = http.header("Content-Type");
+ size_t contentLength = http.getSize();
+ if (contentLength > 0) {
+ String content = http.getString();
+ AsyncWebServerResponse* response = request->beginResponse(200, contentType, content);
+ request->send(response);
+ } else {
+ request->send(500, "text/plain", "no size header");
+ }
+ } else {
+ request->send(httpResponseCode, "text/plain", "Failed to fetch URL");
+ }
+ http.end();
+ } else {
+ request->send(400, "text/plain", "Missing 'url' parameter");
+ }
+}
+
+void handleLittleFSUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
+ bool error = false;
+ if (!index) {
+ String path;
+ if (!request->hasParam("path", true)) {
+ path = "/temp/null.bin";
+ final = true;
+ error = true;
+ } else {
+ path = request->getParam("path", true)->value();
+ Serial.println("update " + path);
+ request->_tempFile = LittleFS.open(path, "w", true);
+ }
+ }
+ if (len) {
+ if (!request->_tempFile.write(data, len)) {
+ error = true;
+ final = true;
+ }
+ }
+ if (final) {
+ request->_tempFile.close();
+ if (error) {
+ request->send(507, "text/plain", "Error. Disk full?");
+ } else {
+ request->send(200, "text/plain", "Ok, file written");
+ }
+ }
+}
+
+struct FirmwareUpdateParams {
+ String url;
+ String md5;
+ size_t size;
+};
+
+void handleUpdateOTA(AsyncWebServerRequest* request) {
+ if (request->hasParam("url", true) && request->hasParam("md5", true) && request->hasParam("size", true)) {
+ saveDB("/current/tagDB.json");
+
+ FirmwareUpdateParams* params = new FirmwareUpdateParams;
+ params->url = request->getParam("url", true)->value();
+ params->md5 = request->getParam("md5", true)->value();
+ params->size = request->getParam("size", true)->value().toInt();
+
+ xTaskCreatePinnedToCore(firmwareUpdateTask, "OTAUpdateTask", 8192, params, 1, NULL, 1);
+
+ request->send(200, "text/plain", "In progress");
+ } else {
+ request->send(400, "Bad request");
+ }
+}
+
+void firmwareUpdateTask(void* parameter) {
+ FirmwareUpdateParams* params = reinterpret_cast(parameter);
+
+ const char* url = params->url.c_str();
+ const char* md5 = params->md5.c_str();
+ size_t size = params->size;
+ updateFirmware(url, md5, size);
+
+ delete params;
+ vTaskDelete(NULL);
+}
+
+void updateFirmware(const char* url, const char* expectedMd5, size_t size) {
+ HTTPClient httpClient;
+
+ wsSerial("start downloading");
+ wsSerial(url);
+
+ httpClient.begin(url);
+ httpClient.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
+ int httpCode = httpClient.GET();
+
+ if (httpCode == HTTP_CODE_OK) {
+ if (Update.begin(size)) {
+ Update.setMD5(expectedMd5);
+
+ unsigned long progressTimer = millis();
+ Update.onProgress([&progressTimer](size_t progress, size_t total) {
+ if (millis() - progressTimer > 500 || progress == total) {
+ char buffer[50];
+ sprintf(buffer, "Progress: %u%% %d %d\r\n", progress * 100 / total, progress, total);
+ wsSerial(String(buffer));
+ progressTimer = millis();
+ }
+ });
+
+ size_t written = Update.writeStream(httpClient.getStream());
+ if (written == httpClient.getSize()) {
+ if (Update.end(true)) {
+ wsSerial("Firmware update successful");
+ wsSerial("Restarting system...");
+ vTaskDelay(1000 / portTICK_PERIOD_MS);
+ ESP.restart();
+ } else {
+ wsSerial("Error updating firmware:");
+ wsSerial(Update.errorString());
+ }
+ } else {
+ wsSerial("Error writing firmware data:");
+ wsSerial(Update.errorString());
+ }
+ } else {
+ wsSerial("Failed to begin firmware update");
+ wsSerial(Update.errorString());
+ }
+ } else {
+ wsSerial("Failed to download firmware file (HTTP code " + String(httpCode) + ")");
+ }
+
+ httpClient.end();
+}
+
+void handleRollback(AsyncWebServerRequest* request) {
+ if (Update.canRollBack()) {
+ bool rollbackSuccess = Update.rollBack();
+ if (rollbackSuccess) {
+ request->send(200, "Rollback successfull");
+ wsSerial("Rollback successfull");
+ wsSerial("Restarting system...");
+ vTaskDelay(1000 / portTICK_PERIOD_MS);
+ ESP.restart();
+ } else {
+ wsSerial("Rollback failed");
+ request->send(400, "Rollback failed");
+ }
+ } else {
+ wsSerial("Rollback not allowed");
+ request->send(400, "Rollback not allowed");
+ }
+}
\ No newline at end of file
diff --git a/ESP32_AP-Flasher/src/tag_db.cpp b/ESP32_AP-Flasher/src/tag_db.cpp
index 490c0797..6e84af58 100644
--- a/ESP32_AP-Flasher/src/tag_db.cpp
+++ b/ESP32_AP-Flasher/src/tag_db.cpp
@@ -256,7 +256,7 @@ void initAPconfig() {
}
configFile.close();
}
- config.channel = APconfig["channel"] | 25;
+ config.channel = APconfig["channel"] | 0;
if (APconfig["alias"]) strlcpy(config.alias, APconfig["alias"], sizeof(config.alias));
config.led = APconfig["led"] | 255;
config.language = APconfig["language"] | getDefaultLanguage();
diff --git a/ESP32_AP-Flasher/src/web.cpp b/ESP32_AP-Flasher/src/web.cpp
index 0f70efd0..9f4600ce 100644
--- a/ESP32_AP-Flasher/src/web.cpp
+++ b/ESP32_AP-Flasher/src/web.cpp
@@ -15,6 +15,8 @@
#include "language.h"
#include "leds.h"
#include "newproto.h"
+#include "ota.h"
+#include "serialap.h"
#include "settings.h"
#include "tag_db.h"
#include "udp.h"
@@ -27,10 +29,8 @@ AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
SemaphoreHandle_t wsMutex;
-TaskHandle_t websocketUpdater;
void webSocketSendProcess(void *parameter) {
- websocketUpdater = xTaskGetCurrentTaskHandle();
wsMutex = xSemaphoreCreateMutex();
while (true) {
ws.cleanupClients();
@@ -45,7 +45,6 @@ void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType
switch (type) {
case WS_EVT_CONNECT:
ets_printf("ws[%s][%u] connect\n", server->url(), client->id());
- xTaskNotify(websocketUpdater, 2, eSetBits);
// client->ping();
break;
case WS_EVT_DISCONNECT:
@@ -59,6 +58,7 @@ void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType
ets_printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len) ? (char *)data : "");
break;
case WS_EVT_DATA:
+ /*
AwsFrameInfo *info = (AwsFrameInfo *)arg;
if (info->final && info->index == 0 && info->len == len) {
// the whole message is in a single frame and we got all of it's data
@@ -105,13 +105,13 @@ void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType
client->binary("{\"status\":\"received\"}");
}
}
- }
+ } */
break;
}
}
void wsLog(String text) {
- StaticJsonDocument<500> doc;
+ StaticJsonDocument<250> doc;
doc["logMsg"] = text;
if (wsMutex) xSemaphoreTake(wsMutex, portMAX_DELAY);
ws.textAll(doc.as());
@@ -119,7 +119,7 @@ void wsLog(String text) {
}
void wsErr(String text) {
- StaticJsonDocument<500> doc;
+ StaticJsonDocument<250> doc;
doc["errMsg"] = text;
if (wsMutex) xSemaphoreTake(wsMutex, portMAX_DELAY);
ws.textAll(doc.as());
@@ -127,7 +127,7 @@ void wsErr(String text) {
}
void wsSendSysteminfo() {
- DynamicJsonDocument doc(250);
+ DynamicJsonDocument doc(150);
JsonObject sys = doc.createNestedObject("sys");
time_t now;
time(&now);
@@ -136,6 +136,7 @@ void wsSendSysteminfo() {
sys["recordcount"] = tagDB.size();
sys["dbsize"] = tagDB.size() * sizeof(tagRecord);
sys["littlefsfree"] = LittleFS.totalBytes() - LittleFS.usedBytes();
+ sys["apstate"] = apInfo.state;
xSemaphoreTake(wsMutex, portMAX_DELAY);
ws.textAll(doc.as());
@@ -199,6 +200,15 @@ void wsSendAPitem(struct APlist *apitem) {
if (wsMutex) xSemaphoreGive(wsMutex);
}
+void wsSerial(String text) {
+ StaticJsonDocument<250> doc;
+ doc["console"] = text;
+ Serial.print(text);
+ if (wsMutex) xSemaphoreTake(wsMutex, portMAX_DELAY);
+ ws.textAll(doc.as());
+ if (wsMutex) xSemaphoreGive(wsMutex);
+}
+
uint8_t wsClientCount() {
return ws.count();
}
@@ -214,6 +224,7 @@ void init_web() {
}
WiFi.mode(WIFI_STA);
+
WiFiManager wm;
bool res;
res = wm.autoConnect("OpenEPaperLink Setup");
@@ -328,7 +339,7 @@ void init_web() {
}
});
- server.on("/get_ap_list", HTTP_GET, [](AsyncWebServerRequest *request) {
+ server.on("/get_ap_config", HTTP_GET, [](AsyncWebServerRequest *request) {
UDPcomm udpsync;
udpsync.getAPList();
File configFile = LittleFS.open("/current/apconfig.json", "r");
@@ -378,6 +389,20 @@ void init_web() {
file.close();
});
+ server.on("/sysinfo", HTTP_GET, handleSysinfoRequest);
+ server.on("/check_file", HTTP_GET, handleCheckFile);
+ server.on("/getexturl", HTTP_GET, handleGetExtUrl);
+ server.on("/update_ota", HTTP_POST, [](AsyncWebServerRequest *request) {
+ handleUpdateOTA(request);
+ });
+ server.on("/rollback", HTTP_POST, handleRollback);
+
+ server.on(
+ "/littlefs_put", HTTP_POST, [](AsyncWebServerRequest *request) {
+ request->send(200);
+ },
+ handleLittleFSUpload);
+
server.onNotFound([](AsyncWebServerRequest *request) {
if (request->url() == "/" || request->url() == "index.htm") {
request->send(200, "text/html", "index.html not found. Did you forget to upload the littlefs partition?");