From a89563a0bb66aece0be63b020d874d1e54700c79 Mon Sep 17 00:00:00 2001 From: Nic Limper Date: Thu, 2 Feb 2023 20:21:07 +0100 Subject: [PATCH 1/2] spiffseditor improvements - spiffseditor improvements - moved some files around in Littlefs-partition - small fixes *** Watch out: also upload the new /data folder to the file system. All existing files will be deleted. If you want to keep the database, download tagDB.json first. --- esp32_fw/data/alignment.bmp | Bin 0 -> 113720 bytes esp32_fw/data/bmp24bpp-h.bmp | Bin 113720 -> 0 bytes esp32_fw/data/bmp24bpp-v.bmp | Bin 113720 -> 0 bytes esp32_fw/data/{ => fonts}/calibrib50.vlw | Bin esp32_fw/data/{ => fonts}/calibrib62.vlw | Bin esp32_fw/data/{ => fonts}/numbers1-1.vlw | Bin esp32_fw/data/{ => fonts}/numbers1-2.vlw | Bin esp32_fw/data/{ => fonts}/numbers2-1.vlw | Bin esp32_fw/data/{ => fonts}/numbers2-2.vlw | Bin esp32_fw/data/{ => fonts}/numbers3-1.vlw | Bin esp32_fw/data/{ => fonts}/numbers3-2.vlw | Bin esp32_fw/data/jpeg-h.jpg | Bin 15239 -> 0 bytes esp32_fw/data/jpeg-v.jpg | Bin 16117 -> 0 bytes esp32_fw/data/www/edit.html | 777 +++++++++++++++++++++++ esp32_fw/data/{ => www}/index.html | 2 + esp32_fw/data/{ => www}/main.css | 20 +- esp32_fw/data/{ => www}/main.js | 13 +- esp32_fw/include/web.h | 3 +- esp32_fw/src/SPIFFSEditor.cpp | 430 +------------ esp32_fw/src/contentmanager.cpp | 36 +- esp32_fw/src/main.cpp | 2 +- esp32_fw/src/makeimage.cpp | 3 +- esp32_fw/src/newproto.cpp | 15 +- esp32_fw/src/serial.cpp | 1 - esp32_fw/src/web.cpp | 22 +- 25 files changed, 851 insertions(+), 473 deletions(-) create mode 100644 esp32_fw/data/alignment.bmp delete mode 100644 esp32_fw/data/bmp24bpp-h.bmp delete mode 100644 esp32_fw/data/bmp24bpp-v.bmp rename esp32_fw/data/{ => fonts}/calibrib50.vlw (100%) rename esp32_fw/data/{ => fonts}/calibrib62.vlw (100%) rename esp32_fw/data/{ => fonts}/numbers1-1.vlw (100%) rename esp32_fw/data/{ => fonts}/numbers1-2.vlw (100%) rename esp32_fw/data/{ => fonts}/numbers2-1.vlw (100%) rename esp32_fw/data/{ => fonts}/numbers2-2.vlw (100%) rename esp32_fw/data/{ => fonts}/numbers3-1.vlw (100%) rename esp32_fw/data/{ => fonts}/numbers3-2.vlw (100%) delete mode 100644 esp32_fw/data/jpeg-h.jpg delete mode 100644 esp32_fw/data/jpeg-v.jpg create mode 100644 esp32_fw/data/www/edit.html rename esp32_fw/data/{ => www}/index.html (95%) rename esp32_fw/data/{ => www}/main.css (96%) rename esp32_fw/data/{ => www}/main.js (95%) diff --git a/esp32_fw/data/alignment.bmp b/esp32_fw/data/alignment.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3e302cbeeccea05896510dfac01d0345d4af228f GIT binary patch literal 113720 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 um;X0qiS4^g_BZmb%V&M}HZ literal 0 HcmV?d00001 diff --git a/esp32_fw/data/bmp24bpp-h.bmp b/esp32_fw/data/bmp24bpp-h.bmp deleted file mode 100644 index 6ba63e1919664765e4bfc1ea3185b5fed44e78b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 113720 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 H0*}Z4zW_R| diff --git a/esp32_fw/data/calibrib50.vlw b/esp32_fw/data/fonts/calibrib50.vlw similarity index 100% rename from esp32_fw/data/calibrib50.vlw rename to esp32_fw/data/fonts/calibrib50.vlw diff --git a/esp32_fw/data/calibrib62.vlw b/esp32_fw/data/fonts/calibrib62.vlw similarity index 100% rename from esp32_fw/data/calibrib62.vlw rename to esp32_fw/data/fonts/calibrib62.vlw diff --git a/esp32_fw/data/numbers1-1.vlw b/esp32_fw/data/fonts/numbers1-1.vlw similarity index 100% rename from esp32_fw/data/numbers1-1.vlw rename to esp32_fw/data/fonts/numbers1-1.vlw diff --git a/esp32_fw/data/numbers1-2.vlw b/esp32_fw/data/fonts/numbers1-2.vlw similarity index 100% rename from esp32_fw/data/numbers1-2.vlw rename to esp32_fw/data/fonts/numbers1-2.vlw diff --git a/esp32_fw/data/numbers2-1.vlw b/esp32_fw/data/fonts/numbers2-1.vlw similarity index 100% rename from esp32_fw/data/numbers2-1.vlw rename to esp32_fw/data/fonts/numbers2-1.vlw diff --git a/esp32_fw/data/numbers2-2.vlw b/esp32_fw/data/fonts/numbers2-2.vlw similarity index 100% rename from esp32_fw/data/numbers2-2.vlw rename to esp32_fw/data/fonts/numbers2-2.vlw diff --git a/esp32_fw/data/numbers3-1.vlw b/esp32_fw/data/fonts/numbers3-1.vlw similarity index 100% rename from esp32_fw/data/numbers3-1.vlw rename to esp32_fw/data/fonts/numbers3-1.vlw diff --git a/esp32_fw/data/numbers3-2.vlw b/esp32_fw/data/fonts/numbers3-2.vlw similarity index 100% rename from esp32_fw/data/numbers3-2.vlw rename to esp32_fw/data/fonts/numbers3-2.vlw diff --git a/esp32_fw/data/jpeg-h.jpg b/esp32_fw/data/jpeg-h.jpg deleted file mode 100644 index 3d03dfd864ce2a235274d60151fba75d3984a1b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15239 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 diff --git a/esp32_fw/data/www/edit.html b/esp32_fw/data/www/edit.html new file mode 100644 index 00000000..f5b566f5 --- /dev/null +++ b/esp32_fw/data/www/edit.html @@ -0,0 +1,777 @@ + + + + + + ESP Editor + + + + + + + + +
+
+
+
+ + + + + diff --git a/esp32_fw/data/index.html b/esp32_fw/data/www/index.html similarity index 95% rename from esp32_fw/data/index.html rename to esp32_fw/data/www/index.html index 9cd7fc7a..5f91685e 100644 --- a/esp32_fw/data/index.html +++ b/esp32_fw/data/www/index.html @@ -7,6 +7,7 @@ Solum - alternative proto AP + @@ -58,6 +59,7 @@
+ Currently active tags:
diff --git a/esp32_fw/data/main.css b/esp32_fw/data/www/main.css similarity index 96% rename from esp32_fw/data/main.css rename to esp32_fw/data/www/main.css index 183074a4..1627b3dd 100644 --- a/esp32_fw/data/main.css +++ b/esp32_fw/data/www/main.css @@ -65,16 +65,21 @@ label { flex-wrap: wrap; } +.filebutton { + padding:2px 5px; + background-color: #cccccc; + text-decoration: none; + color: black; +} + +.editbtn { + float:right; +} + .columns div { flex: 1; } -.actionbox div div { - padding: 5px; - margin: 5px; - background-color: white; -} - input { border: solid 1px #666666; padding: 4px; @@ -239,6 +244,9 @@ ul.messages li.new { animation-timing-function: ease-in-out; } +.error { + color: red; +} @media(max-width: 460px) { diff --git a/esp32_fw/data/main.js b/esp32_fw/data/www/main.js similarity index 95% rename from esp32_fw/data/main.js rename to esp32_fw/data/www/main.js index c3e3881b..9d53493c 100644 --- a/esp32_fw/data/main.js +++ b/esp32_fw/data/www/main.js @@ -42,7 +42,10 @@ function connect() { console.log(event.data); const msg = JSON.parse(event.data); if (msg.logMsg) { - showMessage(msg.logMsg); + showMessage(msg.logMsg,false); + } + if (msg.errMsg) { + showMessage(msg.logMsg,true); } if (msg.tags) { processTags(msg.tags); @@ -226,11 +229,15 @@ function contentselected() { }); } -function showMessage(message) { +function showMessage(message,iserr) { const messages = $('#messages'); var date = new Date(), time = date.toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute:'2-digit', second:'2-digit'}); - messages.insertAdjacentHTML("afterbegin", '
  • '+htmlEncode(time+' '+message)+'
  • '); + if (iserr) { + messages.insertAdjacentHTML("afterbegin", '
  • ' + htmlEncode(time + ' ' + message) + '
  • '); + } else { + messages.insertAdjacentHTML("afterbegin", '
  • '+htmlEncode(time+' '+message)+'
  • '); + } } function htmlEncode(input) { diff --git a/esp32_fw/include/web.h b/esp32_fw/include/web.h index 7badb987..524234df 100644 --- a/esp32_fw/include/web.h +++ b/esp32_fw/include/web.h @@ -7,7 +7,8 @@ void init_web(); void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final); extern void webSocketSendProcess(void *parameter); -void wsString(String text); +void wsLog(String text); +void wsErr(String text); void wsSendTaginfo(uint8_t mac[6]); void wsSendSysteminfo(); diff --git a/esp32_fw/src/SPIFFSEditor.cpp b/esp32_fw/src/SPIFFSEditor.cpp index 42e8d375..e301ecbc 100644 --- a/esp32_fw/src/SPIFFSEditor.cpp +++ b/esp32_fw/src/SPIFFSEditor.cpp @@ -2,396 +2,9 @@ #include -// File: edit.htm.gz, Size: 4151 -#define edit_htm_gz_len 4151 -const uint8_t edit_htm_gz[] PROGMEM = { - 0x1F, 0x8B, 0x08, 0x08, 0xB8, 0x94, 0xB1, 0x59, 0x00, 0x03, 0x65, 0x64, 0x69, 0x74, 0x2E, 0x68, - 0x74, 0x6D, 0x00, 0xB5, 0x3A, 0x0B, 0x7B, 0xDA, 0xB8, 0xB2, 0x7F, 0xC5, 0x71, 0xCF, 0x66, 0xED, - 0x83, 0x31, 0x90, 0xA4, 0xD9, 0xD6, 0xC4, 0xC9, 0x42, 0x92, 0x36, 0x6D, 0xF3, 0x6A, 0x80, 0xB6, - 0x69, 0x4F, 0xEE, 0x7E, 0xC2, 0x16, 0xA0, 0xC6, 0x96, 0x5D, 0x5B, 0x0E, 0x49, 0x59, 0xFE, 0xFB, - 0x9D, 0x91, 0x6C, 0xB0, 0x09, 0x69, 0x77, 0xCF, 0xBD, 0xBB, 0xDD, 0x2D, 0x92, 0x46, 0x33, 0x9A, - 0x19, 0xCD, 0x53, 0xDE, 0xBD, 0x8D, 0xA3, 0x8B, 0xC3, 0xFE, 0xF5, 0xE5, 0xB1, 0x36, 0x11, 0x61, - 0xB0, 0xBF, 0x87, 0x7F, 0x6B, 0x01, 0xE1, 0x63, 0x97, 0xF2, 0xFD, 0x3D, 0xC1, 0x44, 0x40, 0xF7, - 0x8F, 0x7B, 0x97, 0xDA, 0xB1, 0xCF, 0x44, 0x94, 0xEC, 0x35, 0xD4, 0xCA, 0x5E, 0x2A, 0x1E, 0x02, - 0xAA, 0x85, 0xD4, 0x67, 0xC4, 0x4D, 0xBD, 0x84, 0xC2, 0x66, 0xDB, 0x0B, 0x67, 0xDF, 0xEB, 0x8C, - 0xFB, 0xF4, 0xDE, 0xD9, 0x6E, 0x36, 0xDB, 0x71, 0x94, 0x32, 0xC1, 0x22, 0xEE, 0x90, 0x61, 0x1A, - 0x05, 0x99, 0xA0, 0xED, 0x80, 0x8E, 0x84, 0xF3, 0x3C, 0xBE, 0x6F, 0x0F, 0xA3, 0xC4, 0xA7, 0x89, - 0xD3, 0x8A, 0xEF, 0x35, 0x00, 0x31, 0x5F, 0x7B, 0xB6, 0xB3, 0xB3, 0xD3, 0x1E, 0x12, 0xEF, 0x76, - 0x9C, 0x44, 0x19, 0xF7, 0xEB, 0x5E, 0x14, 0x44, 0x89, 0xF3, 0x6C, 0xF4, 0x1C, 0xFF, 0xB4, 0x7D, - 0x96, 0xC6, 0x01, 0x79, 0x70, 0x78, 0xC4, 0x29, 0xE0, 0xDE, 0xD7, 0xD3, 0x09, 0xF1, 0xA3, 0xA9, - 0xD3, 0xD4, 0x9A, 0x5A, 0xAB, 0x09, 0x44, 0x92, 0xF1, 0x90, 0x18, 0x4D, 0x0B, 0xFF, 0xD8, 0x3B, - 0x66, 0x7B, 0x14, 0x71, 0x51, 0x4F, 0xD9, 0x77, 0xEA, 0xB4, 0xB6, 0xE0, 0x34, 0x39, 0x1D, 0x91, - 0x90, 0x05, 0x0F, 0x4E, 0x4A, 0x78, 0x5A, 0x4F, 0x69, 0xC2, 0x46, 0x6A, 0x79, 0x4A, 0xD9, 0x78, - 0x22, 0x9C, 0xDF, 0x9A, 0xCD, 0x39, 0xF0, 0xAF, 0x65, 0xC1, 0x2C, 0x60, 0x29, 0x20, 0xA3, 0x78, - 0xEA, 0x3C, 0x11, 0xC5, 0x4E, 0x53, 0xB1, 0xDE, 0x6C, 0x87, 0x24, 0x19, 0x33, 0x0E, 0x83, 0x98, - 0xF8, 0x3E, 0xE3, 0x63, 0x47, 0xA1, 0x05, 0x6C, 0xB6, 0x90, 0x36, 0xA1, 0x01, 0x11, 0xEC, 0x8E, - 0xB6, 0x43, 0xC6, 0xEB, 0x53, 0xE6, 0x8B, 0x89, 0xB3, 0x0B, 0x3C, 0xB6, 0xBD, 0x2C, 0x49, 0x41, - 0xA6, 0x38, 0x62, 0x5C, 0xD0, 0x44, 0xA2, 0xA5, 0x31, 0xE1, 0xB3, 0x5C, 0x54, 0x54, 0x40, 0x21, - 0x27, 0xE3, 0x01, 0xE3, 0xB4, 0x3E, 0x0C, 0x22, 0xEF, 0x76, 0x71, 0xD2, 0x6E, 0x7C, 0x9F, 0x9F, - 0xE5, 0x4C, 0xA2, 0x3B, 0x9A, 0xCC, 0x96, 0xEA, 0x92, 0xD8, 0x15, 0x60, 0x85, 0x34, 0xA5, 0x74, - 0x6E, 0x8B, 0xBB, 0x0C, 0xA0, 0x96, 0xFC, 0x05, 0x29, 0x17, 0xFC, 0x2F, 0x45, 0x5A, 0x11, 0x5C, - 0xA1, 0x30, 0x1E, 0x67, 0x62, 0xF6, 0xF8, 0x2A, 0xA3, 0x98, 0x78, 0x4C, 0x3C, 0xA0, 0xFC, 0xB0, - 0x6D, 0x86, 0xBA, 0x04, 0xAC, 0x24, 0x24, 0x81, 0x86, 0x3A, 0xD7, 0x3E, 0xD0, 0xC4, 0x27, 0x9C, - 0x58, 0x9D, 0x84, 0x91, 0xC0, 0xEA, 0x2D, 0xB5, 0x5E, 0x0F, 0xA3, 0xEF, 0xF5, 0x0C, 0xC6, 0x30, - 0x0F, 0xA8, 0x27, 0x94, 0x92, 0xE1, 0x1E, 0x86, 0xB7, 0x4C, 0x3C, 0x06, 0x3C, 0x5A, 0x28, 0xA9, - 0x4B, 0x2A, 0x69, 0xA2, 0x2E, 0xB0, 0x25, 0xD5, 0x83, 0x1C, 0x4B, 0xC9, 0x95, 0x50, 0xF5, 0x61, - 0x24, 0x44, 0x14, 0x4A, 0x93, 0x5B, 0x08, 0xAC, 0x49, 0xAB, 0x79, 0xF1, 0xE8, 0x46, 0xD6, 0x6B, - 0xBF, 0x44, 0xBE, 0x0D, 0x7A, 0x15, 0xCC, 0x23, 0x41, 0x9D, 0x04, 0x6C, 0xCC, 0x9D, 0x90, 0xF9, - 0x7E, 0x40, 0x4B, 0x56, 0xEB, 0x64, 0x49, 0x60, 0xF8, 0x44, 0x10, 0x87, 0x85, 0x64, 0x4C, 0x1B, - 0x31, 0x1F, 0x03, 0x34, 0xA5, 0xBB, 0x3B, 0x16, 0xFB, 0xD0, 0xBD, 0xB8, 0x9A, 0x36, 0xDF, 0xBD, - 0x1E, 0x47, 0x1D, 0xF8, 0xE7, 0xBC, 0x37, 0x98, 0x1C, 0x0F, 0xC6, 0x30, 0xEA, 0xE2, 0xB4, 0xF3, - 0xFE, 0xB0, 0xF3, 0x1E, 0x7E, 0x0E, 0x5B, 0xB5, 0xAF, 0xA3, 0x6F, 0xB8, 0xD0, 0x7D, 0xED, 0x77, - 0xFB, 0x83, 0xE3, 0x4E, 0xE7, 0x5D, 0xE3, 0xCD, 0xF9, 0xF4, 0xE3, 0xBB, 0x5D, 0x04, 0x77, 0x83, - 0xE6, 0xD5, 0x87, 0x49, 0x73, 0xB0, 0xF5, 0x32, 0xF4, 0x4F, 0xFC, 0x89, 0x17, 0x0E, 0x3A, 0xEF, - 0x3F, 0x5E, 0xDD, 0x5D, 0x87, 0x83, 0x71, 0xEF, 0x63, 0x6B, 0xF2, 0x79, 0xEB, 0x43, 0xEF, 0xF3, - 0xC7, 0x57, 0xB7, 0xF4, 0xD3, 0xC9, 0xDB, 0xCF, 0xFD, 0x29, 0x20, 0x1C, 0x45, 0xBD, 0xC1, 0x55, - 0xF7, 0x43, 0x77, 0xFC, 0xB9, 0xEB, 0x1D, 0xDF, 0x0F, 0x83, 0xF3, 0xEE, 0xEB, 0xCE, 0xB0, 0xB3, - 0xE5, 0x51, 0x3A, 0xEE, 0x5F, 0x75, 0xB3, 0x37, 0xEF, 0x2E, 0xC6, 0x8C, 0x4D, 0x7A, 0x9F, 0xCF, - 0xFB, 0xDE, 0xE1, 0xF3, 0xD3, 0xC1, 0x49, 0x87, 0x4D, 0xCE, 0xDF, 0x5E, 0x35, 0x6F, 0x5F, 0xBF, - 0x3B, 0x3C, 0xF2, 0xAE, 0xDF, 0x5E, 0xEF, 0x1E, 0x6D, 0x37, 0x7E, 0xFB, 0xED, 0xCC, 0xBF, 0x60, - 0xBC, 0x7F, 0xF7, 0xBD, 0x33, 0x3E, 0x9C, 0xBE, 0x78, 0x48, 0xFB, 0x93, 0x37, 0x77, 0xBC, 0xF1, - 0x21, 0xFA, 0xFA, 0xE6, 0xE1, 0x0C, 0xFE, 0xBB, 0xBC, 0xAC, 0x0D, 0x7B, 0xAD, 0x74, 0xF0, 0xFE, - 0xCD, 0x87, 0xAD, 0xF4, 0xE5, 0xF3, 0xB8, 0x7B, 0x74, 0x74, 0x17, 0x0E, 0x2F, 0x1B, 0xA1, 0x7F, - 0x3B, 0x12, 0x2F, 0xB6, 0x45, 0x7C, 0x3D, 0xCE, 0x3E, 0x7F, 0x7B, 0xFE, 0x76, 0xD2, 0xB8, 0xA0, - 0xE4, 0x7A, 0x52, 0x7B, 0xF8, 0xFE, 0xF0, 0x62, 0xD2, 0x3F, 0xB9, 0x3B, 0x0F, 0xC8, 0xFD, 0xF9, - 0xB9, 0xF7, 0x3D, 0xAC, 0x05, 0xE4, 0xE5, 0x45, 0x3F, 0x20, 0x49, 0x6B, 0xE0, 0x77, 0x1A, 0xB5, - 0xC3, 0xAD, 0xCE, 0x8E, 0x48, 0xAE, 0x0E, 0xF9, 0xD1, 0xF6, 0xD7, 0xDE, 0x8B, 0x6E, 0xB7, 0x15, - 0x0D, 0xBF, 0x6D, 0xBD, 0xBE, 0xDD, 0x7D, 0x3D, 0xD8, 0x7D, 0x3F, 0x7C, 0xDF, 0xE9, 0xED, 0x74, - 0x07, 0xE4, 0xBA, 0xF7, 0xBE, 0x33, 0xDA, 0x19, 0x4E, 0x26, 0xEF, 0xDE, 0xF5, 0x5F, 0xF9, 0x9D, - 0xEF, 0x49, 0xE7, 0x62, 0xDA, 0xB9, 0x3F, 0x1E, 0x74, 0x4E, 0x6A, 0xEF, 0x8E, 0xCF, 0x9A, 0xAD, - 0xDE, 0xF5, 0xF6, 0xF8, 0x6C, 0x77, 0xDA, 0x4D, 0x8F, 0x3B, 0xEF, 0xBB, 0xCD, 0xF1, 0xDB, 0x5A, - 0x48, 0x3E, 0x47, 0x87, 0xDB, 0xE3, 0x37, 0xBB, 0xEC, 0xF2, 0x9A, 0x74, 0xDE, 0x74, 0xDF, 0xA6, - 0xEC, 0x2A, 0x3C, 0x19, 0x34, 0x3B, 0x9D, 0xD3, 0x0B, 0xFA, 0xEA, 0x70, 0x9B, 0xBC, 0xDB, 0xF2, - 0x3E, 0x82, 0xFE, 0x07, 0x9F, 0xE8, 0x6F, 0xB5, 0xCE, 0xF4, 0xA2, 0x19, 0x78, 0x2F, 0x69, 0xFF, - 0xE4, 0xBA, 0x2F, 0x6F, 0xE7, 0x38, 0x78, 0xD5, 0xBF, 0xED, 0x65, 0xEF, 0xC3, 0xC3, 0x43, 0x53, - 0xE3, 0x51, 0x3D, 0xA1, 0x31, 0x25, 0xA2, 0x1C, 0xAE, 0x16, 0xFE, 0x01, 0xB6, 0xB5, 0xB4, 0xC2, - 0xDC, 0x4F, 0x05, 0xBD, 0x17, 0x75, 0x9F, 0x7A, 0x51, 0x42, 0xE4, 0x1E, 0x40, 0xA0, 0x09, 0x9A, - 0xD8, 0xFC, 0x77, 0x19, 0x3F, 0x35, 0x15, 0x3F, 0x35, 0xC2, 0x7D, 0xCD, 0x28, 0x1C, 0x01, 0x83, - 0x87, 0x4F, 0xEF, 0x98, 0x47, 0xEB, 0x31, 0xBB, 0xA7, 0x41, 0x5D, 0x22, 0x3B, 0x4D, 0x73, 0x26, - 0xFD, 0xAD, 0xD8, 0x46, 0x38, 0x98, 0x9A, 0xA4, 0x5A, 0x2C, 0xF8, 0x5F, 0x89, 0x47, 0x21, 0xB0, - 0x81, 0xCB, 0x84, 0xF8, 0xAB, 0x7C, 0x27, 0x4A, 0xEA, 0xC3, 0x6C, 0x3C, 0x62, 0xF7, 0xE0, 0xD0, - 0x23, 0xC6, 0x99, 0xA0, 0x5A, 0x2B, 0x9D, 0xFF, 0x5E, 0x90, 0xB9, 0xA5, 0x0F, 0xA3, 0x84, 0x84, - 0x34, 0xD5, 0xFE, 0x22, 0x99, 0xD9, 0x28, 0x89, 0xC2, 0x65, 0x10, 0x99, 0x8B, 0xA8, 0x34, 0x99, - 0xCF, 0x9F, 0x65, 0x71, 0x10, 0x11, 0x10, 0x73, 0x4D, 0xE4, 0x50, 0xF1, 0x34, 0x91, 0x6E, 0xB5, - 0x88, 0xAB, 0xB9, 0x9B, 0x6D, 0xA1, 0x5B, 0x96, 0xDD, 0x7A, 0x6B, 0x67, 0xE9, 0xBA, 0x75, 0xB9, - 0x17, 0xE3, 0xFD, 0x9A, 0x4C, 0x81, 0xF1, 0xA0, 0x14, 0xEE, 0x9E, 0x09, 0x50, 0xE9, 0x13, 0x87, - 0xCB, 0x43, 0xF2, 0xC8, 0xB0, 0x60, 0x40, 0x05, 0xEA, 0x96, 0x8C, 0xD4, 0x85, 0x24, 0xB0, 0x6F, - 0xFE, 0x8C, 0xCA, 0xBC, 0x67, 0x3D, 0x8B, 0x13, 0xB8, 0x0D, 0x3A, 0xFD, 0x11, 0xCD, 0x42, 0xA6, - 0x2A, 0x6D, 0x45, 0x53, 0x65, 0xBC, 0x5C, 0x84, 0x65, 0xDA, 0x93, 0xBC, 0x16, 0xA4, 0x1F, 0x4B, - 0x05, 0xE0, 0x05, 0x37, 0xCF, 0x91, 0x9B, 0x1F, 0x6A, 0x75, 0x7B, 0xF7, 0x97, 0x9C, 0x87, 0x9D, - 0xE6, 0x2F, 0x73, 0x3B, 0xDF, 0x5B, 0xA4, 0xE4, 0x56, 0x13, 0xFE, 0x29, 0x32, 0xEF, 0x8B, 0x25, - 0x0B, 0xC3, 0xE7, 0xF8, 0xA7, 0x60, 0x10, 0xE9, 0x94, 0x80, 0xDB, 0x3B, 0x2F, 0x5F, 0xF8, 0xC3, - 0x02, 0x98, 0x0B, 0xF6, 0x24, 0x3C, 0x21, 0x3E, 0xCB, 0x52, 0xE7, 0x79, 0xF3, 0x97, 0x5C, 0x9F, - 0x5B, 0x3B, 0x28, 0xFB, 0xE2, 0x2E, 0x71, 0xB2, 0xB4, 0xD8, 0x34, 0x66, 0x5C, 0xDB, 0x4A, 0x35, - 0xBC, 0x6F, 0x92, 0x2C, 0x0C, 0xB3, 0x92, 0xED, 0xE7, 0xBF, 0x2F, 0x4D, 0x13, 0xF7, 0xCF, 0x9A, - 0xBF, 0xCC, 0x44, 0x02, 0xD9, 0x64, 0x04, 0xB9, 0xC6, 0x49, 0x22, 0x41, 0x04, 0x35, 0x9A, 0xE6, - 0x1C, 0x84, 0x5B, 0x03, 0xD8, 0xDE, 0x6D, 0xFA, 0x74, 0x6C, 0xCE, 0xE7, 0x7B, 0x0D, 0x99, 0xD7, - 0xA0, 0x6C, 0xF1, 0x12, 0x16, 0x8B, 0xFD, 0x51, 0xC6, 0x3D, 0xE4, 0x41, 0x1B, 0x53, 0x83, 0x9A, - 0xB3, 0x84, 0x8A, 0x2C, 0xE1, 0x9A, 0x1F, 0x79, 0x19, 0x1A, 0xBB, 0x3D, 0xA6, 0xE2, 0x58, 0xD9, - 0x7D, 0xF7, 0xE1, 0x8D, 0x0F, 0x3B, 0xE6, 0x0B, 0x04, 0x6F, 0x2D, 0x02, 0x38, 0x30, 0x9C, 0x97, - 0xE3, 0x54, 0xF6, 0x43, 0x82, 0x01, 0x22, 0xEF, 0xE8, 0x83, 0x41, 0x2D, 0xB1, 0x40, 0xA4, 0x36, - 0xAE, 0x1B, 0xC5, 0x2E, 0x80, 0x71, 0x73, 0x76, 0x07, 0x4A, 0x20, 0x2E, 0xFD, 0x22, 0x6E, 0x2C, - 0xE6, 0x72, 0xF8, 0x69, 0xE7, 0xBB, 0xC9, 0x1E, 0x3B, 0xA8, 0xB7, 0x1C, 0xB2, 0xCF, 0x0E, 0x5A, - 0xE0, 0x5E, 0x65, 0x6E, 0xE4, 0xB9, 0xAF, 0x58, 0x40, 0x07, 0xB9, 0xC3, 0xE1, 0x31, 0x48, 0x6C, - 0xB1, 0x85, 0x28, 0xE2, 0x5B, 0xCD, 0xE6, 0x86, 0x4B, 0x0F, 0x48, 0x00, 0x39, 0xCC, 0xD0, 0x8F, - 0xAF, 0xAE, 0x2E, 0xAE, 0xBE, 0xE8, 0x35, 0x5A, 0xD3, 0x6F, 0x1C, 0x4D, 0xAF, 0x71, 0xD3, 0x11, - 0x76, 0x42, 0x47, 0x09, 0x4D, 0x27, 0x97, 0x44, 0x4C, 0x8C, 0xD4, 0xBE, 0x23, 0x41, 0x56, 0x16, - 0x84, 0xA1, 0xDC, 0xC8, 0xA2, 0x70, 0x39, 0x9D, 0x6A, 0xAF, 0x40, 0xCD, 0x47, 0x90, 0xEA, 0xDA, - 0xC2, 0x26, 0x71, 0x4C, 0xB9, 0x6F, 0xE8, 0x31, 0x20, 0xEA, 0x16, 0x35, 0xAD, 0x84, 0x7E, 0xCB, - 0x68, 0x2A, 0x52, 0x1B, 0x2C, 0xD7, 0xD0, 0x2F, 0x07, 0x7D, 0xDD, 0xD2, 0x1B, 0xE8, 0x47, 0x3A, - 0xF0, 0x46, 0xCC, 0x39, 0x52, 0x89, 0x5C, 0xD0, 0xA4, 0x3E, 0xCC, 0xC0, 0xA0, 0xB8, 0x6E, 0xB6, - 0x23, 0x9B, 0x71, 0x4E, 0x93, 0x93, 0xFE, 0xD9, 0xA9, 0xAB, 0x5F, 0x29, 0x46, 0xB4, 0x53, 0x28, - 0x48, 0x74, 0x4B, 0x5E, 0x51, 0x7E, 0xC8, 0xE1, 0x84, 0x05, 0xBE, 0x11, 0x99, 0x6D, 0x24, 0xE1, - 0x49, 0x12, 0xB2, 0x40, 0x01, 0x0A, 0x9E, 0x2D, 0x1E, 0x62, 0xEA, 0xEA, 0x23, 0x50, 0x86, 0x6E, - 0x79, 0x76, 0x98, 0x05, 0x82, 0xC5, 0x01, 0x75, 0x37, 0x5A, 0x30, 0xE3, 0x60, 0x41, 0xAE, 0x8E, - 0xB9, 0x19, 0x61, 0xCC, 0x77, 0x75, 0x15, 0xA1, 0xF2, 0xB8, 0xB6, 0xEE, 0x14, 0x4F, 0x9D, 0x92, - 0x56, 0x4E, 0x49, 0xCB, 0xB8, 0x4A, 0xE0, 0x34, 0x3F, 0x18, 0xC3, 0x3C, 0xCE, 0xD4, 0x51, 0x05, - 0xCC, 0xA7, 0x23, 0x02, 0x9C, 0x7C, 0x40, 0x6D, 0xBA, 0x7A, 0x63, 0xDD, 0x41, 0xA9, 0x3A, 0xC8, - 0xAF, 0x6A, 0xC4, 0x2F, 0x6B, 0x44, 0xDD, 0xEE, 0x3A, 0x64, 0x5F, 0x21, 0x07, 0x55, 0xE4, 0xA0, - 0x8C, 0x7C, 0x28, 0x8D, 0x64, 0x1D, 0x72, 0xA0, 0x90, 0x93, 0x8A, 0x88, 0x89, 0x14, 0x51, 0x85, - 0xBD, 0x3A, 0x6A, 0x13, 0x05, 0xD2, 0xAD, 0xA4, 0x22, 0x66, 0x62, 0x83, 0x97, 0x92, 0x61, 0x40, - 0x7D, 0x77, 0xA3, 0x09, 0x33, 0x2C, 0xB6, 0xDD, 0xAD, 0xE6, 0x9A, 0x33, 0x12, 0x75, 0x46, 0x56, - 0x65, 0x30, 0x2B, 0x33, 0xA8, 0xF5, 0xC8, 0x1D, 0xD5, 0xD6, 0x31, 0x98, 0x99, 0x56, 0x60, 0x47, - 0xDC, 0x0B, 0x98, 0x77, 0xEB, 0x2E, 0xBD, 0xC5, 0x9C, 0xB1, 0x85, 0x85, 0x5A, 0x5C, 0x06, 0xBA, - 0x01, 0x94, 0x5E, 0x8B, 0xA5, 0x7C, 0x80, 0xFA, 0x9E, 0x5B, 0xD9, 0x5A, 0x02, 0xDC, 0xA6, 0xF7, - 0xD4, 0x3B, 0x8C, 0xC2, 0x90, 0xA0, 0xED, 0xA6, 0xC0, 0x41, 0x3E, 0xD1, 0xCD, 0xB9, 0x15, 0xAD, - 0xC5, 0x79, 0xC2, 0x45, 0x2C, 0x7F, 0x3D, 0x8B, 0x23, 0x03, 0x5C, 0xCE, 0xF5, 0x6C, 0xD4, 0x61, - 0x6A, 0x83, 0x1E, 0xC7, 0x62, 0xF2, 0x13, 0x17, 0x2A, 0x0C, 0x54, 0xA2, 0x7C, 0x69, 0xDE, 0x58, - 0x0B, 0x91, 0x56, 0x7C, 0xEA, 0xA2, 0xB7, 0xE2, 0x54, 0xA8, 0xBC, 0x8A, 0x5D, 0x9A, 0x4B, 0x1D, - 0x94, 0x61, 0xB9, 0xBD, 0x2F, 0xA0, 0xFA, 0x7C, 0x0E, 0xE7, 0x01, 0xFF, 0x13, 0x68, 0xF9, 0xE8, - 0x5F, 0x17, 0x60, 0xC9, 0xA3, 0x34, 0x78, 0x8B, 0xBB, 0x0D, 0xE3, 0xC0, 0xF9, 0x8F, 0x6D, 0x7C, - 0xF9, 0x1F, 0xFB, 0xA6, 0x66, 0x9A, 0x07, 0xFF, 0x6A, 0x48, 0x0D, 0x1B, 0xC2, 0xFC, 0xD2, 0xBA, - 0xB1, 0x08, 0x80, 0xED, 0x7F, 0x9B, 0xFF, 0xB1, 0x25, 0xB8, 0x02, 0x6B, 0xDF, 0x45, 0x90, 0x49, - 0xF0, 0x24, 0x34, 0xB0, 0x68, 0xA4, 0x91, 0xCD, 0x4D, 0x43, 0xB8, 0xA4, 0x72, 0x8D, 0x35, 0x51, - 0xD3, 0x6D, 0x88, 0x53, 0x50, 0x5B, 0xAC, 0x04, 0xBF, 0x3E, 0x24, 0x7A, 0x15, 0x5B, 0x17, 0x00, - 0xC9, 0x3D, 0xCA, 0x0C, 0x3D, 0x22, 0x97, 0x52, 0xCB, 0x0C, 0x02, 0x42, 0xA7, 0x89, 0xE7, 0x2A, - 0xAD, 0x1D, 0x14, 0x30, 0x17, 0xA2, 0xE0, 0xBC, 0x1C, 0x2D, 0x15, 0xEA, 0xAA, 0xFD, 0x17, 0x0A, - 0xA3, 0xD6, 0x12, 0x8A, 0x04, 0x31, 0xAD, 0xD8, 0x79, 0xC6, 0x72, 0x75, 0x4C, 0x59, 0xBA, 0x35, - 0x59, 0x5D, 0x96, 0xAD, 0x04, 0xAE, 0x2F, 0x8D, 0xFE, 0xD7, 0x3D, 0x16, 0x8E, 0xB5, 0x12, 0x3F, - 0xF8, 0x97, 0xFB, 0x2B, 0x46, 0xE4, 0xCD, 0x3F, 0xBC, 0x21, 0x70, 0x05, 0xA6, 0x41, 0x6D, 0x1E, - 0x4D, 0x0D, 0xB3, 0xF6, 0xAB, 0xAE, 0x49, 0x8A, 0xAE, 0x1E, 0x92, 0xFB, 0xBC, 0xA7, 0xC4, 0x8C, - 0xD7, 0xD6, 0x70, 0x5E, 0xB4, 0x28, 0xF9, 0x82, 0xEC, 0xE6, 0x48, 0x26, 0xA2, 0xB6, 0x56, 0x64, - 0x52, 0xD5, 0xCA, 0xE8, 0x5A, 0x63, 0xFF, 0xD7, 0x4A, 0x40, 0xB7, 0x98, 0xBA, 0x4E, 0x15, 0x8C, - 0xB3, 0x00, 0x1C, 0x93, 0x3E, 0x1D, 0x69, 0x03, 0x26, 0x03, 0x75, 0x35, 0x46, 0x5A, 0x81, 0xC1, - 0xCC, 0x03, 0xC3, 0x2B, 0xFB, 0xF3, 0x1E, 0x16, 0xBF, 0xFB, 0x97, 0xAA, 0xAA, 0x81, 0xD4, 0x8B, - 0x33, 0x5D, 0x59, 0x59, 0xD5, 0x4B, 0xE0, 0xD2, 0x08, 0xA0, 0x5B, 0x8B, 0x3C, 0x3A, 0x8C, 0xFC, - 0x87, 0x52, 0xF6, 0x4D, 0xBB, 0x0F, 0x87, 0x01, 0x49, 0xD3, 0x73, 0xB8, 0x01, 0x43, 0xF7, 0x42, - 0x50, 0xB8, 0xB2, 0xC2, 0xFD, 0xE6, 0xE6, 0x66, 0x15, 0x29, 0xA1, 0x21, 0x14, 0xDB, 0x8A, 0x2B, - 0xF0, 0x49, 0xD3, 0xF1, 0x81, 0x30, 0x18, 0xD2, 0x1A, 0xC6, 0xF0, 0x25, 0xE3, 0x47, 0x5C, 0x71, - 0xF4, 0xF4, 0x22, 0xA6, 0xFC, 0x33, 0xDC, 0x95, 0x32, 0xCB, 0x1A, 0xAD, 0xA6, 0x68, 0xFA, 0x8F, - 0xD8, 0x3E, 0xCA, 0x0D, 0x76, 0xC1, 0x7A, 0xBA, 0x56, 0xA1, 0xFC, 0x9F, 0x61, 0xB9, 0x94, 0x28, - 0xD6, 0x70, 0x9C, 0x40, 0x80, 0x5A, 0xC3, 0x31, 0xC4, 0x1A, 0x41, 0x17, 0xFC, 0x26, 0x6B, 0xF9, - 0xCD, 0xFE, 0x19, 0x7E, 0x97, 0x76, 0x1E, 0x15, 0x25, 0x91, 0xAA, 0xAF, 0x50, 0x02, 0x9F, 0xDD, - 0xE9, 0xA6, 0x15, 0xB9, 0x55, 0x0A, 0x50, 0x1B, 0x46, 0x41, 0xD0, 0x8F, 0xE2, 0x83, 0x27, 0xD6, - 0x9D, 0xC5, 0x7A, 0x31, 0xC8, 0xD9, 0x5C, 0x6E, 0xB1, 0xBC, 0xB5, 0x44, 0x4F, 0xA1, 0xEC, 0x5F, - 0x4B, 0x15, 0x01, 0x3F, 0x23, 0x8B, 0x7B, 0xAC, 0xD4, 0xA5, 0x36, 0x28, 0x0F, 0x56, 0x3F, 0xD5, - 0x3C, 0xCB, 0x5F, 0xCC, 0xAE, 0x6B, 0x51, 0x9B, 0xC0, 0x38, 0x57, 0x92, 0x8B, 0x4A, 0xB2, 0xC8, - 0x13, 0x01, 0xA8, 0x58, 0xC7, 0x2E, 0xC4, 0x4D, 0x6B, 0x7A, 0x7C, 0xBF, 0x5C, 0x83, 0xC2, 0xDF, - 0xF5, 0xD5, 0x12, 0x33, 0x08, 0xC4, 0xD3, 0x95, 0x4B, 0x29, 0x5F, 0x37, 0x29, 0x8A, 0x0E, 0x62, - 0x47, 0xA3, 0x51, 0x4A, 0xC5, 0x47, 0x0C, 0x49, 0x56, 0xB2, 0x98, 0x9F, 0xC8, 0x90, 0x04, 0x8C, - 0x45, 0x3C, 0x8C, 0xB2, 0x94, 0x46, 0x99, 0xA8, 0xA4, 0x16, 0x63, 0x21, 0xCC, 0x5E, 0xFA, 0xE7, - 0x9F, 0x8B, 0xC9, 0x7E, 0x5A, 0x0B, 0x96, 0xD3, 0xEB, 0x3D, 0xBF, 0x34, 0xD9, 0xF7, 0x6B, 0x89, - 0xB9, 0x7A, 0xE9, 0xFF, 0x67, 0x4B, 0x21, 0x65, 0x4B, 0xF1, 0xB0, 0x54, 0x2E, 0x62, 0x62, 0x29, - 0xE6, 0xC9, 0x82, 0x91, 0x97, 0x7C, 0x16, 0x0D, 0x1A, 0x2B, 0x25, 0x55, 0x9E, 0x97, 0x7D, 0x95, - 0x43, 0x40, 0x59, 0x71, 0xE5, 0x35, 0x11, 0x06, 0x34, 0xE0, 0x63, 0x64, 0xF2, 0x41, 0xEB, 0xA7, - 0xD1, 0x94, 0x26, 0x87, 0x24, 0xA5, 0x06, 0x24, 0xCD, 0x65, 0xDC, 0x41, 0xA8, 0xE9, 0x04, 0xEB, - 0x76, 0x6D, 0x6E, 0x12, 0x05, 0xCE, 0x33, 0x77, 0xC4, 0xB1, 0x26, 0x03, 0xF9, 0xB2, 0xCA, 0x09, - 0xD4, 0xC6, 0xBE, 0x12, 0xA4, 0x3E, 0x52, 0x25, 0xA8, 0x61, 0x5A, 0xD0, 0x76, 0xC0, 0x35, 0x5F, - 0x26, 0x51, 0x4C, 0xC6, 0xB2, 0x07, 0x83, 0x35, 0x74, 0x0F, 0xA4, 0x66, 0x6D, 0x34, 0x91, 0x60, - 0xA9, 0x73, 0x29, 0xFC, 0x66, 0xD9, 0xC2, 0x70, 0x4B, 0x57, 0xC9, 0xB0, 0xBD, 0xF4, 0xA5, 0x35, - 0x59, 0x83, 0xE0, 0x0B, 0x6C, 0x62, 0xE0, 0x1E, 0x68, 0x64, 0xF2, 0x7B, 0x00, 0x77, 0x6B, 0xB6, - 0xA3, 0x3D, 0xD6, 0x8E, 0x6A, 0x35, 0x53, 0x55, 0xE9, 0xAE, 0x0B, 0x6D, 0x4E, 0x74, 0x23, 0x0B, - 0x4B, 0x10, 0xAA, 0x9A, 0x59, 0x0C, 0x38, 0x1B, 0x81, 0xAA, 0xBA, 0xC0, 0x11, 0xD6, 0x98, 0x66, - 0xA9, 0x23, 0xF1, 0x97, 0x1D, 0xC9, 0x13, 0xB5, 0x07, 0x95, 0xF5, 0x05, 0xD4, 0x31, 0xAB, 0x25, - 0x86, 0x30, 0xD3, 0x29, 0x13, 0xDE, 0x04, 0x03, 0x90, 0x07, 0x5A, 0xD5, 0x05, 0x14, 0xB5, 0x8E, - 0x1C, 0x4D, 0x44, 0xB8, 0x1C, 0x05, 0xF9, 0xF0, 0x6B, 0x9A, 0x0F, 0xBC, 0xB4, 0x18, 0xDD, 0x97, - 0x80, 0x50, 0xD2, 0xE6, 0xE0, 0x88, 0x8F, 0xF2, 0x21, 0xF4, 0xB2, 0x05, 0x9D, 0x02, 0x58, 0xFC, - 0xC6, 0x71, 0x3E, 0x8A, 0x27, 0xC5, 0x68, 0x42, 0xEF, 0x17, 0x78, 0x51, 0x01, 0xF5, 0xA9, 0xEE, - 0x28, 0x1B, 0xDB, 0x68, 0xCE, 0xF3, 0x41, 0x6B, 0x29, 0x7F, 0xF0, 0xFF, 0x28, 0x7F, 0xCC, 0xC7, - 0x85, 0x34, 0x71, 0x31, 0x1A, 0xB3, 0x42, 0x96, 0x61, 0x18, 0xFF, 0x90, 0x93, 0xA4, 0xD4, 0x13, - 0x97, 0x7A, 0x5A, 0xF1, 0xB3, 0xB6, 0x53, 0x98, 0x8E, 0x31, 0xAA, 0xF8, 0xE3, 0xC8, 0xF6, 0xF0, - 0xF7, 0x3C, 0xF2, 0x65, 0x6D, 0x69, 0x5A, 0xA1, 0x31, 0x82, 0x3A, 0x57, 0x37, 0xCB, 0x7E, 0x9A, - 0xFD, 0xB7, 0xAD, 0xE8, 0xD1, 0xF1, 0xE9, 0x71, 0xFF, 0xB8, 0x5C, 0x38, 0x23, 0xE7, 0x25, 0x93, - 0x8A, 0x2B, 0x5D, 0xFA, 0xB2, 0x22, 0x80, 0x02, 0x1B, 0x45, 0x01, 0x7B, 0xDD, 0xDC, 0x54, 0x7E, - 0xF1, 0xB6, 0x77, 0x71, 0x6E, 0xC7, 0x24, 0x01, 0x8F, 0x24, 0x15, 0xE6, 0xC2, 0x82, 0x44, 0xF9, - 0xE0, 0xD7, 0xC7, 0xA5, 0x72, 0x5D, 0x7E, 0x61, 0x70, 0xC4, 0xDC, 0x52, 0xA7, 0xA9, 0x7E, 0x78, - 0xE2, 0x62, 0x5D, 0x99, 0xBF, 0x04, 0x41, 0x72, 0x1A, 0x2D, 0x13, 0x55, 0x11, 0x67, 0x46, 0xE5, - 0x30, 0x2F, 0xEE, 0xB2, 0x75, 0x0D, 0xD3, 0xC8, 0xB4, 0xC4, 0x84, 0xA5, 0xE5, 0x46, 0xA5, 0x12, - 0x14, 0xFE, 0xA2, 0xB6, 0xE7, 0x8B, 0x91, 0x24, 0xB7, 0x5A, 0x73, 0xAB, 0x6F, 0x41, 0x2A, 0x3E, - 0x58, 0x04, 0x23, 0x66, 0x39, 0xDB, 0x16, 0x77, 0xA3, 0x43, 0xEE, 0x61, 0x5C, 0x7F, 0xBA, 0x35, - 0x78, 0xD2, 0x3C, 0x79, 0x61, 0x9E, 0xFC, 0xB1, 0x7B, 0x2E, 0x1C, 0x45, 0xF9, 0xDA, 0xE2, 0x98, - 0xF6, 0x10, 0x58, 0xBB, 0x6D, 0x2F, 0x7D, 0x18, 0x20, 0xD2, 0x83, 0xCB, 0x00, 0xF4, 0x63, 0x58, - 0xFF, 0x4A, 0xEE, 0x88, 0x7A, 0x09, 0xAA, 0xA2, 0xAD, 0x73, 0x54, 0xD8, 0xEE, 0xFD, 0x81, 0xA3, - 0xF2, 0xCE, 0x65, 0x18, 0x48, 0x97, 0xC3, 0x92, 0x37, 0x8B, 0x75, 0xC1, 0x61, 0x19, 0x31, 0x64, - 0x6C, 0x00, 0xE3, 0xCD, 0x5D, 0x49, 0x13, 0xD5, 0x1C, 0xB4, 0xF0, 0x1B, 0x08, 0x8A, 0x4F, 0x39, - 0xCE, 0x9A, 0x38, 0xAD, 0x62, 0x72, 0xC5, 0x23, 0xC8, 0x4A, 0x67, 0x89, 0xC0, 0x6E, 0x10, 0x0D, - 0x0D, 0x7C, 0x64, 0x9A, 0xA1, 0xB6, 0x1D, 0x3E, 0x37, 0xD7, 0xBC, 0xD9, 0x54, 0xFA, 0x4B, 0x62, - 0x79, 0xD5, 0xB0, 0x8B, 0x1C, 0x56, 0xCC, 0x75, 0x7D, 0x1F, 0xF4, 0xA3, 0x4E, 0x29, 0xAF, 0x48, - 0xA4, 0x53, 0xD1, 0x83, 0xC4, 0x86, 0xA2, 0x41, 0xBE, 0x91, 0x40, 0x44, 0x72, 0x4A, 0x33, 0x5D, - 0xC7, 0xCA, 0xD2, 0x0B, 0x28, 0x49, 0x7A, 0xB2, 0x73, 0x95, 0x49, 0x6B, 0x25, 0x06, 0xFE, 0xC8, - 0xD7, 0xF0, 0xC7, 0xA1, 0xD0, 0xA3, 0x83, 0x9B, 0x49, 0x2B, 0x83, 0xA4, 0x23, 0x64, 0x83, 0xA9, - 0x37, 0xE4, 0xBB, 0xA8, 0x2D, 0x2F, 0xCB, 0xB4, 0x16, 0x50, 0x70, 0x71, 0x83, 0xBB, 0x11, 0x30, - 0x52, 0x5A, 0xC4, 0x9E, 0x94, 0xA8, 0xC7, 0x8F, 0x10, 0x1F, 0x53, 0x4A, 0x20, 0x06, 0x20, 0xA6, - 0x40, 0xD0, 0xA7, 0x42, 0x8A, 0x54, 0xE6, 0x92, 0x53, 0x2A, 0x20, 0xCA, 0x48, 0xCD, 0xE2, 0xC1, - 0x85, 0x78, 0xD4, 0x46, 0xD6, 0x80, 0xFD, 0xDC, 0xBD, 0x73, 0x33, 0xDE, 0x90, 0x68, 0x09, 0x56, - 0x36, 0x3D, 0x9A, 0xA6, 0x52, 0x5C, 0x54, 0xC7, 0x19, 0xF8, 0xA8, 0xA1, 0x03, 0x5A, 0x23, 0x84, - 0x11, 0x1E, 0x84, 0x8A, 0x01, 0x40, 0x7F, 0x42, 0xC3, 0x1C, 0x22, 0x70, 0x08, 0x20, 0x82, 0xA0, - 0x7F, 0x49, 0x0D, 0xF7, 0x64, 0x05, 0xC9, 0xF8, 0xD8, 0x6D, 0x35, 0xF0, 0x9D, 0x66, 0x95, 0xEC, - 0x20, 0xA5, 0xBD, 0x68, 0x24, 0xFA, 0x64, 0x98, 0x1A, 0x50, 0x00, 0xAC, 0xD9, 0x01, 0xA0, 0x1E, - 0x24, 0x5E, 0x63, 0x2B, 0x3F, 0xEF, 0x04, 0x2A, 0xBB, 0x00, 0xAB, 0xBB, 0x8E, 0x87, 0x5F, 0x39, - 0x4F, 0x19, 0xA7, 0x39, 0x26, 0x00, 0x7B, 0x93, 0x68, 0x7A, 0x99, 0x30, 0x2E, 0xCE, 0x64, 0x1B, - 0x6A, 0x6C, 0xB4, 0xE4, 0xF5, 0xA9, 0x87, 0x15, 0x79, 0x3F, 0xC5, 0x8B, 0xCB, 0x0C, 0xF3, 0xBA, - 0x53, 0x79, 0x77, 0xB1, 0x86, 0x70, 0x21, 0x50, 0x66, 0x38, 0xB3, 0x29, 0x74, 0xB0, 0xFA, 0xA1, - 0x48, 0x82, 0x7A, 0x4F, 0xB7, 0x42, 0xE2, 0xC1, 0x44, 0xED, 0x81, 0xF9, 0xDC, 0xC2, 0xD8, 0xE1, - 0x94, 0x83, 0x5A, 0x0A, 0xB5, 0x02, 0x45, 0xC6, 0x95, 0xCD, 0x98, 0x35, 0x1D, 0x6A, 0x58, 0x88, - 0x61, 0xE0, 0xAF, 0xFE, 0x05, 0x0F, 0x1E, 0x1C, 0xC8, 0x55, 0x3F, 0xE1, 0x23, 0xE3, 0x7E, 0xF4, - 0x23, 0x3E, 0x3E, 0xAF, 0xF0, 0xF1, 0x79, 0x1D, 0x1F, 0xB4, 0xAA, 0x3C, 0x98, 0x0C, 0x80, 0xEC, - 0x19, 0xE1, 0x64, 0x4C, 0x13, 0x58, 0xC0, 0x43, 0x50, 0x25, 0x7F, 0x8B, 0xB3, 0x84, 0xFE, 0x98, - 0xB3, 0xDE, 0x84, 0x8D, 0xC4, 0x23, 0xFE, 0x8A, 0xD5, 0xFF, 0x82, 0x4B, 0x3C, 0x70, 0x3D, 0x97, - 0x79, 0x6D, 0x5A, 0x49, 0x28, 0x3F, 0x7E, 0x2B, 0x91, 0x7E, 0xE4, 0x42, 0x78, 0xA9, 0x38, 0xC8, - 0xDF, 0xB7, 0xF4, 0x00, 0xBC, 0x11, 0xF8, 0x29, 0x35, 0x75, 0xBC, 0x0B, 0xA5, 0xFC, 0x29, 0x30, - 0x64, 0xA8, 0xC0, 0x47, 0xDD, 0xD9, 0xDC, 0x12, 0xAE, 0x01, 0x8A, 0xF1, 0xA3, 0x29, 0xB0, 0xEA, - 0xC9, 0x02, 0xD7, 0x9E, 0x40, 0x26, 0x04, 0x91, 0xE0, 0x48, 0xC8, 0xA7, 0x8D, 0x2F, 0x07, 0x9B, - 0x37, 0x35, 0xC8, 0x43, 0x2E, 0xFC, 0x98, 0x2E, 0x0C, 0x36, 0x6F, 0xFE, 0x6D, 0x36, 0xC6, 0xCC, - 0x5A, 0x76, 0xA4, 0x96, 0x4C, 0xF6, 0xF4, 0x0B, 0xBF, 0x71, 0x09, 0x48, 0x5D, 0x49, 0x78, 0x45, - 0x34, 0x03, 0x6B, 0x43, 0x61, 0xE1, 0x07, 0xFF, 0x47, 0x09, 0xF8, 0x91, 0x9E, 0x07, 0xCE, 0xBD, - 0xE6, 0x3D, 0x5E, 0x2F, 0x3E, 0x85, 0xE9, 0x56, 0xE9, 0xC1, 0x4A, 0xC7, 0xEF, 0x53, 0x3A, 0x76, - 0x59, 0xA2, 0x14, 0x4A, 0x14, 0x59, 0x88, 0x1A, 0x6A, 0x50, 0x0E, 0x51, 0x98, 0x89, 0x17, 0xCD, - 0x81, 0x02, 0x9B, 0x73, 0x34, 0x5B, 0x3A, 0x02, 0x0F, 0xF4, 0xF5, 0x45, 0xEE, 0xFC, 0x74, 0x76, - 0x7A, 0x22, 0x44, 0x7C, 0xA5, 0x62, 0x22, 0xD0, 0xAA, 0x2E, 0x2C, 0x2F, 0xCF, 0x9C, 0x89, 0xE4, - 0xA1, 0x28, 0x75, 0x30, 0x31, 0x28, 0x87, 0xFE, 0x74, 0x31, 0xFC, 0x0A, 0x71, 0xD6, 0xD0, 0xCF, - 0x52, 0x48, 0x58, 0x5B, 0x36, 0xA2, 0xF7, 0xFB, 0x97, 0xF6, 0xAE, 0xDD, 0x84, 0xBA, 0x00, 0xB4, - 0x0A, 0x69, 0x19, 0xEE, 0x7D, 0xFE, 0xB7, 0x90, 0xB7, 0xFF, 0x1E, 0x32, 0x83, 0xA8, 0x95, 0x42, - 0x58, 0x2A, 0xF0, 0xAB, 0xB8, 0x93, 0x24, 0x9A, 0x4A, 0xB4, 0xE3, 0x24, 0xC1, 0x4B, 0xE9, 0x43, - 0x85, 0xA2, 0x0D, 0x61, 0x31, 0xA5, 0x89, 0xE6, 0x47, 0x34, 0xD5, 0x78, 0x24, 0xB4, 0x34, 0x8B, - 0x63, 0x68, 0x5C, 0x56, 0xF4, 0x61, 0xEB, 0xC5, 0xEB, 0xCB, 0xFB, 0x8C, 0x66, 0xD4, 0xCF, 0x97, - 0x69, 0x52, 0xD1, 0x0B, 0x56, 0x50, 0xDF, 0x10, 0xEE, 0x7E, 0xB9, 0xC9, 0xEB, 0xA9, 0x8C, 0x73, - 0x8C, 0xA2, 0x1B, 0x2D, 0x35, 0x07, 0xE9, 0x26, 0x40, 0xD5, 0xE5, 0x59, 0x10, 0xCC, 0xDB, 0x2B, - 0xB4, 0xA0, 0xF1, 0x8A, 0x44, 0x24, 0x9F, 0xCB, 0x67, 0x7F, 0xE4, 0xC9, 0xA9, 0xE2, 0x82, 0x50, - 0xF2, 0x54, 0xA9, 0x36, 0xAD, 0x0D, 0x63, 0x83, 0x6A, 0x8C, 0xA7, 0x82, 0x70, 0x0F, 0xAF, 0x51, - 0xE9, 0xC2, 0x2C, 0x6A, 0x29, 0xDC, 0xDE, 0x46, 0x5F, 0xCB, 0x6D, 0xE9, 0x89, 0x7C, 0x2A, 0x25, - 0xE3, 0xAE, 0xAE, 0x63, 0x55, 0x45, 0xB1, 0x3E, 0x25, 0x61, 0x5A, 0x26, 0x5B, 0x54, 0x06, 0x26, - 0x77, 0x0B, 0x70, 0x9B, 0x06, 0x29, 0x1C, 0xBD, 0x7E, 0x7F, 0xCE, 0x46, 0xD1, 0xCE, 0x11, 0x80, - 0x69, 0xC5, 0x3E, 0x93, 0xD7, 0xE0, 0x24, 0xCC, 0x73, 0x07, 0x32, 0xE9, 0x4A, 0x03, 0x0E, 0xA9, - 0x98, 0x44, 0xFE, 0x81, 0x7E, 0xA0, 0x3B, 0x3A, 0xFC, 0xBB, 0x09, 0x35, 0x47, 0xCD, 0xA5, 0xD0, - 0xA4, 0xFA, 0x74, 0x70, 0xF5, 0x06, 0xC2, 0x53, 0x0C, 0xA5, 0x01, 0x17, 0x50, 0x34, 0xD7, 0x74, - 0x7C, 0x7A, 0x7D, 0x0C, 0x29, 0xC8, 0x7F, 0x21, 0x37, 0x66, 0xBB, 0xAA, 0x6C, 0xB8, 0xF3, 0xEA, - 0x75, 0x56, 0x2E, 0x03, 0x7A, 0x61, 0x8C, 0x58, 0x0F, 0x29, 0x7E, 0xFB, 0x7B, 0xF4, 0x9E, 0x8D, - 0x15, 0xD2, 0x6A, 0x5D, 0x6F, 0xCE, 0x76, 0x90, 0x67, 0x89, 0xD5, 0x43, 0x2C, 0x70, 0x97, 0x1F, - 0x29, 0x59, 0x95, 0x35, 0xDC, 0xF6, 0x48, 0x10, 0xE0, 0xC7, 0x5A, 0x03, 0x1B, 0x6A, 0x22, 0xB2, - 0xD4, 0x42, 0x22, 0x29, 0x08, 0x90, 0xD2, 0x3E, 0x84, 0x39, 0xD3, 0x92, 0x65, 0x86, 0xB2, 0xA1, - 0xBC, 0xFF, 0xC5, 0x9A, 0xA3, 0x64, 0x46, 0xE8, 0xCE, 0xF9, 0x6C, 0x73, 0x53, 0xD8, 0x85, 0x99, - 0x18, 0x05, 0x52, 0x8A, 0x01, 0x1C, 0x9A, 0x7D, 0x68, 0x2D, 0x8C, 0xB2, 0x90, 0x58, 0xAB, 0x3D, - 0xD2, 0xB6, 0x51, 0x55, 0x03, 0x54, 0x7C, 0x46, 0x01, 0x03, 0xCE, 0xB2, 0x24, 0x80, 0xA8, 0x8B, - 0x39, 0xBA, 0xB2, 0x2D, 0xC5, 0xBA, 0xD0, 0x84, 0x0E, 0xEC, 0x67, 0xC8, 0x12, 0x95, 0x97, 0xAD, - 0xA2, 0x27, 0x12, 0xC5, 0x77, 0x95, 0x9E, 0xC8, 0x6F, 0xE5, 0x84, 0xAA, 0xC8, 0x77, 0x88, 0x2F, - 0x13, 0x5C, 0xD4, 0xD1, 0x13, 0xA0, 0x24, 0x83, 0x52, 0x34, 0x60, 0x2A, 0x2C, 0x37, 0xEE, 0xEB, - 0xD3, 0xE9, 0xB4, 0x8E, 0xDF, 0x6A, 0xEB, 0x70, 0x82, 0xB2, 0x02, 0x5F, 0x5F, 0xC7, 0x21, 0x47, - 0x15, 0x58, 0xF8, 0x6E, 0xE1, 0xAC, 0xBA, 0xE8, 0x42, 0x7F, 0x2B, 0xDE, 0xD4, 0xAA, 0xD2, 0x59, - 0xE1, 0x73, 0x79, 0xDB, 0x7B, 0x3B, 0x2B, 0x20, 0x32, 0xC4, 0xAF, 0xB2, 0x90, 0x69, 0x20, 0x0D, - 0x3B, 0xE5, 0x46, 0x56, 0x25, 0x85, 0x65, 0x5C, 0xB0, 0xE3, 0x2C, 0x9D, 0x18, 0x33, 0x60, 0xDD, - 0x11, 0x96, 0xD2, 0x95, 0x43, 0x2D, 0x65, 0xB7, 0x0E, 0xB7, 0x0A, 0xFB, 0x70, 0x30, 0x83, 0x94, - 0x79, 0xFB, 0xF3, 0x4F, 0x39, 0x5B, 0xDE, 0xF6, 0x92, 0x62, 0x71, 0xE1, 0xF3, 0xFC, 0xA9, 0x35, - 0xAF, 0x69, 0xA5, 0xD1, 0xAF, 0xC4, 0x97, 0xBD, 0x46, 0xFE, 0x19, 0x3B, 0xFF, 0x9C, 0xAD, 0x81, - 0xB1, 0x43, 0x23, 0x2A, 0xDC, 0x4C, 0x8C, 0xEA, 0x2F, 0x34, 0xE6, 0x63, 0x79, 0x29, 0xBF, 0x2D, - 0xA0, 0x54, 0xA9, 0xD3, 0x68, 0x78, 0x3E, 0xFF, 0x9A, 0x42, 0x19, 0x1D, 0x65, 0xFE, 0x28, 0x20, - 0x09, 0xC5, 0x82, 0xA3, 0x41, 0xBE, 0x92, 0xFB, 0x46, 0xC0, 0x86, 0x69, 0x03, 0x93, 0x6D, 0xCB, - 0xDE, 0xB2, 0x77, 0x71, 0x64, 0x7F, 0x4D, 0xF7, 0x57, 0x4F, 0xD8, 0x5F, 0x34, 0x69, 0x58, 0x0B, - 0xE7, 0xB5, 0xAB, 0x8A, 0x4D, 0x6A, 0x83, 0xFB, 0xC4, 0xA7, 0x70, 0x3D, 0x6F, 0xB3, 0xCC, 0xB6, - 0x1A, 0xE4, 0x5F, 0x60, 0xD4, 0x31, 0xBA, 0x95, 0x2F, 0x92, 0xF4, 0x81, 0x7B, 0x18, 0x5B, 0x17, - 0x54, 0x26, 0x70, 0x49, 0xD5, 0x87, 0x34, 0xB9, 0xD3, 0x9C, 0x2F, 0x39, 0xC3, 0xB7, 0x3C, 0xA8, - 0x03, 0xE4, 0x37, 0x9C, 0x72, 0x39, 0xB0, 0xBF, 0x07, 0x5D, 0x33, 0x2A, 0x41, 0x79, 0xB1, 0x26, - 0x9B, 0xE6, 0x7C, 0x02, 0x82, 0x01, 0x70, 0xB1, 0xA3, 0x48, 0xCD, 0x2B, 0xCB, 0x98, 0x9B, 0x57, - 0x96, 0x54, 0xE2, 0x5F, 0x59, 0xCC, 0xDB, 0x9F, 0xFC, 0xDB, 0x4C, 0xF9, 0x7F, 0x5B, 0x28, 0x36, - 0x32, 0xF9, 0xE1, 0x09, 0xF7, 0x56, 0x3F, 0x45, 0xAD, 0x47, 0x51, 0xBB, 0xF7, 0xFF, 0x17, 0x53, - 0xE8, 0x9D, 0x36, 0x92, 0x29, 0x00, 0x00}; - #define SPIFFS_MAXLENGTH_FILEPATH 32 -const char *excludeListFile = "/.exclude.files"; -typedef struct ExcludeListS { - char *item; - ExcludeListS *next; -} ExcludeList; - -static ExcludeList *excludes = NULL; - -static bool matchWild(const char *pattern, const char *testee) { - const char *nxPat = NULL; - const char *nxTst = NULL; - - while (*testee) { - if ((*pattern == '?') || (*pattern == *testee)) { - ++pattern; - ++testee; - continue; - } - if (*pattern == '*') { - nxPat = pattern++; - nxTst = testee; - continue; - } - if (nxPat) { - pattern = nxPat + 1; - testee = ++nxTst; - continue; - } - return false; - } - while (*pattern == '*') { - ++pattern; - } - return (*pattern == 0); -} - -static bool addExclude(const char *item) { - size_t len = strlen(item); - if (!len) { - return false; - } - ExcludeList *e = (ExcludeList *)malloc(sizeof(ExcludeList)); - if (!e) { - return false; - } - e->item = (char *)malloc(len + 1); - if (!e->item) { - free(e); - return false; - } - memcpy(e->item, item, len + 1); - e->next = excludes; - excludes = e; - return true; -} - -static void loadExcludeList(fs::FS &_fs, const char *filename) { - static char linebuf[SPIFFS_MAXLENGTH_FILEPATH]; - fs::File excludeFile; - if (filename[0] != '/') { - excludeFile = _fs.open("/" + ((String)filename), "r"); - } else { - excludeFile = _fs.open(filename, "r"); - } - if (!excludeFile) { - // addExclude("/*.js.gz"); - return; - } -#ifdef ESP32 - if (excludeFile.isDirectory()) { - excludeFile.close(); - return; - } -#endif - if (excludeFile.size() > 0) { - uint8_t idx; - bool isOverflowed = false; - while (excludeFile.available()) { - linebuf[0] = '\0'; - idx = 0; - int lastChar; - do { - lastChar = excludeFile.read(); - if (lastChar != '\r') { - linebuf[idx++] = (char)lastChar; - } - } while ((lastChar >= 0) && (lastChar != '\n') && (idx < SPIFFS_MAXLENGTH_FILEPATH)); - - if (isOverflowed) { - isOverflowed = (lastChar != '\n'); - continue; - } - isOverflowed = (idx >= SPIFFS_MAXLENGTH_FILEPATH); - linebuf[idx - 1] = '\0'; - if (!addExclude(linebuf)) { - excludeFile.close(); - return; - } - } - } - excludeFile.close(); -} - -static bool isExcluded(fs::FS &_fs, const char *filename) { - if (excludes == NULL) { - loadExcludeList(_fs, excludeListFile); - } - ExcludeList *e = excludes; - while (e) { - if (matchWild(e->item, filename)) { - return true; - } - e = e->next; - } - return false; -} - -// WEB HANDLER IMPLEMENTATION - -#ifdef ESP32 SPIFFSEditor::SPIFFSEditor(const fs::FS &fs, const String &username, const String &password) -#else -SPIFFSEditor::SPIFFSEditor(const String &username, const String &password, const fs::FS &fs) -#endif : _fs(fs), _username(username), _password(password), _authenticated(false), _startTime(0) { } @@ -406,24 +19,20 @@ bool SPIFFSEditor::canHandle(AsyncWebServerRequest *request) { if (!request->_tempFile) { return false; } -#ifdef ESP32 if (request->_tempFile.isDirectory()) { request->_tempFile.close(); return false; } -#endif } if (request->hasParam("download")) { request->_tempFile = _fs.open("/" + request->arg("download"), "r"); if (!request->_tempFile) { return false; } -#ifdef ESP32 if (request->_tempFile.isDirectory()) { request->_tempFile.close(); return false; } -#endif } request->addInterestingHeader("If-Modified-Since"); return true; @@ -442,46 +51,22 @@ void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request) { if (request->method() == HTTP_GET) { if (request->hasParam("list")) { const String path = request->getParam("list")->value(); -#ifdef ESP32 + File dir = _fs.open(path); -#else - Dir dir = _fs.openDir(path); -#endif String output = "["; -#ifdef ESP32 File file = dir.openNextFile(); while (file) { -#else - while (dir.next()) { - fs::File entry = dir.openFile("r"); -#endif - if (isExcluded(_fs, file.name())) { -#ifdef ESP32 - file = dir.openNextFile(); -#endif - continue; - } if (output != "[") { output += ','; } - output += "{\"type\":\""; - output += "file"; - output += "\",\"name\":\""; - output += file.name(); - output += "\",\"size\":"; - output += file.size(); - output += ",\"ver\":"; - output += file.getLastWrite(); - output += "}"; -#ifdef ESP32 + if (file.isDirectory()) { + output += "{\"type\":\"dir\",\"name\":\"" + String(file.name()) + "\",\"size\":" + file.size() + "}"; + } else { + output += "{\"type\":\"file\",\"name\":\"" + String(file.name()) + "\",\"size\":" + file.size() + "}"; + } file = dir.openNextFile(); -#else - file.close(); -#endif } -#ifdef ESP32 dir.close(); -#endif output += "]"; request->send(200, "application/json", output); } else if (request->hasParam("edit") || request->hasParam("download")) { @@ -491,8 +76,7 @@ void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request) { if (request->header("If-Modified-Since").equals(buildTime)) { request->send(304); } else { - AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", edit_htm_gz, edit_htm_gz_len); - response->addHeader("Content-Encoding", "gzip"); + AsyncWebServerResponse *response = request->beginResponse(_fs, "/www/edit.html"); response->addHeader("Last-Modified", buildTime); request->send(response); } diff --git a/esp32_fw/src/contentmanager.cpp b/esp32_fw/src/contentmanager.cpp index 32970c2f..25bf46df 100644 --- a/esp32_fw/src/contentmanager.cpp +++ b/esp32_fw/src/contentmanager.cpp @@ -56,7 +56,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { deserializeJson(doc, taginfo->modeConfigJson); JsonObject cfgobj = doc.as(); - Serial.println("Updating " + dst + " mode " + String(taginfo->contentMode) + " nextupdate " + String(taginfo->nextupdate)); + wsLog("Updating " + dst + " mode " + String(taginfo->contentMode)); taginfo->nextupdate = now + 600; switch (taginfo->contentMode) { @@ -67,7 +67,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { if (prepareDataAvail(&filename, DATATYPE_IMGRAW, mac, cfgobj["timetolive"].as())) { cfgobj["#fetched"] = true; } else { - wsString("Error accessing " + filename); + wsErr("Error accessing " + filename); } taginfo->nextupdate = 3216153600; } @@ -75,9 +75,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { case Today: - Serial.println("heap voor drawDate: " + String(ESP.getFreeHeap())); drawDate(filename); - Serial.println("heap na drawDate: " + String(ESP.getFreeHeap())); // updateTagImage(filename, mac, (midnight - now) / 60 - 10); updateTagImage(filename, mac, 60); taginfo->nextupdate = midnight; @@ -86,10 +84,8 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { case CountDays: if (buttonPressed) cfgobj["counter"] = 0; - Serial.println("heap voor drawnumber: " + String(ESP.getFreeHeap())); drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"]); - Serial.println("heap na drawnumber: " + String(ESP.getFreeHeap())); - updateTagImage(filename, mac, 60); + updateTagImage(filename, mac, (buttonPressed?0:60)); cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1; taginfo->nextupdate = midnight; break; @@ -97,12 +93,10 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { case CountHours: if (buttonPressed) cfgobj["counter"] = 0; - Serial.println("heap voor drawnumber: " + String(ESP.getFreeHeap())); drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"]); - Serial.println("heap na drawnumber: " + String(ESP.getFreeHeap())); // updateTagImage(&filename, mac, (3600 - now % 3600) / 60); // taginfo->nextupdate = now + 3600 - (now % 3600); - updateTagImage(filename, mac, 3); + updateTagImage(filename, mac, (buttonPressed?0:3)); cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1; taginfo->nextupdate = now + 300; break; @@ -119,7 +113,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { if (prepareDataAvail(&filename, DATATYPE_UPDATE, mac, cfgobj["timetolive"].as())) { cfgobj["#fetched"] = true; } else { - wsString("Error accessing " + filename); + wsErr("Error accessing " + filename); } cfgobj["filename"]=""; taginfo->nextupdate = 3216153600; @@ -166,15 +160,15 @@ void drawDate(String &filename) { long w = 296, h = 128; // mag staand of liggend spr.createSprite(w, h); if (spr.getPointer() == nullptr) { - Serial.println("Failed to create sprite in drawDate"); + wsErr("Failed to create sprite in drawDate"); } spr.setColorDepth(8); spr.fillSprite(TFT_WHITE); spr.setTextDatum(TC_DATUM); - spr.loadFont("calibrib62", LittleFS); + spr.loadFont("fonts/calibrib62", LittleFS); spr.setTextColor(TFT_RED, TFT_WHITE); spr.drawString(Dag[timeinfo.tm_wday], w / 2, 10); - spr.loadFont("calibrib50", LittleFS); + spr.loadFont("fonts/calibrib50", LittleFS); spr.setTextColor(TFT_BLACK, TFT_WHITE); spr.drawString(String(timeinfo.tm_mday) + " " + Maand[timeinfo.tm_mon], w / 2, 73); spr.unloadFont(); @@ -192,7 +186,7 @@ void drawNumber(String &filename, int32_t count, int32_t thresholdred) { long w = 296, h = 128; spr.createSprite(w, h); if (spr.getPointer() == nullptr) { - Serial.println("Failed to create sprite in drawNumber"); + wsErr("Failed to create sprite in drawNumber"); } spr.setColorDepth(8); spr.fillSprite(TFT_WHITE); @@ -202,9 +196,9 @@ void drawNumber(String &filename, int32_t count, int32_t thresholdred) { } else { spr.setTextColor(TFT_BLACK, TFT_WHITE); } - String font = "numbers1-2"; - if (count>999) font="numbers2-2"; - if (count>9999) font="numbers3-2"; + String font = "fonts/numbers1-2"; + if (count > 999) font = "fonts/numbers2-2"; + if (count > 9999) font = "fonts/numbers3-2"; spr.loadFont(font, LittleFS); spr.drawString(String(count), w/2, h/2+10); spr.unloadFont(); @@ -233,7 +227,11 @@ bool getImgURL(String &filename, String URL, time_t fetched) { jpg2grays(filename, filename); } } else { - Serial.println("http " + String(httpCode)); + if (httpCode!=304) { + wsErr("http " + URL + " " + String(httpCode)); + } else { + wsLog("http " + URL + " " + String(httpCode)); + } } http.end(); return (httpCode == 200); diff --git a/esp32_fw/src/main.cpp b/esp32_fw/src/main.cpp index e46ea598..f9552de1 100644 --- a/esp32_fw/src/main.cpp +++ b/esp32_fw/src/main.cpp @@ -35,7 +35,7 @@ void setup() { // https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv init_web(); - loadDB("/tagDB.json"); + loadDB("/current/tagDB.json"); xTaskCreate(timeTask, "timed tasks", 10000, NULL, 2, NULL); xTaskCreate(zbsRxTask, "zbsRX Process", 10000, NULL, 2, NULL); diff --git a/esp32_fw/src/makeimage.cpp b/esp32_fw/src/makeimage.cpp index 7d23a013..1d6762db 100644 --- a/esp32_fw/src/makeimage.cpp +++ b/esp32_fw/src/makeimage.cpp @@ -4,6 +4,7 @@ #include #include #include +#include TFT_eSPI tft = TFT_eSPI(); TFT_eSprite spr = TFT_eSprite(&tft); @@ -22,7 +23,7 @@ void jpg2grays(String filein, String fileout) { spr.createSprite(w, h); if (spr.getPointer() == nullptr) { - Serial.println("Failed to create sprite in jpg2grays"); + wsErr("Failed to create sprite in jpg2grays"); } spr.setColorDepth(8); spr.fillSprite(TFT_WHITE); diff --git a/esp32_fw/src/newproto.cpp b/esp32_fw/src/newproto.cpp index 76fbfd40..727fc0d2 100644 --- a/esp32_fw/src/newproto.cpp +++ b/esp32_fw/src/newproto.cpp @@ -71,12 +71,11 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t if (hdr.width == 296 && hdr.height == 128) { //sorry, can't rotate Serial.println("when using BMP files, remember to only use 128px width and 296px height"); - wsString("when using BMP files, remember to only use 128px width and 296px height"); + wsErr("when using BMP files, remember to only use 128px width and 296px height"); return false; } if (hdr.sig[0] == 'B' && hdr.sig[1] == 'M' && hdr.bpp == 24) { Serial.println("converting 24bpp bmp to grays"); - wsString("converting 24bpp bmp to grays"); char fileout[64]; sprintf(fileout, "/temp/%02X%02X%02X%02X%02X%02X.bmp\0", dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]); bmp2grays(*filename,(String)fileout); @@ -88,7 +87,6 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t if (filename->endsWith(".jpg") || filename->endsWith(".JPG")) { Serial.println("converting jpg to grays"); - wsString("converting jpg to grays"); char fileout[64]; sprintf(fileout, "/temp/%02X%02X%02X%02X%02X%02X.bmp\0", dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]); jpg2grays(*filename, (String)fileout); @@ -114,7 +112,7 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t taginfo = tagRecord::findByMAC(mac); if (taginfo != nullptr) { if (memcmp(md5bytes, taginfo->md5pending, 16) == 0) { - wsString("new image is the same as current image. not updating tag."); + Serial.println("new image is the same as current image. not updating tag."); wsSendTaginfo(mac); return false; } @@ -156,7 +154,7 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t } dstfile.close(); - wsString("new image pending: " + String(dst_path)); + wsLog("new image pending: " + String(dst_path)); if (taginfo != nullptr) { taginfo->pending = true; taginfo->CheckinInMinPending = nextCheckin + 1; @@ -164,7 +162,7 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t } } else { - Serial.println("firmware upload pending"); + wsLog("firmware upload pending"); } file.close(); @@ -211,7 +209,7 @@ void processBlockRequest(struct espBlockRequest* br) { sendBlock(pd->data + (br->blockId * BLOCK_DATA_SIZE), len); char buffer[64]; sprintf(buffer, "< Block Request received for MD5 %llu, block %d\n\0", br->ver, br->blockId); - wsString((String)buffer); + wsLog((String)buffer); Serial.printf("blockId); } @@ -220,7 +218,7 @@ void processXferComplete(struct espXferComplete* xfc) { uint8_t src[8]; *((uint64_t*)src) = swap64(*((uint64_t*)xfc->src)); sprintf(buffer, "< %02X%02X%02X%02X%02X%02X reports xfer complete\n\0", src[2], src[3], src[4], src[5], src[6], src[7]); - wsString((String)buffer); + wsLog((String)buffer); Serial.print(buffer); uint8_t mac[6]; memcpy(mac, src + 2, sizeof(mac)); @@ -275,6 +273,5 @@ void processDataReq(struct espAvailDataReq* eadr) { sprintf(buffer, "", 4) == 0) { Serial.print(">SYNC BURST\n"); - wsString(">SYNC BURST"); RXState = ZBS_RX_WAIT_HEADER; } if (strncmp(cmdbuffer, "XFC>", 4) == 0) { diff --git a/esp32_fw/src/web.cpp b/esp32_fw/src/web.cpp index 5b8618a3..1f4514d4 100644 --- a/esp32_fw/src/web.cpp +++ b/esp32_fw/src/web.cpp @@ -143,7 +143,7 @@ void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType } } -void wsString(String text) { +void wsLog(String text) { DynamicJsonDocument doc(100); doc["logMsg"] = text; xSemaphoreTake(wsMutex, portMAX_DELAY); @@ -151,6 +151,14 @@ void wsString(String text) { xSemaphoreGive(wsMutex); } +void wsErr(String text) { + DynamicJsonDocument doc(100); + doc["errMsg"] = text; + xSemaphoreTake(wsMutex, portMAX_DELAY); + ws.textAll(doc.as()); + xSemaphoreGive(wsMutex); +} + void wsSendSysteminfo() { DynamicJsonDocument doc(250); JsonObject sys = doc.createNestedObject("sys"); @@ -181,11 +189,6 @@ void wsSendTaginfo(uint8_t mac[6]) { void init_web() { LittleFS.begin(true); - if (!LittleFS.exists("/.exclude.files")) { - Serial.println("littlefs exclude.files aanmaken"); - File f = LittleFS.open("/.exclude.files", "w"); - f.close(); - } if (!LittleFS.exists("/current")) { LittleFS.mkdir("/current"); } @@ -214,8 +217,9 @@ void init_web() { ESP.restart(); }); - server.serveStatic("/", LittleFS, "/").setDefaultFile("index.htm"); - + server.serveStatic("/current", LittleFS, "/current/"); + server.serveStatic("/", LittleFS, "/www/").setDefaultFile("index.html"); + server.on( "/imgupload", HTTP_POST, [](AsyncWebServerRequest *request) { request->send(200); @@ -282,7 +286,7 @@ void init_web() { taginfo->model = atoi(request->getParam("model", true)->value().c_str()); taginfo->nextupdate = 0; wsSendTaginfo(mac); - saveDB("/tagDB.json"); + saveDB("/current/tagDB.json"); request->send(200, "text/plain", "Ok, saved"); } else { request->send(200, "text/plain", "Error while saving: mac not found"); From 45c4867444d0c213afcfcf1129e82c9ff4cfe826 Mon Sep 17 00:00:00 2001 From: Nic Limper Date: Sat, 4 Feb 2023 01:43:10 +0100 Subject: [PATCH 2/2] Weather displays - added weather content - adapted all content to work with 1.54" displays --- esp32_fw/data/fonts/bahnschrift30.vlw | Bin 0 -> 24433 bytes esp32_fw/data/fonts/bahnschrift70.vlw | Bin 0 -> 22292 bytes esp32_fw/data/fonts/bahnschrift80.vlw | Bin 0 -> 28768 bytes esp32_fw/data/fonts/calibrib30.vlw | Bin 0 -> 8628 bytes esp32_fw/data/fonts/calibrib35.vlw | Bin 0 -> 11284 bytes esp32_fw/data/fonts/calibrib40.vlw | Bin 0 -> 14772 bytes esp32_fw/data/fonts/weathericons30.vlw | Bin 0 -> 28057 bytes esp32_fw/data/fonts/weathericons78.vlw | Bin 0 -> 118301 bytes esp32_fw/data/www/index.html | 12 +- esp32_fw/data/www/main.js | 4 +- esp32_fw/include/contentmanager.h | 10 +- esp32_fw/include/settings.h | 3 + esp32_fw/src/contentmanager.cpp | 352 ++++++++++++++++++++++--- esp32_fw/src/main.cpp | 2 +- esp32_fw/src/newproto.cpp | 1 + esp32_fw/src/web.cpp | 4 +- 16 files changed, 336 insertions(+), 52 deletions(-) create mode 100644 esp32_fw/data/fonts/bahnschrift30.vlw create mode 100644 esp32_fw/data/fonts/bahnschrift70.vlw create mode 100644 esp32_fw/data/fonts/bahnschrift80.vlw create mode 100644 esp32_fw/data/fonts/calibrib30.vlw create mode 100644 esp32_fw/data/fonts/calibrib35.vlw create mode 100644 esp32_fw/data/fonts/calibrib40.vlw create mode 100644 esp32_fw/data/fonts/weathericons30.vlw create mode 100644 esp32_fw/data/fonts/weathericons78.vlw diff --git a/esp32_fw/data/fonts/bahnschrift30.vlw b/esp32_fw/data/fonts/bahnschrift30.vlw new file mode 100644 index 0000000000000000000000000000000000000000..9c3ee829bf5dd3ec0972195459480f98bd07cfd3 GIT binary patch literal 24433 zcmeHMhq5JEaU28)FT8hw_g=z#@4fe)MZ$X|;G6hEsI1Jas?*)~&Ykz(%q|-`5q(Zo zW|lkM_f2f0@)9X^O3UuEl#W##`6e0h((OK7s-EO zoKKehkT|hk9M3c3d3-$JpB|67mxPTxPtpn7+)ITa<|(pW?`7emmxsplaB+@%dHBzb z2mGVsc}6_YVZ9=rE$6sb28X%uJT9Ku=c|N0HqIyLaWAh9-@W)W_nP2P8-4soJdcXU z+-t)Ihi4o2y1N@ zjpujVy8^@S!0!)tVD8;vpNe}=aG2NM(!9_22L9}L@O|N&_wqjZ_&scYNADMhxy)f6 ztPjM4Jd<;u=eQ3l4s-c8=A0iAhdc4_)7*y#=XYf8BjS#F`RL%b^*$yJdHlD>IX^CL zzIXp_KOqj^?e<r z$bVn(eqHZ7VIx0!!86x)!@=KT_k#Hy_r0(&7rsH<8*Fpm4;wR?oAdoZT7SDU?uTdM zel+5?^?s~4|7}7&zlWb}a?X19iNoA}f1cr|;&3le0>L0z^ zpNaE3n)mYa;LzLC;(3;A*ZYNh_c>?yr8wX9%J+QW zeAmAV8^2e4Gl+$4?)PCsi(YWoGv^=VyVlJ4M{$?~-|gi7G~)bCxZaV~^M45+@7>?T{B8a<@NJxX`CD-K4Y?kA^1JbK)x$$;md?#Wa<1@2VdVxR_e2hXkT@eeeJwELK|Q+$ru<6&#_J%k%+4eO4O* zDQur{1l^iLx>M*iLK>qg@S2w-3d4#8lWTF5xftsxLZCx=Vi!UtcZn=X4Kfuz5SLOO zNm5H&=@~w*~ z(n_PYYU}{nb0tzLC{T+#4k&ok?xrTQVy8T$J;jZhmiS~Ldp^~fU7y+QL80O~-FbIc zQ3((7mUKu8+Yhx+r=7T_%!f^?V-uvFIJ^LHDazRr1m+-FuEK^>kdWgtq2Sxvu6^!A zF7TJ;ocdi}T-FXe23!vTWsg3P$M`TOJng$JaVxXt#=Zf`HKvH_5$yyt(<@USXbE+_ zD~Wc?ym=6@x_?^kIlbb;8}@1{ zMHNBhs>obK55%E0x`_jGcBxcoSi<7Qfn+7xa{K@ zM>|WwGPcgu;yd+bMdS6TBS59 zb8rH7S2kvW6A~V@)9Xe6nxJ7X&)7Oa<^htol!lzLL5a`PHhW;88an4x0mo&53aqS# zaSd&!h0Q1_AxmEnQCM2!a@k?B-P9rxmv!(uDouyx8JiS$0cI;RziakcnlhT(S*5Wt z{u9M!P#FhF7*zV;2|n=>j+kL44J8dHW6FcwvUwiJCS+;V0GS|FZBm&iOd0d=xQbzb zuSH&hz+1PToz;DUAE?|wbeE1##GApK z+h9i|B2C+{DvCcx6(AR(GaIty@& zgrt^om(P?N7ou1|XTfW@PKA?;>20f;$z@Q0chp7(S8;j&e|5Z| zzPi2|FzxB1YM?(GW4dd(@|5BM=q-JEhCWZuj!{~6JIjM_er6GN6{e~!9qgQ|q&^)} zMQ_A7UFRx{TUuVp-nmNZ>&0YrcIP~9l`p=$+x`hUNaB<@gTBD+aw$IVD(%e7fGVVT z`%tzTWPXxWDhz;H1*bo9gbz>0kXfTF_smtpmmT-4tMi2}YxY<*Gyf~tn8JMylV4DM zO#B!*aj0o1u+aI{0UUn1?bBU+^njRy)3A62Ti-7EQZpZD5194LU#c#<}1q z48Xjt#ScOIN3G8LN^QuqOy0>1UNSvc987z`;$_s8M8rTG-_Q;*&|!rbYTy776lF`6 z)xs0vH?>0mSVPecNuV^R3=|CI5HARGPt5^>2&~FO_Y!0iLf2QNg!Nj)nzmclXduhe z*>xK*XtAd4%Aan?%O%}z`m0NC+fsJqM5pFa9mS3&J36gVuv0Qh)p01kB2wC!QYm)~ zSn_JpC9J*c;+h{ra1hi+!`#YqYoIpsyW6Caqm{d|8ClZoYKCf`%q?7jbJaR^q4bwt z*r%)C6EBOo6;C8V=T&Wda<=3Pg^D zLCZ}mM<8F#Upsj<}>O$WBNfeHk(_2CW}sA-8EHQnt* z)>L$f$*gV;8xU3&%!>3kP3!I`qrQS7^{ktj23ftZglSbQH_KX*&O{2ZB2Hk#J@Pm7 z_#aVDNJXYH#FP1rrEEI3FFLEpwx8igqTykf2dg_r0a6itL}<<+FxVziyQ%Ip7zC*;5PoHIt^d)%laB}1SrYwItYY#UJEa@uj$b5 zR2FuM%;$R@sZZcgvAC{ zEXSZBQFP(x3Q1xClM^O9t>FxFdu6MBggOlhIOi~fZoJF^WnBAdZQN~G(5R%t1=gVU zGJ3cySZ#0Jl%Hf#(^Il=`)M^#!t{EUiiXL`=n|k~RXc~S=~T8lt;7&6!?eyunS`)K z3Mx0RradOzc2~VtO(Qds9ht#-57;Pi$*QhGJnhAq#I#R}P7-%1`jSca8oh^{{2pza zCgydN9bd<^+sPhHP3M%od}ntoy0i~>w0_pJ+@+b0N8MyPDGJ?YMQ4FL5cW-D>?*pO zcW2vVHm`3%@sNOqJ7=S>064c@SvpiRi|j<%8q=t%9iF*oJ8lYRuApr4{yQQ&AFlNk zlANNcqcd!nGmpm>wwCTxcaCRGUJA=EWyQL>W~Ol0^U~XS4ew3`;n*CfF`N=)NlJ#< zd`FY!TxrLaIyu+0)jyNE>X;&{5LZDQ*DT5myo=>>+<;D%f(O%W-ISe4Gr8(bTQep! z?B&Brr(^401YOc+N3RxRIr6kGe_x||UmS~6qxB*KXRWmhd8hO@hv!$obgqe(Hr-$( z(>WfUG(}v;zSHlaYSD-tJ?ozdn(8)T+fd7P2q3HsqInG`lPg;ll0go;>iM6i)~h}P z?iXh%U6)1lcDv^A^@Us`tOxOXu#bN8t-GmHJi$k{XIiimcT^rOn>bV}`hWYh~Bn_e8V`mqtsO~ zIwyxr(nJ$XWRI$5OUinHYoooq4eV*dlaWU{mTy+0W7^i?Qdc z+Uk5axQn_C^c&tewu8po#UqKq)_i z+o@EedgpI8Gb}x-4JWP&00aWdvGQnpfRa`WMWwfk?g|X6Tcmx^Ocp^*8Wb=c5QAaJ zgu!>f%R|X#7rTW}q_7(5EKpg=v-aJkq|joPt;9#_kwE;AdMmg{{jxL&voel@m5qqa1rfL?+#HAXkQgm)pP=_2@ zqwEpAV1BDB7~bi{9{3ue3O~g>lO@`>bcg;bAIY;@`}_1Y_?loZyntWk^N3mc2DiMd z`WiAZAQ(yA;8_Wm!CX>>Zm1rzY7CzoD~Ia&LIo+rJbNDqL!GEj24@*Wr-As55gaux ZVLr~sy#IUm{o=PjxbHVV{qgTs=bvv5Kk5Jg literal 0 HcmV?d00001 diff --git a/esp32_fw/data/fonts/bahnschrift70.vlw b/esp32_fw/data/fonts/bahnschrift70.vlw new file mode 100644 index 0000000000000000000000000000000000000000..3c516de3315e1741dba451630ef234e2b408486c GIT binary patch literal 22292 zcmeI2>xyhe421_&6a*0z1#gIV5XJiqe<}DL2SJWM6j2|{Hyo3ytd*6j>fJLvqceTZ zZrG=i^(CoPYIl1SFD@=#xVX4@)}Ej2@y}=WJZH~`_PlP-YxcZr&wKVfWsiS8vgc`g zUbg2gdtR~!>|?W?J!8-FVLxfBe?GD23wylo`}TZp5Ar@W8+&_Qx1X@bKW=-h_wr8I zsOdUtdY|qi&vnmz)1D8)M&6g^qo$wDG z5xF6{n}roV5+pVqj_1?%YU13`)KX;^ZveI3rq>;?-PAnV>6ItwM!Yv|8PKwv z#MD<#fSH|Djh;g~qoHi9B556!U{oT?aBL?C60+)`s5V2>vS^Q4vMPobHLuIi$|~>n zOI~5OI;q~)MjFtGX^Uf{;s!eN%@*zB&1*Yc znJ0K+LKlp5$$}nWeOtob1YM_avOiJ81P^JINKFi+S=!Wq>)C$4*r&1TtUyR8=Ur^*Fy(i0$BJ0UeXzKhKD)*N9sYGX*j zsuYPBM!CNU3MJE*qgMuMzcuNMmIsd-{IL*pUprgpf~{yWv$SBA%nn;VYyZqxsto55(ONe;r`qB*3)=LH>z2M^5jF@H=S_>{ip z*v-~ba85soOZk$&D8J&Gu6&ig;3Zu+($96R=Ykiffjg~_5I=lze2o_&_mWN57^g9z%7PT1n zn6TwBF|@vNByIq~P{>*|NSTH_zONKqL0C|_g{9i8w#T4_8{01-T3bLM=?77XMyRCO zs}v2|;=wc=_MV1GaNU>|-YCY3ab9V?TWNoM0X={?&Q=z{BEsnq-mo?}XmlOY8H|F) zDw5Wa1}6hjC3ZSXB%*_rX*2`d6OoEW> zN$8}c#W~T&ZMS~g_0G8UeH3`faEbG87r$p4h*iWm{581ssoSooxzXgZA?Cr56J$|9 zTggQeGq5OXcB)FT8%|}6HlMJwK>;Pqgp3qOyiM)QQMu%%c%NwYvY1A;ygHI5sNGIo z1$g~+s9p|}dN(+|L2oW0JIg3xI6*>rC08)9rik5(i%`vmQyQbqC+y7sgp_8o)DHYV z*2v~sa;e5173S6*p9RIVu-2w=&LvF|R{lU?QvL>z0#_lu(ehqKjec*GZZ&XP zF*;k84Yb^o%#5hVRaw~B za)I`=c_^6ZOfW6JHyZM2rs3VW?y8eH2m89`Lwq?K#O3?JuPUNSsyhI=4j~D|(ioA| z%n((JjgTT%r-z6(3H{uRKuueVRvZSY3Y)MSDW`QPl}jWh@2xrIU2CB z35xDDFp0kI^Z3u^ap0G$?RR+WAXsaA?t#QQZ$m(1ERrCt5+gFqx=&Gy6LqRa5C1}d zQ#j}u5;B@-ZPe)_fWsjPBLfbEXuv9@a`%QU=717(Jv`Kw(T~J@lwKgvHR_oMSiP0L zM~2eJ=C*>0lxDyb)Y^h~%W$T$dh10HX9L+pC2%k|Q2s}vA*Z;Dc?MT9wy?n}s{NhH z_^$Q>PN(l9|B-o%2Fu zhOkUoifC{Nm*Ukk*iKI=)!0^5QGCxRbBVDOZJ+LPDj3T?t3H9p{u@SaE4?;b_^8>z`7# z0{3=!qapNxmRoER6klshkSQu61ItqG2-$t@mE#Vv6tTqNvYQxTx2<8rzP7W|3_})c iLcC_SzN)x!BIe^b&;Ib>;qQO_{P53TfBE~OIR64ph=qp$ literal 0 HcmV?d00001 diff --git a/esp32_fw/data/fonts/bahnschrift80.vlw b/esp32_fw/data/fonts/bahnschrift80.vlw new file mode 100644 index 0000000000000000000000000000000000000000..84d11fff808788e0df36e34cbc75fdb3166890a1 GIT binary patch literal 28768 zcmeHMO^;+p5o`&tEcs%=!9obJ76J@52%B%Xfw=P*EKA57u#oK^=6`51t12QRv#R>d z^z7I>v#+JM6&Vqkm6fl$cP(Eommgd%m$&Tsmp%UZi#>1K^S(VV#`7y%{rR3f{`s{% zZ`$)Cdwy=u5AAuw9{>Et9@qT&{#cRsTZ{i@&kK7#u;;J#An!xN*xTzm{;oa#`N$sk zzH84f2Zr`Li@m06-lO}V{ob(G`I$Z5mt*wu2gB}tVUPFX8hZJoVb^|QkME~>e;ToC z?+uLo{%o=L@>6?!MqUH^c@3?pnq%JwGjoi*e;D?fzpw{$K%XBQ=kLkS?yH|4z5I8? zzVAN=M&AD{_B~(N<7f9gXkQtAXFNZ))qUTwCw;yipF~P<>U{z-k_2y=4{~1DRR@R} zD5v<;`vi0*if@?@a?UkRK;{~Pkbog}1ir6T^;jG$Sqy8LhMhbNd$}79bd^WTLR~Hq ztPUi)$igx`$x?L~vOE_!^*jO2^_*XuK%UNdf6n_+CXg=(#rP#khK0WTLC+U$lwflE z0hVZjo-mO@w1N^BIPaGXL*8vcd2JBcU@B}uR$CO7S)^8+tq#RjS0mdehc78A=dq@w zaBgv&1^Ni-KjS`ub3T0nPvt&%pLl1>Z?mZRWc7qmCH*J}EfC4NxyT0L$gD=)p-jci zQAv&6P~(XSL*Fqci9KX3yEAa(Fr)%O$B*C6z!rCg!ZX|@-rvBC3a~}x86O#-^GYyt z02nHd82Y0XINE^vt^*p-a#`=JZ#H25M(168u)Znqj2gJ{gfDLbPrrefg`&8G$_joa z2KjFDaKZ(o+Cc~d72U#QndR(Q>QZB)hhu9PeeCsaP`!69j1v*Y!N?^;=#ixbRFAUB zs}^kXG7MT*aBLx@!f(!?yC(eL77hwP&iB?^pfW&eYc@=Bsj$#>iQvNbY2Kp?`=S0tfd45xrfOC-EI}3 z-Y_Kt^T%5#FDu~;+rBeRruW5g2HmjVWa!&As2iB8>#3WwS~MM)9uS)qzKz^yc&0`) z2`1$xd>7MQJe<chN1NuGOAxvQz5$W(=k{X|{q*X5+n>8IQ#5W^lQ z&4p@Q2hl?fIes;$8Wk9NW+bZ6_p6-5tsRLo9L;8u(NH(_sV;R<4YFhhS!@v9O(! zV1$8_NC4`=Ob+B!G6ED2MiJC!TKha!yzD!^>dnYnor4nmeICBp8;Au0KpJZZf|5sg zh2l{!W!W}Fq zbEOI_m@KSXT`_c9l~#OU$vYvjvxuZb-X|j7g$0E&wzf(&c2c+uW$!F7633=aa;&#X z7M9LHJXw&49^on+o=T^pcpKKgmrej z(w6)!^5$`n%|Fl8qUL<=C=&JZR&*F0SUs7Q2&PJDyuhu)1s~KnLl6_sEsFH(1NuBjmZWF+?xaPT#21qi<__>PrsS}S^7CLjCil7@LF0v{OdqWdRqP5Ft z0!`khcX?MI>P8RQ(Ab$E=CHEy8=Ay3xr`&`dJZf`gG(NaBB(cAz#Eg9b|cXKpEADj z_)IHj-|=drrvsG&7n6q}0xe7n&0l*@(sDFbj-| zOMFvKwn_zo*`p<*^5k)XH3*ir*>^YF+C>(dl30V&R_1aJ*RS6mj(`{{hdUyPzuNM{u!rf0QVhyzKY!AW*PEK6M+8=a1=4#yP(`haD$ zh0sJ+fAVIiAzanMi!YE+)_p@D5MrGmo}--ZEu*0ncZS&7{s;S7&-dq;CpVvY@A z@BQLW@Z13c);k+$L?%`USiy6JfRzFO1gzk>LcmG^00LI7T3@K~9-joP$SL`|SLx4_9_Ps%>x1VWRxgip3fzCu?Ua zocm@znT9;IvE*eYp{H`#uFGsV5;ZSnCgCt~SCD*wR9Lp$@HZz$&%4Tn=T6An>6Jed zZ6dp&QP>%F{F!)86?dbr^?L`k^5@MtZ_e_@Ocy2*2z(t^3Y5%KDE-j`9jli>L;%89 z(UlGy`N1hFx^I{%-L|XQA$rJCB6O9-z;5oRN8M2%r_URldY%C1{+i4W-$zWLp8uZu zl+G`F+kmp(Q*5o5Q+1SJvVp9t(y*4AL{Dcvf!eGign_{Hb`XhGsL1leL1?QJ_5s(U z8czDK#ZvM^*cB{}Grv!ujD_K2&j%Z=_5s&c7Fb7#%MTt8s`wV?O2(k`P3FKRU|M=6-zWC(R&;Rr9FE1(h EKLX_8djJ3c literal 0 HcmV?d00001 diff --git a/esp32_fw/data/fonts/calibrib30.vlw b/esp32_fw/data/fonts/calibrib30.vlw new file mode 100644 index 0000000000000000000000000000000000000000..f2da8ab37dafca686bcf7db8a3f48b0704f443d0 GIT binary patch literal 8628 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 JaeKc^?|&%**$w~z literal 0 HcmV?d00001 diff --git a/esp32_fw/data/fonts/weathericons30.vlw b/esp32_fw/data/fonts/weathericons30.vlw new file mode 100644 index 0000000000000000000000000000000000000000..7e69ede5f82f983865b93888ef304bb08585b776 GIT binary patch literal 28057 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

    @@ -38,8 +38,8 @@ - - + + diff --git a/esp32_fw/data/www/main.js b/esp32_fw/data/www/main.js index 9d53493c..1e122901 100644 --- a/esp32_fw/data/www/main.js +++ b/esp32_fw/data/www/main.js @@ -1,7 +1,7 @@ const $ = document.querySelector.bind(document); const contentModes = ["static image", "current date", "counting days", "counting hours", "current weather", "firmware update", "memo text", "image url"]; -const models = ["unknown type", "1.54\" 152x152px", "2.9\" 296x128px", "4.2\" 400x300px"]; +const models = ["1.54\" 152x152px", "2.9\" 296x128px", "4.2\" 400x300px"]; const contentModeOptions = []; contentModeOptions[0] = ["filename","timetolive"]; contentModeOptions[1] = []; @@ -45,7 +45,7 @@ function connect() { showMessage(msg.logMsg,false); } if (msg.errMsg) { - showMessage(msg.logMsg,true); + showMessage(msg.errMsg,true); } if (msg.tags) { processTags(msg.tags); diff --git a/esp32_fw/include/contentmanager.h b/esp32_fw/include/contentmanager.h index 64fe7a25..6ccbbef9 100644 --- a/esp32_fw/include/contentmanager.h +++ b/esp32_fw/include/contentmanager.h @@ -9,7 +9,11 @@ void contentRunner(); void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo); bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin); -void drawDate(String &filename); -void drawNumber(String &filename, int32_t count, int32_t thresholdred); +void drawDate(String &filename, tagRecord *&taginfo); +void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord *&taginfo); +void drawWeather(String &filename, String location, tagRecord *&taginfo); bool getImgURL(String &filename, String URL, time_t fetched); -char *formatHttpDate(time_t t); \ No newline at end of file +char *formatHttpDate(time_t t); +String urlEncode(const char *msg); +int windSpeedToBeaufort(float windSpeed); +String windDirectionIcon(int degrees); diff --git a/esp32_fw/include/settings.h b/esp32_fw/include/settings.h index 117bb857..4f9ce696 100644 --- a/esp32_fw/include/settings.h +++ b/esp32_fw/include/settings.h @@ -24,6 +24,9 @@ // this determines how long images will be cached; #define PENDING_DATA_TIMEOUT 60 +#define SOLUM_154_033 0 +#define SOLUM_29_033 1 +#define SOLUM_42_033 2 // flasher options #define CUSTOM_MAC_HDR 0x0000 diff --git a/esp32_fw/src/contentmanager.cpp b/esp32_fw/src/contentmanager.cpp index 25bf46df..7fa8d864 100644 --- a/esp32_fw/src/contentmanager.cpp +++ b/esp32_fw/src/contentmanager.cpp @@ -75,16 +75,16 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { case Today: - drawDate(filename); + drawDate(filename, taginfo); // updateTagImage(filename, mac, (midnight - now) / 60 - 10); - updateTagImage(filename, mac, 60); + updateTagImage(filename, mac, 0); taginfo->nextupdate = midnight; break; case CountDays: if (buttonPressed) cfgobj["counter"] = 0; - drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"]); + drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"], taginfo); updateTagImage(filename, mac, (buttonPressed?0:60)); cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1; taginfo->nextupdate = midnight; @@ -93,7 +93,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { case CountHours: if (buttonPressed) cfgobj["counter"] = 0; - drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"]); + drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"], taginfo); // updateTagImage(&filename, mac, (3600 - now % 3600) / 60); // taginfo->nextupdate = now + 3600 - (now % 3600); updateTagImage(filename, mac, (buttonPressed?0:3)); @@ -104,6 +104,13 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { case Weather: // https://open-meteo.com/ + // https://geocoding-api.open-meteo.com/v1/search?name=eindhoven + // https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41¤t_weather=true + // https://github.com/erikflowers/weather-icons + + drawWeather(filename, cfgobj["location"], taginfo); + updateTagImage(filename, mac, 0); + taginfo->nextupdate = now + 900; break; case Firmware: @@ -143,8 +150,7 @@ bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin) { return true; } -void drawDate(String &filename) { - +void drawDate(String &filename, tagRecord *&taginfo) { TFT_eSPI tft = TFT_eSPI(); TFT_eSprite spr = TFT_eSprite(&tft); time_t now; @@ -155,59 +161,291 @@ void drawDate(String &filename) { String Maand[] = {"januari", "februari", "maart", "april", "mei", "juni","juli", "augustus", "september", "oktober", "november", "december"}; int weekday_number = timeinfo.tm_wday; int month_number = timeinfo.tm_mon; + long w,h; LittleFS.begin(); - long w = 296, h = 128; // mag staand of liggend - spr.createSprite(w, h); - if (spr.getPointer() == nullptr) { - wsErr("Failed to create sprite in drawDate"); + + if (taginfo->model == SOLUM_29_033) { + w = 296; + h = 128; + spr.createSprite(w, h); + if (spr.getPointer() == nullptr) { + wsErr("Failed to create sprite in drawDate"); + } + spr.setColorDepth(8); + spr.fillSprite(TFT_WHITE); + spr.setTextDatum(TC_DATUM); + spr.loadFont("fonts/calibrib62", LittleFS); + spr.setTextColor(TFT_RED, TFT_WHITE); + spr.drawString(Dag[timeinfo.tm_wday], w / 2, 10); + spr.loadFont("fonts/calibrib50", LittleFS); + spr.setTextColor(TFT_BLACK, TFT_WHITE); + spr.drawString(String(timeinfo.tm_mday) + " " + Maand[timeinfo.tm_mon], w / 2, 73); + spr.unloadFont(); + } else if (taginfo->model == SOLUM_154_033) { + w = 154; + h = 154; + spr.createSprite(w, h); + if (spr.getPointer() == nullptr) { + wsErr("Failed to create sprite in drawDate"); + } + spr.setColorDepth(8); + spr.fillSprite(TFT_WHITE); + spr.setTextDatum(TC_DATUM); + spr.loadFont("fonts/calibrib30", LittleFS); + spr.setTextColor(TFT_BLACK, TFT_WHITE); + spr.drawString(Dag[timeinfo.tm_wday], w / 2, 10); + spr.drawString(String(Maand[timeinfo.tm_mon]), w / 2, 120); + spr.unloadFont(); + spr.loadFont("fonts/numbers2-1", LittleFS); + spr.setTextColor(TFT_RED, TFT_WHITE); + spr.drawString(String(timeinfo.tm_mday), w / 2, 42); + spr.unloadFont(); } - spr.setColorDepth(8); - spr.fillSprite(TFT_WHITE); - spr.setTextDatum(TC_DATUM); - spr.loadFont("fonts/calibrib62", LittleFS); - spr.setTextColor(TFT_RED, TFT_WHITE); - spr.drawString(Dag[timeinfo.tm_wday], w / 2, 10); - spr.loadFont("fonts/calibrib50", LittleFS); - spr.setTextColor(TFT_BLACK, TFT_WHITE); - spr.drawString(String(timeinfo.tm_mday) + " " + Maand[timeinfo.tm_mon], w / 2, 73); - spr.unloadFont(); spr2grays(spr, w, h, filename); spr.deleteSprite(); } -void drawNumber(String &filename, int32_t count, int32_t thresholdred) { +void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord *&taginfo) { TFT_eSPI tft = TFT_eSPI(); TFT_eSprite spr = TFT_eSprite(&tft); LittleFS.begin(); - long w = 296, h = 128; - spr.createSprite(w, h); - if (spr.getPointer() == nullptr) { - wsErr("Failed to create sprite in drawNumber"); - } spr.setColorDepth(8); - spr.fillSprite(TFT_WHITE); - spr.setTextDatum(MC_DATUM); - if (count > thresholdred) { - spr.setTextColor(TFT_RED, TFT_WHITE); - } else { - spr.setTextColor(TFT_BLACK, TFT_WHITE); + long w, h; + + if (taginfo->model == SOLUM_29_033) { + w = 296; + h = 128; + spr.createSprite(w, h); + if (spr.getPointer() == nullptr) { + wsErr("Failed to create sprite in drawNumber"); + } + spr.fillSprite(TFT_WHITE); + spr.setTextDatum(MC_DATUM); + if (count > thresholdred) { + spr.setTextColor(TFT_RED, TFT_WHITE); + } else { + spr.setTextColor(TFT_BLACK, TFT_WHITE); + } + String font = "fonts/numbers1-2"; + if (count > 999) font = "fonts/numbers2-2"; + if (count > 9999) font = "fonts/numbers3-2"; + spr.loadFont(font, LittleFS); + spr.drawString(String(count), w / 2, h / 2 + 10); + spr.unloadFont(); + } else if (taginfo->model == SOLUM_154_033) { + w = 154; + h = 154; + spr.createSprite(w, h); + if (spr.getPointer() == nullptr) { + wsErr("Failed to create sprite in drawNumber"); + } + spr.fillSprite(TFT_WHITE); + spr.setTextDatum(MC_DATUM); + if (count > thresholdred) { + spr.setTextColor(TFT_RED, TFT_WHITE); + } else { + spr.setTextColor(TFT_BLACK, TFT_WHITE); + } + String font = "fonts/numbers1-1"; + if (count > 99) font = "fonts/numbers2-1"; + if (count > 999) font = "fonts/numbers3-1"; + spr.loadFont(font, LittleFS); + spr.drawString(String(count), w / 2, h / 2 + 10); + spr.unloadFont(); } - String font = "fonts/numbers1-2"; - if (count > 999) font = "fonts/numbers2-2"; - if (count > 9999) font = "fonts/numbers3-2"; - spr.loadFont(font, LittleFS); - spr.drawString(String(count), w/2, h/2+10); - spr.unloadFont(); spr2grays(spr, w, h, filename); spr.deleteSprite(); } +void drawWeather(String &filename, String location, tagRecord *&taginfo) { + + TFT_eSPI tft = TFT_eSPI(); + TFT_eSprite spr = TFT_eSprite(&tft); + + wsLog("get weather"); + HTTPClient http; + http.begin("https://geocoding-api.open-meteo.com/v1/search?name=" + urlEncode(location.c_str()) + "&count=1"); + http.setTimeout(5000); // timeout in ms + int httpCode = http.GET(); + if (httpCode == 200) { + + StaticJsonDocument<1000> doc; + DeserializationError error = deserializeJson(doc, http.getStream()); + http.end(); + + http.begin("https://api.open-meteo.com/v1/forecast?latitude=" + doc["results"][0]["latitude"].as() + "&longitude=" + doc["results"][0]["longitude"].as() + "¤t_weather=true&windspeed_unit=ms&timezone=" + doc["results"][0]["timezone"].as()); + http.setTimeout(5000); // timeout in ms + int httpCode = http.GET(); + + if (httpCode == 200) { + + StaticJsonDocument<200> filter; + filter["current_weather"]["temperature"] = true; + filter["current_weather"]["windspeed"] = true; + filter["current_weather"]["winddirection"] = true; + filter["current_weather"]["weathercode"] = true; + + doc.clear(); + DeserializationError error = deserializeJson(doc, http.getString(), DeserializationOption::Filter(filter)); + if (error) { + Serial.println(F("deserializeJson() failed: ")); + Serial.println(error.c_str()); + } + + auto temperature = doc["current_weather"]["temperature"].as(); + auto windspeed = doc["current_weather"]["windspeed"].as(); + auto winddirection = doc["current_weather"]["winddirection"].as(); + + uint8_t weathercode = doc["current_weather"]["weathercode"].as(); + if (weathercode > 40) weathercode -= 40; + int wind = windSpeedToBeaufort(windspeed); + + String weatherIcons[] = {"\uf00d", "\uf00c", "\uf002", "\uf013", "\uf013", "\uf014", "-", "-", "\uf014", "-", "-", + "\uf01a", "-", "\uf01a", "-", "\uf01a", "\uf017", "\uf017", "-", "-", "-", + "\uf019", "-", "\uf019", "-", "\uf019", "\uf015", "\uf015", "-", "-", "-", + "\uf01b", "-", "\uf01b", "-", "\uf01b", "-", "\uf076", "-", "-", "\uf01a", + "\uf01a", "\uf01a", "-", "-", "\uf064", "\uf064", "-", "-", "-", "-", + "-", "-", "-", "-", "\uf01e", "\uf01d", "-", "-", "\uf01e"}; + if (1==0) { //nacht + weatherIcons[0] = "\0uf02e"; + weatherIcons[1] = "\0uf083"; + weatherIcons[2] = "\0uf086"; + } + + String windIcons[] = {"\uf0b7", "\uf0b8", "\uf0b9", "\uf0ba", "\uf0bb", "\uf0bc", "\uf0bd", "\uf0be", "\uf0bf", "\uf0c0", "\uf0c1", "\uf0c2", "\uf0c3"}; + + doc.clear(); + + LittleFS.begin(); + spr.setColorDepth(8); + tft.setTextWrap(false,false); + + long w, h; + + if (taginfo->model == SOLUM_29_033) { + w = 296; + h = 128; + spr.createSprite(w, h); + if (spr.getPointer() == nullptr) { + wsErr("Failed to create sprite in drawWeather"); + } + spr.fillSprite(TFT_WHITE); + spr.setTextDatum(TL_DATUM); + + spr.loadFont("fonts/bahnschrift30", LittleFS); + spr.setTextColor(TFT_BLACK, TFT_WHITE); + spr.drawString(location, 10, 10); + spr.unloadFont(); + + spr.loadFont("fonts/bahnschrift70", LittleFS); + if (temperature < 0) { + spr.setTextColor(TFT_RED, TFT_WHITE); + } else { + spr.setTextColor(TFT_BLACK, TFT_WHITE); + } + char tmpOutput[5]; + dtostrf(temperature, 2, 1, tmpOutput); + spr.drawString(String(tmpOutput), 5, 65); + spr.unloadFont(); + + spr.loadFont("fonts/weathericons78", LittleFS); + if (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 99) { + spr.setTextColor(TFT_RED, TFT_WHITE); + } else { + spr.setTextColor(TFT_BLACK, TFT_WHITE); + } + spr.setTextColor(TFT_BLACK, TFT_WHITE); + spr.setCursor(185, 20); + spr.printToSprite(weatherIcons[weathercode]); + spr.unloadFont(); + + spr.loadFont("fonts/weathericons30", LittleFS); + if (wind > 4) { + spr.setTextColor(TFT_RED, TFT_WHITE); + } else { + spr.setTextColor(TFT_BLACK, TFT_WHITE); + } + spr.setCursor(255, 0); + spr.printToSprite(windIcons[wind]); + + spr.setTextColor(TFT_BLACK, TFT_WHITE); + spr.setCursor(230, -5); + spr.printToSprite(windDirectionIcon(winddirection)); + if (weathercode > 10) { + spr.setTextColor(TFT_RED, TFT_WHITE); + spr.setCursor(190, 0); + spr.printToSprite("\uf084"); + } + } else if (taginfo->model == SOLUM_154_033) { + w = 154; + h = 154; + + spr.createSprite(w, h); + if (spr.getPointer() == nullptr) { + wsErr("Failed to create sprite in drawWeather"); + } + spr.fillSprite(TFT_WHITE); + spr.setTextDatum(TL_DATUM); + + spr.setTextFont(2); + spr.setTextColor(TFT_BLACK, TFT_WHITE); + spr.drawString(location, 10, 130); + + spr.loadFont("fonts/bahnschrift30", LittleFS); + if (temperature < 0) { + spr.setTextColor(TFT_RED, TFT_WHITE); + } else { + spr.setTextColor(TFT_BLACK, TFT_WHITE); + } + char tmpOutput[5]; + dtostrf(temperature, 2, 1, tmpOutput); + spr.drawString(String(tmpOutput), 10, 10); + spr.unloadFont(); + + spr.loadFont("fonts/weathericons78", LittleFS); + if (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 99) { + spr.setTextColor(TFT_RED, TFT_WHITE); + } else { + spr.setTextColor(TFT_BLACK, TFT_WHITE); + } + spr.setTextColor(TFT_BLACK, TFT_WHITE); + spr.setCursor(35, 25); + spr.printToSprite(weatherIcons[weathercode]); + spr.unloadFont(); + + spr.loadFont("fonts/weathericons30", LittleFS); + if (wind > 4) { + spr.setTextColor(TFT_RED, TFT_WHITE); + } else { + spr.setTextColor(TFT_BLACK, TFT_WHITE); + } + spr.setCursor(115, -5); + spr.printToSprite(windIcons[wind]); + + spr.setTextColor(TFT_BLACK, TFT_WHITE); + spr.setCursor(90, -5); + spr.printToSprite(windDirectionIcon(winddirection)); + if (weathercode > 10) { + spr.setTextColor(TFT_RED, TFT_WHITE); + spr.setCursor(115, 110); + spr.printToSprite("\uf084"); + } + } + + spr.unloadFont(); + spr2grays(spr, w, h, filename); + spr.deleteSprite(); + } + } + http.end(); +} + bool getImgURL(String &filename, String URL, time_t fetched) { // https://images.klari.net/kat-bw29.jpg @@ -244,3 +482,41 @@ char *formatHttpDate(time_t t) { strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", timeinfo); return buf; } + +String urlEncode(const char *msg) { + const char *hex = "0123456789ABCDEF"; + String encodedMsg = ""; + + while (*msg != '\0') { + if ( + ('a' <= *msg && *msg <= 'z') || ('A' <= *msg && *msg <= 'Z') || ('0' <= *msg && *msg <= '9') || *msg == '-' || *msg == '_' || *msg == '.' || *msg == '~') { + encodedMsg += *msg; + } else { + encodedMsg += '%'; + encodedMsg += hex[(unsigned char)*msg >> 4]; + encodedMsg += hex[*msg & 0xf]; + } + msg++; + } + return encodedMsg; +} + +int windSpeedToBeaufort(float windSpeed) { + int beaufort = 0; + float speeds[] = {0.3, 1.5, 3.3, 5.5, 8, 10.8, 13.9, 17.2, 20.8, 24.5, 28.5, 32.7}; + for (int i = 0; i < 12; i++) { + if (windSpeed >= speeds[i]) { + beaufort = i + 1; + } + } + return beaufort; +} + +String windDirectionIcon(int degrees) { + String directions[] = {"\uf044", "\uf088", "\uf04d", "\uf057", "\uf058", "\uf087", "\uf048", "\uf043"}; + int index = (degrees + 22) / 45; + if (index >= 8) { + index = 0; + } + return directions[index]; +} diff --git a/esp32_fw/src/main.cpp b/esp32_fw/src/main.cpp index f9552de1..0f8f2aa2 100644 --- a/esp32_fw/src/main.cpp +++ b/esp32_fw/src/main.cpp @@ -31,7 +31,7 @@ void setup() { Serial.begin(115200); Serial.print(">\n"); - configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", "europe.pool.ntp.org", "time.nist.gov"); + configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", "0.nl.pool.ntp.org", "europe.pool.ntp.org", "time.nist.gov"); // https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv init_web(); diff --git a/esp32_fw/src/newproto.cpp b/esp32_fw/src/newproto.cpp index f79dbabb..4d47f36f 100644 --- a/esp32_fw/src/newproto.cpp +++ b/esp32_fw/src/newproto.cpp @@ -268,6 +268,7 @@ void processDataReq(struct espAvailDataReq* eadr) { time_t now; time(&now); taginfo->lastseen = now; + //taginfo->model = eadr->adr.hwType; taginfo->expectedNextCheckin = now + 300; taginfo->button = (eadr->adr.wakeupReason==WAKEUP_REASON_GPIO); diff --git a/esp32_fw/src/web.cpp b/esp32_fw/src/web.cpp index 1f4514d4..cb60af77 100644 --- a/esp32_fw/src/web.cpp +++ b/esp32_fw/src/web.cpp @@ -144,7 +144,7 @@ void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType } void wsLog(String text) { - DynamicJsonDocument doc(100); + StaticJsonDocument<500> doc; doc["logMsg"] = text; xSemaphoreTake(wsMutex, portMAX_DELAY); ws.textAll(doc.as()); @@ -152,7 +152,7 @@ void wsLog(String text) { } void wsErr(String text) { - DynamicJsonDocument doc(100); + StaticJsonDocument<500> doc; doc["errMsg"] = text; xSemaphoreTake(wsMutex, portMAX_DELAY); ws.textAll(doc.as());