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 44aefdc3..d84a7716 100644 Binary files a/ESP32_AP-Flasher/data/fonts/bahnschrift20.vlw and b/ESP32_AP-Flasher/data/fonts/bahnschrift20.vlw differ diff --git a/ESP32_AP-Flasher/data/fonts/bahnschrift30.vlw b/ESP32_AP-Flasher/data/fonts/bahnschrift30.vlw index 091a0d7c..ba0c7b6b 100644 Binary files a/ESP32_AP-Flasher/data/fonts/bahnschrift30.vlw and b/ESP32_AP-Flasher/data/fonts/bahnschrift30.vlw differ diff --git a/ESP32_AP-Flasher/data/fonts/calibrib100.vlw b/ESP32_AP-Flasher/data/fonts/calibrib100.vlw index e4c2ce70..f6f008e9 100644 Binary files a/ESP32_AP-Flasher/data/fonts/calibrib100.vlw and b/ESP32_AP-Flasher/data/fonts/calibrib100.vlw differ diff --git a/ESP32_AP-Flasher/data/fonts/calibrib120.vlw b/ESP32_AP-Flasher/data/fonts/calibrib120.vlw index 5590633f..8308150e 100644 Binary files a/ESP32_AP-Flasher/data/fonts/calibrib120.vlw and b/ESP32_AP-Flasher/data/fonts/calibrib120.vlw differ diff --git a/ESP32_AP-Flasher/data/fonts/calibrib150.vlw b/ESP32_AP-Flasher/data/fonts/calibrib150.vlw index 1539e7ca..050cbd86 100644 Binary files a/ESP32_AP-Flasher/data/fonts/calibrib150.vlw and b/ESP32_AP-Flasher/data/fonts/calibrib150.vlw differ diff --git a/ESP32_AP-Flasher/data/fonts/calibrib30.vlw b/ESP32_AP-Flasher/data/fonts/calibrib30.vlw index 87c41c69..cd0a0fe7 100644 Binary files a/ESP32_AP-Flasher/data/fonts/calibrib30.vlw and b/ESP32_AP-Flasher/data/fonts/calibrib30.vlw differ diff --git a/ESP32_AP-Flasher/data/fonts/calibrib60.vlw b/ESP32_AP-Flasher/data/fonts/calibrib60.vlw index 04f6e3b1..3f6d44af 100644 Binary files a/ESP32_AP-Flasher/data/fonts/calibrib60.vlw and b/ESP32_AP-Flasher/data/fonts/calibrib60.vlw differ diff --git a/ESP32_AP-Flasher/data/fonts/calibrib80.vlw b/ESP32_AP-Flasher/data/fonts/calibrib80.vlw index eb9f8dfe..c78c7915 100644 Binary files a/ESP32_AP-Flasher/data/fonts/calibrib80.vlw and b/ESP32_AP-Flasher/data/fonts/calibrib80.vlw differ 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;