From c68b582be7841a113ac7bf45c604a7c9b6f0f77f Mon Sep 17 00:00:00 2001 From: Nic Limper Date: Thu, 8 Jun 2023 15:41:15 +0200 Subject: [PATCH] overhaul of contentmanager, 4.2" layouts, bugfixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - added Swedish å Å to Bahnschrift20 and 30 - option to turn dither on/off in 'static image' - major overhaul of contentmanager.cpp, content is now generated from json template. - fallback to 1bpp for 4.2" when no psram - added 4.2" content layouts - fixed small apconfig bug - pause content generation for 60 seconds after crash, to prevent uncontrollable boot loop --- ESP32_AP-Flasher/data/content_template.json | 129 +++ ESP32_AP-Flasher/data/fonts/bahnschrift20.vlw | Bin 14672 -> 15128 bytes ESP32_AP-Flasher/data/fonts/bahnschrift30.vlw | Bin 28570 -> 29483 bytes ESP32_AP-Flasher/data/fonts/calibrib100.vlw | Bin 28072 -> 29003 bytes ESP32_AP-Flasher/data/fonts/calibrib120.vlw | Bin 41955 -> 43376 bytes ESP32_AP-Flasher/data/fonts/calibrib150.vlw | Bin 64410 -> 66571 bytes ESP32_AP-Flasher/data/fonts/calibrib30.vlw | Bin 11797 -> 26685 bytes ESP32_AP-Flasher/data/fonts/calibrib60.vlw | Bin 23834 -> 44201 bytes ESP32_AP-Flasher/data/fonts/calibrib80.vlw | Bin 18398 -> 19072 bytes ESP32_AP-Flasher/data/www/content_cards.json | 10 + ESP32_AP-Flasher/data/www/index.html | 2 +- ESP32_AP-Flasher/data/www/main.js | 9 + ESP32_AP-Flasher/include/contentmanager.h | 7 +- ESP32_AP-Flasher/include/makeimage.h | 1 + ESP32_AP-Flasher/src/contentmanager.cpp | 768 ++++++------------ ESP32_AP-Flasher/src/main.cpp | 14 +- ESP32_AP-Flasher/src/makeimage.cpp | 6 +- ESP32_AP-Flasher/src/tag_db.cpp | 5 +- 18 files changed, 445 insertions(+), 506 deletions(-) create mode 100644 ESP32_AP-Flasher/data/content_template.json diff --git a/ESP32_AP-Flasher/data/content_template.json b/ESP32_AP-Flasher/data/content_template.json new file mode 100644 index 00000000..c0aa9d5f --- /dev/null +++ b/ESP32_AP-Flasher/data/content_template.json @@ -0,0 +1,129 @@ +{ + "1": { + "0": { + "weekday": [ 76, 10, "fonts/calibrib30" ], + "month": [ 76, 120, "fonts/calibrib30" ], + "day": [ 76, 42, "fonts/calibrib100" ] + }, + "1": { + "weekday": [ 148, 10, "fonts/calibrib60" ], + "date": [ 148, 73, "fonts/calibrib50" ] + }, + "2": { + "weekday": [ 200, 25, "fonts/calibrib60" ], + "month": [ 200, 225, "fonts/calibrib60" ], + "day": [ 200, 95, "fonts/calibrib150" ] + } + }, + "2": { + "0": { + "fonts": [ "fonts/calibrib120", "fonts/calibrib80", "fonts/calibrib50", "fonts/calibrib50" ], + "xy": [ 76, 83 ] + }, + "1": { + "fonts": [ "fonts/calibrib150", "fonts/calibrib150", "fonts/calibrib120", "fonts/calibrib100" ], + "xy": [ 148, 74 ] + }, + "2": { + "fonts": [ "fonts/calibrib150", "fonts/calibrib150", "fonts/calibrib150", "fonts/calibrib120" ], + "xy": [ 200, 148 ] + } + }, + "4": { + "0": { + "location": [ 10, 130, 2 ], + "wind": [ 140, 10, "fonts/bahnschrift30" ], + "temp": [ 10, 10, "fonts/bahnschrift30" ], + "icon": [ 33, 33, "fonts/weathericons78" ], + "dir": [ 100, -2, "fonts/weathericons30" ], + "umbrella": [ 115, 110 ] + }, + "1": { + "location": [ 5, 5, "fonts/bahnschrift30" ], + "wind": [ 280, 5, "fonts/bahnschrift30" ], + "temp": [ 5, 65, "fonts/bahnschrift70" ], + "icon": [ 185, 32, "fonts/weathericons70" ], + "dir": [ 240, -3, "fonts/weathericons30" ], + "umbrella": [ 190, 0 ] + }, + "2": { + "location": [ 20, 20, "fonts/calibrib30" ], + "wind": [ 290, 83, "fonts/calibrib60" ], + "temp": [ 20, 170, "fonts/calibrib150" ], + "icon": [ 100, 50, "fonts/weathericons78" ], + "dir": [ 220, 50, "fonts/weathericons78" ], + "umbrella": [ 330, 10 ] + } + }, + "8": { + "1": { + "location": [ 5, 0, 2 ], + "column": [ 5, 59 ], + "day": [ 30, 18, "fonts/twcondensed20", 41, 108 ], + "icon": [ 12, 58, "fonts/weathericons30" ], + "wind": [ 17, 25 ], + "line": [ 20, 128 ] + }, + "2": { + "location": [ 10, 10, "fonts/calibrib30" ], + "column": [ 6, 66 ], + "day": [ 33, 60, "fonts/bahnschrift20", 104, 230 ], + "rain": [ 34, 260 ], + "icon": [ 15, 145, "fonts/weathericons30" ], + "wind": [ 17, 90 ], + "line": [ 50, 300 ] + } + }, + "9": { + "1": { + "title": [ 5, 3, "fonts/bahnschrift20" ], + "items": 8, + "line": [ 5, 34, 13 ], + "font": "glasstown_nbp_tf" + }, + "2": { + "title": [ 10, 10, "fonts/calibrib30" ], + "items": 12, + "line": [ 10, 60, 20 ], + "font": "7x14_tf" + } + }, + "10": { + "0": { + "title": [ 10, 3, 2 ], + "pos": [ 76, 20 ] + }, + "1": { + "title": [ 10, 5, "fonts/bahnschrift20" ], + "pos": [ 149, 25 ] + }, + "2": { + "title": [ 10, 10, "fonts/bahnschrift20" ], + "pos": [ 200, 30 ] + } + }, + "11": { + "1": { + "title": [ 5, 2, "fonts/bahnschrift20" ], + "date": [ 290, 2 ], + "items": 7, + "red": [ 0, 21, 296, 14 ], + "line": [ 5, 32, 15, "t0_14b_tf", 50 ] + }, + "2": { + "title": [ 10, 10, "fonts/bahnschrift30" ], + "date": [ 390, 10 ], + "items": 12, + "red": [ 0, 48, 400, 17 ], + "line": [ 10, 61, 18, "7x14_tf", 60 ] + } + }, + "16": { + "1": { + "location": [ 5, 5, "fonts/bahnschrift30" ], + "title": [ 247, 11, "glasstown_nbp_tf" ], + "cols": [ 1, 125, 12 ], + "bars": [ 5, 111, 10 ] + } + } +} \ No newline at end of file diff --git a/ESP32_AP-Flasher/data/fonts/bahnschrift20.vlw b/ESP32_AP-Flasher/data/fonts/bahnschrift20.vlw index 44aefdc3f44b9f46d3e788388d93b82df2843727..d84a77162110dc1cbae4346d6908a0fc6becf788 100644 GIT binary patch delta 371 zcmX|*F-XHu6h&`C28|jBA&58#ju~7^addMK2RA1XDX4UiQbdpS!F}BG-u?U+Hy*BZ0B6s7)374xY^jx8cI(n;S`$f@ z)S}dq^5Lg3ac_aGS25m{gRF))9cBjN@i{|D|^MV&YEU6NW@=UWClG6sxTR`kdd*)Mo6yZ+00O*P;!_v9`R z@JNMm&Mxd!EN9@PVxh($^T3eDKwIY8tcG!=rn%Tpgs-C7%(D`8h`~MhT{Zdk%nwh! RJV-=fwn`NzeBZ8@{s0O1aWen_ delta 32 ocmbPHcAI;tR>-uO$x4h70J~BO-v9sr diff --git a/ESP32_AP-Flasher/data/fonts/bahnschrift30.vlw b/ESP32_AP-Flasher/data/fonts/bahnschrift30.vlw index 091a0d7c9b16d000f32a19c5e4057bc11b0aea6b..ba0c7b6bc110b40251cdc56065309205edd26463 100644 GIT binary patch delta 695 zcmY+CF-SsD6o!wc5s!i_LL%E5B20{iridUK8YF^RA{q)?q9NQITq8t8r)r332yCg0 z2*g1m+8g3+5v37=Lqy2q!QVOe`SSY1;hpo{d*3_%dH49$b6NBFh-l~9mox&8oP{_B zTM^19jAWq7)i6gNN5AC755vEa;*8>E^ZR5-GFR&mJ-DSj?~wIIErc~z#{y9aOB#}0 zVG9urt*CdEH9{172eQ44)c2hwZyng^*hNcx^u7}n7}-Ic85SDAzK*tltva4{RDsod z1ik%H;D%E3z+rn|Qd;nSL59_m39E;QkFr-s)4&yHbHJI4vcS6JpJ;YFjNMo&%>kPZ zVmAeq%>u1)6jE%*l|3S>Y)4Z<5ZlqDY*m0|SrYLqqeN`ySMGpP2FMVxn{$`Y4Dd;0 z0)9zN~3HHn^;xFh5WWt?5QG$ gQcv*3nFd8{>AN8pk+t|_H%k-wsrY2^V0WPF4{9Xg4FCWD delta 34 qcmZ4ejB(a|MivGJhSZHL)m)Qja}{h>=l&J5*)%JWb@T0l044y~sSKL{ diff --git a/ESP32_AP-Flasher/data/fonts/calibrib100.vlw b/ESP32_AP-Flasher/data/fonts/calibrib100.vlw index e4c2ce70e6f60a417872e5e00f01f328d5565030..f6f008e97a00a6b2a0d5a544de1bffe3aaae5815 100644 GIT binary patch delta 840 zcmZ2+oALA`MiT}G1|A^h2I3ST1_2i!76M{DAm)c+X(-JGq!~6!NHf-N05T+jSPF=h zfY=#`S%4S_gv-~x`|<1B!&S*lK%V`(|1fY(l7ZtTj02>1FvKG9{{NRw;Z3sJeVccaMGFGt}9rJ-;o`4Eo${~zczw@+~S3!*?zh*xj=@Z-<7 zyQ?BWUIK#6CX5j|lM@)(os2uSeE9L}%Y~(Bf)FVv>30Ec#Q$G2K>-K>!kZA{Al8@U z$px|ETtHR_Sb_LtMII5hJP>>T}x?_T5*{C1ccDwOh!|%!}meS(MgV3uF zm#gP z73EkMTQ6kHT!`Yg-HZ5Xe{4(oP^O9T<8Zom*WvS301BLcKm!HII1i{EfW#raT3zs5wzm_zHo+yuNzDs-4 u<}RO_=Q`IWfGnS%LPp4*>NHNw*4dVhzUX-Gjk0)iOE=9*F)QQe=K4Px(}$@5 delta 48 wcmexxiRtliCJ6=x1}-4x2I2}J27w?T7Tahd%{aM2SbXyhrrJf5LsrTF0P}7O3;+NC diff --git a/ESP32_AP-Flasher/data/fonts/calibrib150.vlw b/ESP32_AP-Flasher/data/fonts/calibrib150.vlw index 1539e7ca480aa6f0b404dab943f13d2d2ca4607a..050cbd86967640fe3377c75401c477911ca5bf69 100644 GIT binary patch delta 1096 zcmZ9KUr19?9LLYS-sRM7ZtY$-H^YR1Q=?4xuxQnc5qsD}g3ulee9#gy^1%lmA{P<{ zKCJkHgM65guxOCU9`@ja_OxJx%A$XZE@Olb7JHbWH0X48H^-d^zW4m@=lsq&zu)=& zUK}-|Sv^I)yx59(P8mbsG44*qZ0Vxd-l2k*iG@fcm5i52%k#`1^X?7mN zpCv9){^2&T+b?lxV*>Pm@0ls_s4}wW?KLJhRh-^C$LsYt zFy!tu_>n19lgKA#kh^XbJ3{;FwAd+0Kl3&7)(BoMiG0e9A0K*J7i+IrzEi-E*$l6s z<+E1&PIof1iI*Ml+;Z|+9iHoLj2IxdHbRqbXLd6}Ne!O-vNBtT&7@{-rv6sC7mvo| z6O4`$qTQ>5Uv4UaxNw|DWp#s)V4dHqTD<=H0(X$jvLnQ45#P5A#!;QR)+Hyr_uKh} z0GY`bI$n-K*J8(xq?o-Q?YMo!%w>#yv*YS(U0>>< zI-FpoP}Q$dePb+$&|?cO-!<|f2mYM{t&r65udJESiSU|3qOy#vGS&oGvd8`wt21Ec*Kk0 zw`UA=+gJ^Q5MKco1-l_7)(9bPw#kOI!B*ayV5@Cnfz|cI@F91}%oi5WH6We#EvsLM c()6U#)m~0x(0XShiHt1E>3Enm`h!y6f4VavTmS$7 delta 49 xcmeC~V43xuS%QIqfeVPafp{7agFqS(OK&uhW}JNTiNxkwX5(Me-!n7H008PW3r_$5 diff --git a/ESP32_AP-Flasher/data/fonts/calibrib30.vlw b/ESP32_AP-Flasher/data/fonts/calibrib30.vlw index 87c41c69169c8c803b82ae3e2f6b9b5cef69e349..cd0a0fe7997e8f7e514c5ebb80a541c55e504cb6 100644 GIT binary patch literal 26685 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 diff --git a/ESP32_AP-Flasher/data/fonts/calibrib60.vlw b/ESP32_AP-Flasher/data/fonts/calibrib60.vlw index 04f6e3b139595904673544b4a9632a72fef3d34e..3f6d44af1fbd072c8269bbee1fe12674d2e4cb71 100644 GIT binary patch literal 44201 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 diff --git a/ESP32_AP-Flasher/data/fonts/calibrib80.vlw b/ESP32_AP-Flasher/data/fonts/calibrib80.vlw index eb9f8dfebfba02daa69c93818e2442c53397564b..c78c7915a835dd4c98e2f8d1da5239d714350426 100644 GIT binary patch delta 711 zcmccD&)6`P(S(74fd`1Wfj9t&LBI@%`G8mth(U6^Kr9MnvjAy^jS|v~^&6m~VnA9N zh(WrTffxwHTTg!Z_2cPsdmvl-<$oy1VqlmAWB>oc!f+SP`R~Q_2f>NvLa_fQqi~oI zGA93{8J;7!ZVWTwoOetN@^D*|fo_oOI`i$%kB7@GH|sG@ahtq>o3&mvZ_V=`zrGxv zVheIG1PJ&1f}3%{11f0x7%m24bb*B}zn}>I|33{VEDE<1E*Z+eun^9HGafUk)I&Kp zJeZg~u0T1FjS$9tVUQ()_aVIHdm%K)BS4S{RReVaD4u|T2O|0nLj8vbGB7~6f1utH zgb0Jw)&udU`#=H+Pz~O{93uJ<6yprS4n!0 zCiMURJ5UOli{QJ0nLixCF6u#u16j|l!Ga77@NoJ6|IZ|FkU%6HryT$G=hw@1MbeXZ HyDI|#UW!*;w1wBm{|uk diff --git a/ESP32_AP-Flasher/data/www/content_cards.json b/ESP32_AP-Flasher/data/www/content_cards.json index bc1c5641..f4a7fb6d 100644 --- a/ESP32_AP-Flasher/data/www/content_cards.json +++ b/ESP32_AP-Flasher/data/www/content_cards.json @@ -21,6 +21,16 @@ "name": "TimeToLive", "desc": "Amount (minutes) that this image will stay valid. The tag might not respond meanwhile", "type": "int" + }, + { + "key": "dither", + "name": "Dithering", + "desc": "Turn halftone dithering on or off. Turn it on when displaying photos. For texts, you better leave if off", + "type": "select", + "options": { + "0": "off", + "1": "on" + } } ] }, diff --git a/ESP32_AP-Flasher/data/www/index.html b/ESP32_AP-Flasher/data/www/index.html index 81a7e624..a18eb81f 100644 --- a/ESP32_AP-Flasher/data/www/index.html +++ b/ESP32_AP-Flasher/data/www/index.html @@ -105,7 +105,7 @@ Latency will be around 40 seconds."> alias tags ch - fw ver + AP ver

diff --git a/ESP32_AP-Flasher/data/www/main.js b/ESP32_AP-Flasher/data/www/main.js index 8688af1f..b775c95d 100644 --- a/ESP32_AP-Flasher/data/www/main.js +++ b/ESP32_AP-Flasher/data/www/main.js @@ -487,6 +487,15 @@ function contentselected() { input.type = "text"; input.disabled = true; break; + case 'select': + input = document.createElement("select"); + for (const key in element.options) { + const optionElement = document.createElement("option"); + optionElement.value = key; + optionElement.text = element.options[key]; + input.appendChild(optionElement); + } + break; } input.id = 'opt' + element.key; input.title = element.desc; diff --git a/ESP32_AP-Flasher/include/contentmanager.h b/ESP32_AP-Flasher/include/contentmanager.h index c11165a4..af98be72 100644 --- a/ESP32_AP-Flasher/include/contentmanager.h +++ b/ESP32_AP-Flasher/include/contentmanager.h @@ -2,6 +2,7 @@ #include #include +#include "U8g2_for_TFT_eSPI.h" #include "makeimage.h" #include "tag_db.h" @@ -18,7 +19,7 @@ void contentRunner(); void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo); bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin, tagRecord *&taginfo, imgParam &imageParams); void drawString(TFT_eSprite &spr, String content, uint16_t posx, uint16_t posy, String font, byte align = 0, uint16_t color = TFT_BLACK); -void initSprite(TFT_eSprite &spr, int w, int h); +void initSprite(TFT_eSprite &spr, int w, int h, imgParam &imageParams); void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams); void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord *&taginfo, imgParam &imageParams); void drawWeather(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgParam &imageParams); @@ -35,4 +36,6 @@ int windSpeedToBeaufort(float windSpeed); String windDirectionIcon(int degrees); void getLocation(JsonObject &cfgobj); void prepareNFCReq(uint8_t* dst, const char* url); -void prepareLUTreq(uint8_t *dst, String input); \ No newline at end of file +void prepareLUTreq(uint8_t *dst, String input); +void getTemplate(JsonDocument &json, const char *filePath, uint8_t id, uint8_t hwtype); +void setU8G2Font(const String &title, U8g2_for_TFT_eSPI &u8f); diff --git a/ESP32_AP-Flasher/include/makeimage.h b/ESP32_AP-Flasher/include/makeimage.h index 165a2f2e..4f0cf0ce 100644 --- a/ESP32_AP-Flasher/include/makeimage.h +++ b/ESP32_AP-Flasher/include/makeimage.h @@ -8,6 +8,7 @@ struct imgParam { uint8_t dataType; bool dither; bool grayLut = false; + uint8_t bpp = 8; char segments[12]; uint16_t symbols; diff --git a/ESP32_AP-Flasher/src/contentmanager.cpp b/ESP32_AP-Flasher/src/contentmanager.cpp index 9589340b..9f4d9067 100644 --- a/ESP32_AP-Flasher/src/contentmanager.cpp +++ b/ESP32_AP-Flasher/src/contentmanager.cpp @@ -13,8 +13,10 @@ #ifdef CONTENT_RSS #include #endif -#include #include +#include + +#include #if defined CONTENT_RSS || defined CONTENT_CAL #include "U8g2_for_TFT_eSPI.h" @@ -25,15 +27,18 @@ #ifdef CONTENT_QR #include "qrcode.h" #endif -#include "tag_db.h" -#include "settings.h" -#include "web.h" #include "language.h" +#include "settings.h" +#include "tag_db.h" +#include "web.h" #define PAL_BLACK TFT_BLACK #define PAL_WHITE TFT_WHITE #define PAL_RED TFT_RED +#define TEMPLATE "/content_template.json" +// https://csvjson.com/json_beautifier + enum contentModes { Image, Today, @@ -51,14 +56,26 @@ enum contentModes { SegStatic, }; -void contentRunner() { +struct HwType { + uint8_t basetype; + uint16_t width; + uint16_t height; +}; +std::map hwdata = { + {0, {0, 152, 152}}, + {1, {1, 296, 128}}, + {2, {2, 400, 300}}, + {17, {1, 296, 128}}, +}; + +void contentRunner() { if (config.runStatus == RUNSTATUS_STOP) return; time_t now; time(&now); - //xSemaphoreTake(tagDBOwner, portMAX_DELAY); + // xSemaphoreTake(tagDBOwner, portMAX_DELAY); for (int16_t c = 0; c < tagDB.size(); c++) { tagRecord *taginfo = nullptr; taginfo = tagDB.at(c); @@ -80,7 +97,7 @@ void contentRunner() { vTaskDelay(1 / portTICK_PERIOD_MS); // add a small delay to allow other threads to run } - //xSemaphoreGive(tagDBOwner); + // xSemaphoreGive(tagDBOwner); } void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { @@ -125,7 +142,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { if (imageParams.hasRed) imageParams.dataType = DATATYPE_IMG_RAW_2BPP; if (prepareDataAvail(&filename, imageParams.dataType, mac, cfgobj["timetolive"].as())) { cfgobj["#fetched"] = true; - if (cfgobj["delete"].as()=="1") LittleFS.remove("/"+cfgobj["filename"].as()); + if (cfgobj["delete"].as() == "1") LittleFS.remove("/" + cfgobj["filename"].as()); } else { wsErr("Error accessing " + filename); } @@ -194,33 +211,26 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { } break; - case Memo: - - drawIdentify(filename, taginfo, imageParams); - taginfo->nextupdate = now + 12 * 3600; - updateTagImage(filename, mac, 0, taginfo, imageParams); - break; - case ImageUrl: - { + { int httpcode = getImgURL(filename, cfgobj["url"], (time_t)cfgobj["#fetched"], imageParams, String(hexmac)); if (httpcode == 200) { - taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 3 ? 3 : cfgobj["interval"].as()); + taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 3 ? 15 : cfgobj["interval"].as()); updateTagImage(filename, mac, cfgobj["interval"].as(), taginfo, imageParams); cfgobj["#fetched"] = now; } else if (httpcode == 304) { - taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 3 ? 3 : cfgobj["interval"].as()); + taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 3 ? 15 : cfgobj["interval"].as()); } else { taginfo->nextupdate = now + 300; } - break; - } + break; + } case RSSFeed: if (getRssFeed(filename, cfgobj["url"], cfgobj["title"], taginfo, imageParams)) { - taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 5 ? 5 : cfgobj["interval"].as()); + taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 3 ? 60 : cfgobj["interval"].as()); updateTagImage(filename, mac, cfgobj["interval"].as(), taginfo, imageParams); } else { taginfo->nextupdate = now + 300; @@ -237,7 +247,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { case Calendar: if (getCalFeed(filename, cfgobj["apps_script_url"], cfgobj["title"], taginfo, imageParams)) { - taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 5 ? 5 : cfgobj["interval"].as()); + taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 3 ? 15 : cfgobj["interval"].as()); updateTagImage(filename, mac, cfgobj["interval"].as(), taginfo, imageParams); } else { taginfo->nextupdate = now + 300; @@ -275,7 +285,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { taginfo->nextupdate = now + (cfgobj["ttl"].as() < 5 ? 5 : cfgobj["ttl"].as()) * 60; updateTagImage(filename, mac, (cfgobj["ttl"].as() < 5 ? 5 : cfgobj["ttl"].as()), taginfo, imageParams); break; - } + } taginfo->modeConfigJson = doc.as(); } @@ -293,15 +303,29 @@ bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin, tagRec void drawString(TFT_eSprite &spr, String content, uint16_t posx, uint16_t posy, String font, byte align, uint16_t color) { // drawString(spr,"test",100,10,"bahnschrift30",TC_DATUM,PAL_RED); spr.setTextDatum(align); - if (font != "") spr.loadFont(font, LittleFS); - spr.setTextColor(color, PAL_WHITE); - spr.drawString(content, posx, posy); - if (font != "") spr.unloadFont(); + + if (font == "2") { + spr.setTextFont(2); + spr.setTextColor(PAL_BLACK, PAL_WHITE); + spr.drawString(content, posx, posy); + } else { + if (font != "") spr.loadFont(font, LittleFS); + spr.setTextColor(color, PAL_WHITE); + spr.drawString(content, posx, posy); + if (font != "") spr.unloadFont(); + } } -void initSprite(TFT_eSprite &spr, int w, int h) { +void initSprite(TFT_eSprite &spr, int w, int h, imgParam &imageParams) { spr.setColorDepth(8); spr.createSprite(w, h); + if (spr.getPointer() == nullptr) { + wsErr("low on memory. Fallback to 1bpp"); + spr.setColorDepth(1); + spr.setBitmapColor(TFT_WHITE, TFT_BLACK); + imageParams.bpp = 1; + spr.createSprite(w, h); + } if (spr.getPointer() == nullptr) { wsErr("Failed to create sprite"); } @@ -326,23 +350,19 @@ void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams) { TFT_eSPI tft = TFT_eSPI(); TFT_eSprite spr = TFT_eSprite(&tft); - LittleFS.begin(); - if (taginfo->hwType == SOLUM_29_SSD1619 || taginfo->hwType == SOLUM_29_UC8151) { - initSprite(spr, 296, 128); - drawString(spr, languageDays[getCurrentLanguage()][timeinfo.tm_wday], 296 / 2, 10, "fonts/calibrib60", TC_DATUM, PAL_RED); - drawString(spr, String(timeinfo.tm_mday) + " " + languageMonth[getCurrentLanguage()][timeinfo.tm_mon], 296 / 2, 73, "fonts/calibrib50", TC_DATUM); + DynamicJsonDocument loc(1000); + getTemplate(loc, TEMPLATE, 1, hwdata[taginfo->hwType].basetype); - } else if (taginfo->hwType == SOLUM_154_SSD1619) { - initSprite(spr, 152, 152); - drawString(spr, languageDays[getCurrentLanguage()][timeinfo.tm_wday], 152 / 2, 10, "fonts/calibrib30", TC_DATUM); - drawString(spr, String(languageMonth[getCurrentLanguage()][timeinfo.tm_mon]), 152 / 2, 120, "fonts/calibrib30", TC_DATUM); - drawString(spr, String(timeinfo.tm_mday), 152 / 2, 42, "fonts/calibrib100", TC_DATUM, PAL_RED); + initSprite(spr, hwdata[taginfo->hwType].width, hwdata[taginfo->hwType].height, imageParams); - } else if (taginfo->hwType == SOLUM_42_SSD1619) { - initSprite(spr, 400, 300); - drawString(spr, languageDays[getCurrentLanguage()][timeinfo.tm_wday], 400 / 2, 30, "fonts/calibrib60", TC_DATUM, PAL_RED); - drawString(spr, String(timeinfo.tm_mday) + " " + languageMonth[getCurrentLanguage()][timeinfo.tm_mon], 400 / 2, 113, "fonts/calibrib50", TC_DATUM); + if (loc["date"]) { + drawString(spr, languageDays[getCurrentLanguage()][timeinfo.tm_wday], loc["weekday"][0], loc["weekday"][1], loc["weekday"][2], TC_DATUM, PAL_RED); + drawString(spr, String(timeinfo.tm_mday) + " " + languageMonth[getCurrentLanguage()][timeinfo.tm_mon], loc["date"][0], loc["date"][1], loc["date"][2], TC_DATUM); + } else { + drawString(spr, languageDays[getCurrentLanguage()][timeinfo.tm_wday], loc["weekday"][0], loc["weekday"][1], loc["weekday"][2], TC_DATUM, PAL_BLACK); + drawString(spr, String(languageMonth[getCurrentLanguage()][timeinfo.tm_mon]), loc["month"][0], loc["month"][1], loc["month"][2], TC_DATUM); + drawString(spr, String(timeinfo.tm_mday), loc["day"][0], loc["day"][1], loc["day"][2], TC_DATUM, PAL_RED); } spr2buffer(spr, filename, imageParams); @@ -371,63 +391,30 @@ void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord TFT_eSPI tft = TFT_eSPI(); TFT_eSprite spr = TFT_eSprite(&tft); - LittleFS.begin(); - if (taginfo->hwType == SOLUM_29_SSD1619 || taginfo->hwType == SOLUM_29_UC8151) { - initSprite(spr, 296, 128); - spr.setTextDatum(MC_DATUM); - if (count > thresholdred) { - spr.setTextColor(PAL_RED, PAL_WHITE); - } else { - spr.setTextColor(PAL_BLACK, PAL_WHITE); - } - String font = "fonts/calibrib150"; - if (count > 99) font = "fonts/calibrib150"; - if (count > 999) font = "fonts/calibrib120"; - if (count > 9999) font = "fonts/calibrib100"; - spr.loadFont(font, LittleFS); - spr.drawString(String(count), 296 / 2, 128 / 2 + 10); - spr.unloadFont(); + DynamicJsonDocument loc(1000); + getTemplate(loc, TEMPLATE, 2, hwdata[taginfo->hwType].basetype); - } else if (taginfo->hwType == SOLUM_154_SSD1619) { - initSprite(spr, 152, 152); - spr.setTextDatum(MC_DATUM); - if (count > thresholdred) { - spr.setTextColor(PAL_RED, PAL_WHITE); - } else { - spr.setTextColor(PAL_BLACK, PAL_WHITE); - } - String font = "fonts/calibrib120"; - if (count > 99) font = "fonts/calibrib80"; - if (count > 999) font = "fonts/calibrib50"; - if (count > 9999) font = "fonts/calibrib50"; - spr.loadFont(font, LittleFS); - spr.drawString(String(count), 152 / 2, 152 / 2 + 7); - spr.unloadFont(); - - } else if (taginfo->hwType == SOLUM_42_SSD1619) { - initSprite(spr, 400, 300); - spr.setTextDatum(MC_DATUM); - if (count > thresholdred) { - spr.setTextColor(PAL_RED, PAL_WHITE); - } else { - spr.setTextColor(PAL_BLACK, PAL_WHITE); - } - String font = "fonts/calibrib150"; - if (count > 99) font = "fonts/calibrib150"; - if (count > 999) font = "fonts/calibrib120"; - if (count > 9999) font = "fonts/calibrib100"; - spr.loadFont(font, LittleFS); - spr.drawString(String(count), 400 / 2, 300 / 2 + 7); - spr.unloadFont(); + initSprite(spr, hwdata[taginfo->hwType].width, hwdata[taginfo->hwType].height, imageParams); + spr.setTextDatum(MC_DATUM); + if (count > thresholdred) { + spr.setTextColor(PAL_RED, PAL_WHITE); + } else { + spr.setTextColor(PAL_BLACK, PAL_WHITE); } + String font = loc["fonts"][0].as(); + if (count > 99) font = loc["fonts"][1].as(); + if (count > 999) font = loc["fonts"][2].as(); + if (count > 9999) font = loc["fonts"][3].as(); + spr.loadFont(font, LittleFS); + spr.drawString(String(count), loc["xy"][0].as(), loc["xy"][1].as()); + spr.unloadFont(); spr2buffer(spr, filename, imageParams); spr.deleteSprite(); } void drawWeather(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgParam &imageParams) { - wsLog("get weather"); getLocation(cfgobj); @@ -442,15 +429,8 @@ void drawWeather(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgP Serial.printf("Got code %d for this OpenMeteo\n", httpCode); 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; - filter["current_weather"]["is_day"] = true; - StaticJsonDocument<1000> doc; - DeserializationError error = deserializeJson(doc, http.getString(), DeserializationOption::Filter(filter)); + DeserializationError error = deserializeJson(doc, http.getString()); if (error) { Serial.println(F("deserializeJson() failed: ")); Serial.println(error.c_str()); @@ -484,12 +464,15 @@ void drawWeather(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgP return; } - 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"}; + DynamicJsonDocument loc(1000); + getTemplate(loc, TEMPLATE, 4, hwdata[taginfo->hwType].basetype); + + 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 (isday == 0) { weatherIcons[0] = "\uf02e"; weatherIcons[1] = "\uf083"; @@ -498,109 +481,36 @@ void drawWeather(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgP TFT_eSPI tft = TFT_eSPI(); TFT_eSprite spr = TFT_eSprite(&tft); - LittleFS.begin(); tft.setTextWrap(false, false); - if (taginfo->hwType == SOLUM_29_SSD1619 || taginfo->hwType == SOLUM_29_UC8151) { - initSprite(spr, 296, 128); + initSprite(spr, hwdata[taginfo->hwType].width, hwdata[taginfo->hwType].height, imageParams); + drawString(spr, cfgobj["location"], loc["location"][0], loc["location"][1], loc["location"][2]); + drawString(spr, String(wind), loc["wind"][0], loc["wind"][1], loc["wind"][2], TR_DATUM, (wind > 4 ? PAL_RED : PAL_BLACK)); - drawString(spr, cfgobj["location"], 5, 5, "fonts/bahnschrift30"); - drawString(spr, String(wind), 280, 5, "fonts/bahnschrift30", TR_DATUM, (wind > 4 ? PAL_RED : PAL_BLACK)); + char tmpOutput[5]; + dtostrf(temperature, 2, 1, tmpOutput); + drawString(spr, String(tmpOutput), loc["temp"][0], loc["temp"][1], loc["temp"][2], TL_DATUM, (temperature < 0 ? PAL_RED : PAL_BLACK)); - char tmpOutput[5]; - dtostrf(temperature, 2, 1, tmpOutput); - drawString(spr, String(tmpOutput), 5, 65, "fonts/bahnschrift70", TL_DATUM, (temperature < 0 ? PAL_RED : PAL_BLACK)); - - spr.loadFont("fonts/weathericons70", LittleFS); - if (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 99) { - spr.setTextColor(PAL_RED, PAL_WHITE); - } else { - spr.setTextColor(PAL_BLACK, PAL_WHITE); - } - - spr.setCursor(185, 32); - spr.printToSprite(weatherIcons[weathercode]); - spr.unloadFont(); - - spr.loadFont("fonts/weathericons30", LittleFS); + spr.loadFont(loc["icon"][2], LittleFS); + if (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 99) { + spr.setTextColor(PAL_RED, PAL_WHITE); + } else { spr.setTextColor(PAL_BLACK, PAL_WHITE); - spr.setCursor(235, -3); - spr.printToSprite(windDirectionIcon(winddirection)); - if (weathercode > 10) { - spr.setTextColor(PAL_RED, PAL_WHITE); - spr.setCursor(190, 0); - spr.printToSprite("\uf084"); - } - spr.unloadFont(); - - } else if (taginfo->hwType == SOLUM_154_SSD1619) { - initSprite(spr, 152, 152); - spr.setTextDatum(TL_DATUM); - - spr.setTextFont(2); - spr.setTextColor(PAL_BLACK, PAL_WHITE); - spr.drawString(cfgobj["location"].as(), 10, 130); - - char tmpOutput[5]; - dtostrf(temperature, 2, 1, tmpOutput); - drawString(spr, String(tmpOutput), 10, 10, "fonts/bahnschrift30", TL_DATUM, (temperature < 0 ? PAL_RED : PAL_BLACK)); - - spr.loadFont("fonts/weathericons78", LittleFS); - if (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 99) { - spr.setTextColor(PAL_RED, PAL_WHITE); - } else { - spr.setTextColor(PAL_BLACK, PAL_WHITE); - } - - spr.setCursor(33, 33); - spr.printToSprite(weatherIcons[weathercode]); - spr.unloadFont(); - - drawString(spr, String(wind), 140, 10, "fonts/bahnschrift30", TR_DATUM, (wind > 4 ? PAL_RED : PAL_BLACK)); - - spr.loadFont("fonts/weathericons30", LittleFS); - spr.setTextColor(PAL_BLACK, PAL_WHITE); - spr.setCursor(100, -2); - spr.printToSprite(windDirectionIcon(winddirection)); - if (weathercode > 10) { - spr.setTextColor(PAL_RED, PAL_WHITE); - spr.setCursor(115, 110); - spr.printToSprite("\uf084"); - } - spr.unloadFont(); - - } else if (taginfo->hwType == SOLUM_42_SSD1619) { - initSprite(spr, 400, 300); - - drawString(spr, cfgobj["location"], 10, 10, "fonts/bahnschrift30"); - drawString(spr, String(wind), 280, 10, "fonts/bahnschrift30", TR_DATUM, (wind > 4 ? PAL_RED : PAL_BLACK)); - - char tmpOutput[5]; - dtostrf(temperature, 2, 1, tmpOutput); - drawString(spr, String(tmpOutput), 5, 65, "fonts/bahnschrift70", TL_DATUM, (temperature < 0 ? PAL_RED : PAL_BLACK)); - - spr.loadFont("fonts/weathericons70", LittleFS); - if (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 99) { - spr.setTextColor(PAL_RED, PAL_WHITE); - } else { - spr.setTextColor(PAL_BLACK, PAL_WHITE); - } - - spr.setCursor(185, 32); - spr.printToSprite(weatherIcons[weathercode]); - spr.unloadFont(); - - spr.loadFont("fonts/weathericons30", LittleFS); - spr.setTextColor(PAL_BLACK, PAL_WHITE); - spr.setCursor(235, -3); - spr.printToSprite(windDirectionIcon(winddirection)); - if (weathercode > 10) { - spr.setTextColor(PAL_RED, PAL_WHITE); - spr.setCursor(190, 0); - spr.printToSprite("\uf084"); - } - spr.unloadFont(); } + spr.setCursor(loc["icon"][0], loc["icon"][1]); + spr.printToSprite(weatherIcons[weathercode]); + spr.unloadFont(); + + spr.loadFont(loc["dir"][2], LittleFS); + spr.setTextColor(PAL_BLACK, PAL_WHITE); + spr.setCursor(loc["dir"][0], loc["dir"][1]); + spr.printToSprite(windDirectionIcon(winddirection)); + if (weathercode > 10) { + spr.setTextColor(PAL_RED, PAL_WHITE); + spr.setCursor(loc["umbrella"][0], loc["umbrella"][1]); + spr.printToSprite("\uf084"); + } + spr.unloadFont(); spr2buffer(spr, filename, imageParams); spr.deleteSprite(); @@ -626,156 +536,77 @@ void drawForecast(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, img int httpCode = http.GET(); if (httpCode == 200) { - StaticJsonDocument<500> filter; - filter["daily"]["time"][0] = true; - filter["daily"]["weathercode"][0] = true; - filter["daily"]["temperature_2m_max"][0] = true; - filter["daily"]["temperature_2m_min"][0] = true; - filter["daily"]["precipitation_sum"][0] = true; - filter["daily"]["windspeed_10m_max"][0] = true; - filter["daily"]["winddirection_10m_dominant"][0] = true; - DynamicJsonDocument doc(2000); - DeserializationError error = deserializeJson(doc, http.getString(), DeserializationOption::Filter(filter)); + DeserializationError error = deserializeJson(doc, http.getString()); if (error) { Serial.println(F("deserializeJson() failed: ")); Serial.println(error.c_str()); } - 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"}; + 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"}; - LittleFS.begin(); tft.setTextWrap(false, false); - if (taginfo->hwType == SOLUM_29_SSD1619 || taginfo->hwType == SOLUM_29_UC8151) { - initSprite(spr, 296, 128); + DynamicJsonDocument loc(1000); + getTemplate(loc, TEMPLATE, 8, hwdata[taginfo->hwType].basetype); + initSprite(spr, hwdata[taginfo->hwType].width, hwdata[taginfo->hwType].height, imageParams); - spr.setTextFont(2); - spr.setTextColor(PAL_BLACK, PAL_WHITE); - spr.drawString(cfgobj["location"].as(), 5, 0); - - for (uint8_t dag = 0; dag < 5; dag++) { - time_t weatherday = doc["daily"]["time"][dag].as(); - struct tm *datum = localtime(&weatherday); - drawString(spr, String(languageDaysShort[getCurrentLanguage()][datum->tm_wday]), dag * 59 + 30, 18, "fonts/twcondensed20", TC_DATUM, PAL_BLACK); - - uint8_t weathercode = doc["daily"]["weathercode"][dag].as(); - if (weathercode > 40) weathercode -= 40; - - spr.loadFont("fonts/weathericons30", LittleFS); - if (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 99) { - spr.setTextColor(PAL_RED, PAL_WHITE); - } else { - spr.setTextColor(PAL_BLACK, PAL_WHITE); - } - spr.setTextDatum(TL_DATUM); - spr.setCursor(12 + dag * 59, 58); - spr.printToSprite(weatherIcons[weathercode]); + drawString(spr, cfgobj["location"], loc["location"][0], loc["location"][1], loc["location"][2], TL_DATUM, PAL_BLACK); + for (uint8_t dag = 0; dag < loc["column"][0]; dag++) { + time_t weatherday = doc["daily"]["time"][dag].as(); + struct tm *datum = localtime(&weatherday); + drawString(spr, String(languageDaysShort[getCurrentLanguage()][datum->tm_wday]), dag * loc["column"][1].as() + loc["day"][0].as(), loc["day"][1], loc["day"][2], TC_DATUM, PAL_BLACK); + uint8_t weathercode = doc["daily"]["weathercode"][dag].as(); + if (weathercode > 40) weathercode -= 40; + spr.loadFont(loc["icon"][2], LittleFS); + if (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 99) { + spr.setTextColor(PAL_RED, PAL_WHITE); + } else { spr.setTextColor(PAL_BLACK, PAL_WHITE); - spr.setCursor(17 + dag * 59, 27); - spr.printToSprite(windDirectionIcon(doc["daily"]["winddirection_10m_dominant"][dag])); - spr.unloadFont(); + } + spr.setTextDatum(TL_DATUM); + spr.setCursor(loc["icon"][0].as() + dag * loc["column"][1].as(), loc["icon"][1]); + spr.printToSprite(weatherIcons[weathercode]); - int8_t tmin = round(doc["daily"]["temperature_2m_min"][dag].as()); - int8_t tmax = round(doc["daily"]["temperature_2m_max"][dag].as()); - uint8_t wind = windSpeedToBeaufort(doc["daily"]["windspeed_10m_max"][dag].as()); + spr.setTextColor(PAL_BLACK, PAL_WHITE); + spr.setCursor(loc["wind"][0].as() + dag * loc["column"][1].as(), loc["wind"][1]); + spr.printToSprite(windDirectionIcon(doc["daily"]["winddirection_10m_dominant"][dag])); + spr.unloadFont(); - spr.loadFont("fonts/twcondensed20", LittleFS); - drawString(spr, String(tmin) + " ", dag * 59 + 30, 108, "", TR_DATUM, (tmin < 0 ? PAL_RED : PAL_BLACK)); - drawString(spr, String(" ") + String(tmax), dag * 59 + 30, 108, "", TL_DATUM, (tmax < 0 ? PAL_RED : PAL_BLACK)); - drawString(spr, String(" ") + String(wind), dag * 59 + 30, 43, "", TL_DATUM, (wind > 5 ? PAL_RED : PAL_BLACK)); - spr.unloadFont(); - if (dag > 0) { - for (int i = 20; i < 128; i += 3) { - spr.drawPixel(dag * 59, i, PAL_BLACK); - } - } + int8_t tmin = round(doc["daily"]["temperature_2m_min"][dag].as()); + int8_t tmax = round(doc["daily"]["temperature_2m_max"][dag].as()); + uint8_t wind = windSpeedToBeaufort(doc["daily"]["windspeed_10m_max"][dag].as()); + + spr.loadFont(loc["day"][2], LittleFS); + + if (loc["rain"]) { + int8_t rain = round(doc["daily"]["precipitation_sum"][dag].as()); + drawString(spr, String(rain) + "mm", dag * loc["column"][1].as() + loc["rain"][0].as(), loc["rain"][1], "", TC_DATUM, (rain > 10 ? PAL_RED : PAL_BLACK)); } - } else if (taginfo->hwType == SOLUM_42_SSD1619) { - initSprite(spr, 400, 300); - spr.setTextFont(2); - spr.setTextColor(PAL_BLACK, PAL_WHITE); - spr.drawString(cfgobj["location"].as(), 5, 0); - - for (uint8_t dag = 0; dag < 5; dag++) { - time_t weatherday = doc["daily"]["time"][dag].as(); - struct tm *datum = localtime(&weatherday); - drawString(spr, String(languageDaysShort[getCurrentLanguage()][datum->tm_wday]), dag * 59 + 30, 18, "fonts/twcondensed20", TC_DATUM, PAL_BLACK); - - uint8_t weathercode = doc["daily"]["weathercode"][dag].as(); - if (weathercode > 40) weathercode -= 40; - - spr.loadFont("fonts/weathericons30", LittleFS); - if (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 99) { - spr.setTextColor(PAL_RED, PAL_WHITE); - } else { - spr.setTextColor(PAL_BLACK, PAL_WHITE); - } - spr.setTextDatum(TL_DATUM); - spr.setCursor(12 + dag * 59, 58); - spr.printToSprite(weatherIcons[weathercode]); - - spr.setTextColor(PAL_BLACK, PAL_WHITE); - spr.setCursor(17 + dag * 59, 27); - spr.printToSprite(windDirectionIcon(doc["daily"]["winddirection_10m_dominant"][dag])); - spr.unloadFont(); - - int8_t tmin = round(doc["daily"]["temperature_2m_min"][dag].as()); - int8_t tmax = round(doc["daily"]["temperature_2m_max"][dag].as()); - uint8_t wind = windSpeedToBeaufort(doc["daily"]["windspeed_10m_max"][dag].as()); - - spr.loadFont("fonts/twcondensed20", LittleFS); - drawString(spr, String(tmin) + " ", dag * 59 + 30, 108, "", TR_DATUM, (tmin < 0 ? PAL_RED : PAL_BLACK)); - drawString(spr, String(" ") + String(tmax), dag * 59 + 30, 108, "", TL_DATUM, (tmax < 0 ? PAL_RED : PAL_BLACK)); - drawString(spr, String(" ") + String(wind), dag * 59 + 30, 43, "", TL_DATUM, (wind > 5 ? PAL_RED : PAL_BLACK)); - spr.unloadFont(); - if (dag > 0) { - for (int i = 20; i < 128; i += 3) { - spr.drawPixel(dag * 59, i, PAL_BLACK); - } + drawString(spr, String(tmin) + " ", dag * loc["column"][1].as() + loc["day"][0].as(), loc["day"][4], "", TR_DATUM, (tmin < 0 ? PAL_RED : PAL_BLACK)); + drawString(spr, String(" ") + String(tmax), dag * loc["column"][1].as() + loc["day"][0].as(), loc["day"][4], "", TL_DATUM, (tmax < 0 ? PAL_RED : PAL_BLACK)); + drawString(spr, String(" ") + String(wind), dag * loc["column"][1].as() + loc["day"][0].as(), loc["day"][3], "", TL_DATUM, (wind > 5 ? PAL_RED : PAL_BLACK)); + spr.unloadFont(); + if (dag > 0) { + for (int i = loc["line"][0]; i < loc["line"][1]; i += 3) { + spr.drawPixel(dag * loc["column"][1].as(), i, PAL_BLACK); } } } + spr2buffer(spr, filename, imageParams); spr.deleteSprite(); } http.end(); } -void drawIdentify(String &filename, tagRecord *&taginfo, imgParam &imageParams) { - TFT_eSPI tft = TFT_eSPI(); - TFT_eSprite spr = TFT_eSprite(&tft); - LittleFS.begin(); - - char hexmac[17]; - mac2hex(taginfo->mac, hexmac); - if (taginfo->hwType == SOLUM_29_SSD1619 || taginfo->hwType == SOLUM_29_UC8151) { - initSprite(spr, 296, 128); - drawString(spr, taginfo->alias, 10, 10, "fonts/bahnschrift20"); - drawString(spr, String(hexmac), 10, 50, "fonts/bahnschrift20", TL_DATUM, PAL_RED); - - } else if (taginfo->hwType == SOLUM_154_SSD1619) { - initSprite(spr, 152, 152); - drawString(spr, taginfo->alias, 5, 5, "fonts/bahnschrift20"); - drawString(spr, String(hexmac), 10, 50, "fonts/bahnschrift20", TL_DATUM, PAL_RED); - - } else if (taginfo->hwType == SOLUM_42_SSD1619) { - initSprite(spr, 400, 300); - drawString(spr, taginfo->alias, 20, 20, "fonts/bahnschrift20"); - drawString(spr, String(hexmac), 20, 70, "fonts/bahnschrift20", TL_DATUM, PAL_RED); - } - - spr2buffer(spr, filename, imageParams); - spr.deleteSprite(); -} - int getImgURL(String &filename, String URL, time_t fetched, imgParam &imageParams, String MAC) { // https://images.klari.net/kat-bw29.jpg @@ -818,11 +649,6 @@ bool getRssFeed(String &filename, String URL, String title, tagRecord *&taginfo, // https://www.nu.nl/rss/Algemeen Serial.println("RSS feed"); - 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); - const char *url = URL.c_str(); const char *tag = "title"; const int rssArticleSize = 128; @@ -832,50 +658,23 @@ bool getRssFeed(String &filename, String URL, String title, tagRecord *&taginfo, U8g2_for_TFT_eSPI u8f; u8f.begin(spr); - if (taginfo->hwType == SOLUM_29_SSD1619 || taginfo->hwType == SOLUM_29_UC8151) { - initSprite(spr, 296, 128); - if (title == "" || title == "null") title = "RSS feed"; - drawString(spr, title, 5, 3, "fonts/bahnschrift20", TL_DATUM, PAL_RED); + DynamicJsonDocument loc(1000); + getTemplate(loc, TEMPLATE, 9, hwdata[taginfo->hwType].basetype); + initSprite(spr, hwdata[taginfo->hwType].width, hwdata[taginfo->hwType].height, imageParams); - u8f.setFont(u8g2_font_glasstown_nbp_tr); - u8f.setFontMode(0); - u8f.setFontDirection(0); - u8f.setForegroundColor(PAL_BLACK); - u8f.setBackgroundColor(PAL_WHITE); - u8f.setCursor(220, 20); - u8f.print(header); + if (title == "" || title == "null") title = "RSS feed"; + drawString(spr, title, loc["title"][0], loc["title"][1], loc["title"][2], TL_DATUM, PAL_BLACK); - // u8g2_font_nine_by_five_nbp_tr - // u8g2_font_7x14_tr - // u8g2_font_crox1h_tr - // u8g2_font_miranda_nbp_tr - u8f.setFont(u8g2_font_glasstown_nbp_tr); + setU8G2Font(loc["font"], u8f); + u8f.setFontMode(0); + u8f.setFontDirection(0); + u8f.setForegroundColor(PAL_BLACK); + u8f.setBackgroundColor(PAL_WHITE); - int n = reader.getArticles(url, tag, rssArticleSize, 8); - for (int i = 0; i < n; i++) { - u8f.setCursor(5, 34 + i * 13); - u8f.print(reader.itemData[i]); - } - } else if (taginfo->hwType == SOLUM_42_SSD1619) { - initSprite(spr, 400, 300); - if (title == "" || title == "null") title = "RSS feed"; - drawString(spr, title, 5, 5, "fonts/bahnschrift20", TL_DATUM, PAL_RED); - - u8f.setFont(u8g2_font_glasstown_nbp_tr); - u8f.setFontMode(0); - u8f.setFontDirection(0); - u8f.setForegroundColor(PAL_BLACK); - u8f.setBackgroundColor(PAL_WHITE); - u8f.setCursor(220, 20); - u8f.print(header); - - u8f.setFont(u8g2_font_glasstown_nbp_tr); - - int n = reader.getArticles(url, tag, rssArticleSize, 10); - for (int i = 0; i < n; i++) { - u8f.setCursor(5, 34 + i * 14); - u8f.print(reader.itemData[i]); - } + int n = reader.getArticles(url, tag, rssArticleSize, loc["items"]); + for (int i = 0; i < n; i++) { + u8f.setCursor(loc["line"][0], loc["line"][1].as() + i * loc["line"][2].as()); + u8f.print(reader.itemData[i]); } spr2buffer(spr, filename, imageParams); @@ -920,7 +719,7 @@ bool getCalFeed(String &filename, String URL, String title, tagRecord *&taginfo, HTTPClient http; http.begin(URL); - http.setTimeout(10000); + http.setTimeout(10000); http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); int httpCode = http.GET(); if (httpCode != 200) { @@ -940,80 +739,37 @@ bool getCalFeed(String &filename, String URL, String title, tagRecord *&taginfo, U8g2_for_TFT_eSPI u8f; u8f.begin(spr); - if (taginfo->hwType == SOLUM_29_SSD1619 || taginfo->hwType == SOLUM_29_UC8151) { - initSprite(spr, 296, 128); - if (title == "" || title == "null") title = "Calendar"; + DynamicJsonDocument loc(1000); + getTemplate(loc, TEMPLATE, 11, hwdata[taginfo->hwType].basetype); + initSprite(spr, hwdata[taginfo->hwType].width, hwdata[taginfo->hwType].height, imageParams); - u8f.setFont(u8g2_font_t0_22b_tr); // select u8g2 font from here: https://github.com/olikraus/u8g2/wiki/fntlistall - u8f.setFontMode(0); - u8f.setFontDirection(0); - u8f.setForegroundColor(PAL_BLACK); - u8f.setBackgroundColor(PAL_WHITE); - u8f.setCursor(5, 16); - u8f.print(title); - u8f.setCursor(180, 16); - u8f.print(dateString); + if (title == "" || title == "null") title = "Calendar"; + drawString(spr, title, loc["title"][0], loc["title"][1], loc["title"][2], TL_DATUM, PAL_BLACK); + drawString(spr, dateString, loc["date"][0], loc["date"][1], loc["title"][2], TR_DATUM, PAL_BLACK); - int n = doc.size(); - if (n > 7) n = 7; - for (int i = 0; i < n; i++) { - JsonObject obj = doc[i]; - String eventtitle = obj["title"]; - String startz = obj["start"]; - time_t starttime = obj["start"]; - time_t endtime = obj["end"]; - if (starttime < now && endtime > now) { - u8f.setFont(u8g2_font_t0_14b_tr); - u8f.setForegroundColor(PAL_WHITE); - u8f.setBackgroundColor(PAL_RED); - spr.fillRect(0, i * 15 + 21, 296, 14, PAL_RED); - } else { - u8f.setFont(u8g2_font_t0_14_tr); - u8f.setForegroundColor(PAL_BLACK); - u8f.setBackgroundColor(PAL_WHITE); - } - u8f.setCursor(5, 32 + i * 15); - u8f.print(epoch_to_display(obj["start"])); - u8f.setCursor(50, 32 + i * 15); - u8f.print(eventtitle); - } - } else if (taginfo->hwType == SOLUM_42_SSD1619) { - initSprite(spr, 400, 300); - if (title == "" || title == "null") title = "Calendar"; - - u8f.setFont(u8g2_font_t0_22b_tr); - u8f.setFontMode(0); - u8f.setFontDirection(0); - u8f.setForegroundColor(PAL_BLACK); - u8f.setBackgroundColor(PAL_WHITE); - u8f.setCursor(5, 16); - u8f.print(title); - u8f.setCursor(280, 16); - u8f.print(dateString); - - int n = doc.size(); - if (n > 8) n = 8; - for (int i = 0; i < n; i++) { - JsonObject obj = doc[i]; - String eventtitle = obj["title"]; - String startz = obj["start"]; - time_t starttime = obj["start"]; - time_t endtime = obj["end"]; - if (starttime < now && endtime > now) { - u8f.setFont(u8g2_font_t0_14b_tr); - u8f.setForegroundColor(PAL_WHITE); - u8f.setBackgroundColor(PAL_RED); - spr.fillRect(0, i * 15 + 21, 296, 14, PAL_RED); - } else { - u8f.setFont(u8g2_font_t0_14_tr); - u8f.setForegroundColor(PAL_BLACK); - u8f.setBackgroundColor(PAL_WHITE); - } - u8f.setCursor(5, 32 + i * 15); - u8f.print(epoch_to_display(obj["start"])); - u8f.setCursor(50, 32 + i * 15); - u8f.print(eventtitle); + u8f.setFontMode(0); + u8f.setFontDirection(0); + int n = doc.size(); + if (n > loc["items"]) n = loc["items"]; + for (int i = 0; i < n; i++) { + JsonObject obj = doc[i]; + String eventtitle = obj["title"]; + String startz = obj["start"]; + time_t starttime = obj["start"]; + time_t endtime = obj["end"]; + setU8G2Font(loc["line"][3], u8f); + if (starttime < now && endtime > now) { + u8f.setForegroundColor(PAL_WHITE); + u8f.setBackgroundColor(PAL_RED); + spr.fillRect(loc["red"][0], loc["red"][1].as() + i * loc["line"][2].as(), loc["red"][2], loc["red"][3], PAL_RED); + } else { + u8f.setForegroundColor(PAL_BLACK); + u8f.setBackgroundColor(PAL_WHITE); } + u8f.setCursor(loc["line"][0], loc["line"][1].as() + i * loc["line"][2].as()); + u8f.print(epoch_to_display(obj["start"])); + u8f.setCursor(loc["line"][4], loc["line"][1].as() + i * loc["line"][2].as()); + u8f.print(eventtitle); } spr2buffer(spr, filename, imageParams); @@ -1036,27 +792,14 @@ void drawQR(String &filename, String qrcontent, String title, tagRecord *&taginf int size = qrcode.size; int xpos = 0, ypos = 0, dotsize = 1; - if (taginfo->hwType == SOLUM_29_SSD1619 || taginfo->hwType == SOLUM_29_UC8151) { - initSprite(spr, 296, 128); - drawString(spr, title, 10, 5, "fonts/bahnschrift20"); - dotsize = int((128 - 25) / size); - xpos = 149 - dotsize * size / 2; - ypos = 25; - } else if (taginfo->hwType == SOLUM_154_SSD1619) { - initSprite(spr, 152, 152); - spr.setTextFont(2); - spr.setTextColor(PAL_BLACK, PAL_WHITE); - spr.drawString(title, 10, 5); - dotsize = int((152 - 20) / size); - xpos = 76 - dotsize * size / 2; - ypos = 20; - } else if (taginfo->hwType == SOLUM_42_SSD1619) { - initSprite(spr, 400, 300); - drawString(spr, title, 10, 10, "fonts/bahnschrift20"); - dotsize = int((300 - 30) / size); - xpos = 200 - dotsize * size / 2; - ypos = 30; - } + DynamicJsonDocument loc(1000); + getTemplate(loc, TEMPLATE, 10, hwdata[taginfo->hwType].basetype); + initSprite(spr, hwdata[taginfo->hwType].width, hwdata[taginfo->hwType].height, imageParams); + drawString(spr, title, loc["title"][0], loc["title"][1], loc["title"][2]); + + dotsize = int((hwdata[taginfo->hwType].height - loc["pos"][1].as()) / size); + xpos = loc["pos"][0].as() - dotsize * size / 2; + ypos = loc["pos"][1]; for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { @@ -1072,7 +815,6 @@ void drawQR(String &filename, String qrcontent, String title, tagRecord *&taginf } void drawBuienradar(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgParam &imageParams) { - wsLog("get weather"); getLocation(cfgobj); @@ -1092,55 +834,56 @@ void drawBuienradar(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, i U8g2_for_TFT_eSPI u8f; u8f.begin(spr); + DynamicJsonDocument loc(1000); + getTemplate(loc, TEMPLATE, 16, hwdata[taginfo->hwType].basetype); + initSprite(spr, hwdata[taginfo->hwType].width, hwdata[taginfo->hwType].height, imageParams); + tft.setTextWrap(false, false); String response = http.getString(); - if (taginfo->hwType == SOLUM_29_SSD1619 || taginfo->hwType == SOLUM_29_UC8151) { + drawString(spr, cfgobj["location"], loc["location"][0], loc["location"][1], loc["location"][2]); - initSprite(spr, 296, 128); - drawString(spr, cfgobj["location"], 5, 5, "fonts/bahnschrift30"); + for (int i = 0; i < 295; i += 4) { + spr.drawPixel(i, 110, PAL_BLACK); + spr.drawPixel(i, 81, PAL_BLACK); + spr.drawPixel(i, 72, PAL_BLACK); + spr.drawPixel(i, 62, PAL_BLACK); + spr.drawPixel(i, 52, PAL_BLACK); + spr.drawPixel(i, 46, PAL_BLACK); + spr.drawPixel(i, 42, PAL_BLACK); + } - for (int i = 0; i < 295; i += 4) { - spr.drawPixel(i, 110, PAL_BLACK); - spr.drawPixel(i, 81, PAL_BLACK); - spr.drawPixel(i, 72, PAL_BLACK); - spr.drawPixel(i, 62, PAL_BLACK); - spr.drawPixel(i, 52, PAL_BLACK); - spr.drawPixel(i, 46, PAL_BLACK); - spr.drawPixel(i, 42, PAL_BLACK); - } + setU8G2Font(loc["title"][2], u8f); + u8f.setFontMode(0); + u8f.setFontDirection(0); + u8f.setForegroundColor(PAL_BLACK); + u8f.setBackgroundColor(PAL_WHITE); + u8f.setCursor(loc["title"][0], loc["title"][1]); + u8f.print("Buienradar"); - u8f.setFont(u8g2_font_glasstown_nbp_tr); - u8f.setFontMode(0); - u8f.setFontDirection(0); - u8f.setForegroundColor(PAL_BLACK); - u8f.setBackgroundColor(PAL_WHITE); - u8f.setCursor(247, 11); - u8f.print("Buienradar"); + for (int i = 0; i < 24; i++) { + int startPos = i * 11; + uint8_t value = response.substring(startPos, startPos + 3).toInt(); + String timestring = response.substring(startPos + 4, startPos + 9); + int minutes = timestring.substring(3).toInt(); + if (value < 60) value = 60; + if (value > 170) value = 170; - for (int i = 0; i < 24; i++) { - int startPos = i * 11; - - uint8_t value = response.substring(startPos, startPos + 3).toInt(); - String timestring = response.substring(startPos + 4, startPos + 9); - int minutes = timestring.substring(3).toInt(); - if (value < 60) value = 60; - if (value > 170) value = 170; - - spr.fillRect(i * 12 + 5, 111 - (value - 60), 10, (value - 60), (value > 130 ? PAL_RED : PAL_BLACK)); + spr.fillRect(i * loc["cols"][2].as() + loc["bars"][0].as(), loc["bars"][1].as() - (value - 60), loc["bars"][2], (value - 60), (value > 130 ? PAL_RED : PAL_BLACK)); + if (minutes % 15 == 0) { spr.setTextFont(2); spr.setTextColor(PAL_BLACK, PAL_WHITE); - u8f.setCursor(i * 12 + 1, 125); - if (minutes % 15 == 0) u8f.print(timestring); + u8f.setCursor(i * loc["cols"][2].as() + loc["cols"][0].as(), loc["cols"][1]); + u8f.print(timestring); } } + spr2buffer(spr, filename, imageParams); spr.deleteSprite(); } http.end(); - } char *formatHttpDate(time_t t) { @@ -1255,3 +998,32 @@ void prepareLUTreq(uint8_t *dst, String input) { size_t waveformLen = sizeof(waveform); prepareDataAvail(waveform, waveformLen, DATATYPE_CUSTOM_LUT_OTA, dst); } + +void getTemplate(JsonDocument &json, const char *filePath, uint8_t id, uint8_t hwtype) { + File jsonFile = LittleFS.open(filePath, "r"); + if (!jsonFile) { + Serial.println("Failed to open JSON file"); + return; + } + + StaticJsonDocument<50> filter; + filter[String(id)][String(hwtype)] = true; + + DynamicJsonDocument doc(1024); + DeserializationError error = deserializeJson(doc, jsonFile, DeserializationOption::Filter(filter)); + jsonFile.close(); + + if (error) { + Serial.println("json error in getTemplate:"); + Serial.println(error.c_str()); + return; + } + + json.set(doc[String(id)][String(hwtype)]); +} + +void setU8G2Font(const String &title, U8g2_for_TFT_eSPI &u8f) { + if (title == "glasstown_nbp_tf") u8f.setFont(u8g2_font_glasstown_nbp_tf); + if (title == "7x14_tf") u8f.setFont(u8g2_font_7x14_tf); + if (title == "t0_14b_tf") u8f.setFont(u8g2_font_t0_14b_tf); +} \ No newline at end of file diff --git a/ESP32_AP-Flasher/src/main.cpp b/ESP32_AP-Flasher/src/main.cpp index 49b9ba67..52b571cf 100644 --- a/ESP32_AP-Flasher/src/main.cpp +++ b/ESP32_AP-Flasher/src/main.cpp @@ -19,10 +19,22 @@ #include "udp.h" #include "web.h" +void delayedStart(void* parameter) { + vTaskDelay(30000 / portTICK_PERIOD_MS); + Serial.println("Resuming content generation"); + wsLog("resuming content generation"); + config.runStatus = RUNSTATUS_RUN; + vTaskDelete(NULL); +} + void timeTask(void* parameter) { config.runStatus = RUNSTATUS_RUN; esp_reset_reason_t resetReason = esp_reset_reason(); - // if (resetReason == ESP_RST_PANIC) config.runStatus = RUNSTATUS_PAUSE; + if (resetReason == ESP_RST_PANIC) { + Serial.println("Panic! Pausing content generation for 60 seconds"); + config.runStatus = RUNSTATUS_PAUSE; + xTaskCreate(delayedStart, "delaystart", 2000, NULL, 2, NULL); + } while (1) { time_t now; time(&now); diff --git a/ESP32_AP-Flasher/src/makeimage.cpp b/ESP32_AP-Flasher/src/makeimage.cpp index 2e5e5c49..5f0fa9c4 100644 --- a/ESP32_AP-Flasher/src/makeimage.cpp +++ b/ESP32_AP-Flasher/src/makeimage.cpp @@ -37,9 +37,10 @@ void jpg2buffer(String filein, String fileout, imgParam &imageParams) { #endif spr.createSprite(w, h); if (spr.getPointer() == nullptr) { - //no heap space for 8bpp, fallback to 1bpp - wsErr("fallback to 1bpp"); + wsErr("low on memory. Fallback to 1bpp"); spr.setColorDepth(1); + spr.setBitmapColor(TFT_WHITE, TFT_BLACK); + imageParams.bpp = 1; spr.createSprite(w, h); } if (spr.getPointer() == nullptr) { @@ -105,6 +106,7 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) { Serial.println("rendering with gray"); } int num_colors = palette.size(); + if (imageParams.bpp == 1) num_colors = 2; Color color; Error *error_bufferold = new Error[bufw + 4]; Error *error_buffernew = new Error[bufw + 4]; diff --git a/ESP32_AP-Flasher/src/tag_db.cpp b/ESP32_AP-Flasher/src/tag_db.cpp index 44e09134..f9dcf46d 100644 --- a/ESP32_AP-Flasher/src/tag_db.cpp +++ b/ESP32_AP-Flasher/src/tag_db.cpp @@ -260,13 +260,14 @@ void clearPending(tagRecord* taginfo) { void initAPconfig() { LittleFS.begin(true); - DynamicJsonDocument APconfig(150); + DynamicJsonDocument APconfig(500); File configFile = LittleFS.open("/current/apconfig.json", "r"); if (configFile) { DeserializationError error = deserializeJson(APconfig, configFile); if (error) { configFile.close(); Serial.println("failed to read apconfig.json. Using default config"); + Serial.println(error.c_str()); } configFile.close(); } @@ -280,7 +281,7 @@ void initAPconfig() { void saveAPconfig() { fs::File configFile = LittleFS.open("/current/apconfig.json", "w"); - DynamicJsonDocument APconfig(150); + DynamicJsonDocument APconfig(500); APconfig["channel"] = config.channel; APconfig["alias"] = config.alias; APconfig["led"] = config.led;