From 6c91c78aa1ffe9f378823ff6e917790c52fccb29 Mon Sep 17 00:00:00 2001 From: Moritz Wirger Date: Sat, 12 Aug 2023 16:57:42 +0200 Subject: [PATCH] Refactor contentmanager (#112) * Start contentmanager cleanup - Make destination constant - Remove unneeded asignments - Improve loops - Move printHeap function into util * Refactor contentmanager::drawNew * Optimize contentManager::replaceVariables * Fix missing const in prepareCancelPending * Refactor drawDate * Refactor drawWeather, drawForecast & getLocation - Generalize http get json function * Add util function for printing largest free block * Reuse weather icons for both drawWeather & drawForecast * Make httpGetJson timeout const * Reafctor more functions * Add few more const * Fix spelling mistake * Add util for debugging streams - Add util for checking if strings are empty or contain null - Fix file and string checks * Remove leftover debug print --- ESP32_AP-Flasher/include/contentmanager.h | 24 +- ESP32_AP-Flasher/include/newproto.h | 16 +- ESP32_AP-Flasher/include/tag_db.h | 13 +- ESP32_AP-Flasher/include/util.h | 102 ++++ ESP32_AP-Flasher/include/web.h | 4 +- ESP32_AP-Flasher/src/contentmanager.cpp | 617 +++++++++++----------- ESP32_AP-Flasher/src/main.cpp | 27 +- ESP32_AP-Flasher/src/makeimage.cpp | 7 +- ESP32_AP-Flasher/src/newproto.cpp | 40 +- ESP32_AP-Flasher/src/ota.cpp | 12 +- ESP32_AP-Flasher/src/tag_db.cpp | 16 +- ESP32_AP-Flasher/src/web.cpp | 9 +- 12 files changed, 503 insertions(+), 384 deletions(-) create mode 100644 ESP32_AP-Flasher/include/util.h diff --git a/ESP32_AP-Flasher/include/contentmanager.h b/ESP32_AP-Flasher/include/contentmanager.h index 99adb942..13d3aeb4 100644 --- a/ESP32_AP-Flasher/include/contentmanager.h +++ b/ESP32_AP-Flasher/include/contentmanager.h @@ -9,16 +9,16 @@ struct contentTypes { uint16_t id; String name; - uint16_t tagTypes; + uint16_t tagTypes; void (*functionname)(); - String description; - String optionList; + String description; + String optionList; }; void contentRunner(); void checkVars(); -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 drawNew(const uint8_t mac[8], const bool buttonPressed, tagRecord *&taginfo); +bool updateTagImage(String &filename, const uint8_t *dst, uint16_t nextCheckin, tagRecord *&taginfo, imgParam &imageParams); void drawString(TFT_eSprite &spr, String content, int16_t posx, int16_t posy, String font, byte align = 0, uint16_t color = TFT_BLACK, uint16_t size = 30, uint16_t bgcolor = TFT_WHITE); void initSprite(TFT_eSprite &spr, int w, int h, imgParam &imageParams); void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams); @@ -35,14 +35,14 @@ int getJsonTemplateFile(String &filename, String jsonfile, tagRecord *&taginfo, int getJsonTemplateUrl(String &filename, String URL, time_t fetched, String MAC, tagRecord *&taginfo, imgParam &imageParams); void drawJsonStream(Stream &stream, String &filename, tagRecord *&taginfo, imgParam &imageParams); void drawElement(const JsonObject &element, TFT_eSprite &spr); -uint16_t getColor(String color); +uint16_t getColor(const String &color); char *formatHttpDate(time_t t); String urlEncode(const char *msg); -int windSpeedToBeaufort(float windSpeed); -String windDirectionIcon(int degrees); +int windSpeedToBeaufort(const float windSpeed); +String windDirectionIcon(const int degrees); void getLocation(JsonObject &cfgobj); -void prepareNFCReq(uint8_t* dst, const char* url); -void prepareLUTreq(uint8_t *dst, String input); -void prepareConfigFile(uint8_t *dst, JsonObject config); -void getTemplate(JsonDocument &json, uint8_t id, uint8_t hwtype); +void prepareNFCReq(const uint8_t *dst, const char *url); +void prepareLUTreq(const uint8_t *dst, const String &input); +void prepareConfigFile(const uint8_t *dst, const JsonObject &config); +void getTemplate(JsonDocument &json, const uint8_t id, const uint8_t hwtype); void setU8G2Font(const String &title, U8g2_for_TFT_eSPI &u8f); diff --git a/ESP32_AP-Flasher/include/newproto.h b/ESP32_AP-Flasher/include/newproto.h index c41bd889..f3a290a5 100644 --- a/ESP32_AP-Flasher/include/newproto.h +++ b/ESP32_AP-Flasher/include/newproto.h @@ -4,21 +4,21 @@ extern void addCRC(void* p, uint8_t len); extern bool checkCRC(void* p, uint8_t len); extern void processBlockRequest(struct espBlockRequest* br); -extern void prepareCancelPending(uint8_t dst[8]); -extern void prepareIdleReq(uint8_t* dst, uint16_t nextCheckin); -extern void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, uint8_t* dst); -extern bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t nextCheckin); +extern void prepareCancelPending(const uint8_t dst[8]); +extern void prepareIdleReq(const uint8_t* dst, uint16_t nextCheckin); +extern void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, const uint8_t* dst); +extern bool prepareDataAvail(String* filename, uint8_t dataType, const uint8_t* dst, uint16_t nextCheckin); extern void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP); extern void processXferComplete(struct espXferComplete* xfc, bool local); extern void processXferTimeout(struct espXferComplete* xfc, bool local); extern void processDataReq(struct espAvailDataReq* adr, bool local); -extern bool sendTagCommand(uint8_t* dst, uint8_t cmd, bool local); -extern bool sendAPSegmentedData(uint8_t* dst, String data, uint16_t icons, bool inverted, bool local); -extern bool showAPSegmentedInfo(uint8_t* dst, bool local); +extern bool sendTagCommand(const uint8_t* dst, uint8_t cmd, bool local); +extern bool sendAPSegmentedData(const uint8_t* dst, String data, uint16_t icons, bool inverted, bool local); +extern bool showAPSegmentedInfo(const uint8_t* dst, bool local); extern void updateTaginfoitem(struct TagInfo* taginfoitem); bool checkMirror(struct tagRecord* taginfo, struct pendingData* pending); void refreshAllPending(); -void updateContent(uint8_t* dst); +void updateContent(const uint8_t* dst); void setAPchannel(); diff --git a/ESP32_AP-Flasher/include/tag_db.h b/ESP32_AP-Flasher/include/tag_db.h index 7ee6386a..f978432f 100644 --- a/ESP32_AP-Flasher/include/tag_db.h +++ b/ESP32_AP-Flasher/include/tag_db.h @@ -19,8 +19,7 @@ class tagRecord { public: - tagRecord() : mac{0}, alias(""), lastseen(0), nextupdate(0), contentMode(0), pending(false), md5{0}, md5pending{0}, expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0), lastfullupdate(0), isExternal(false), pendingIdle(0), hasCustomLUT(false), rotate(0), lut(0), tagSoftwareVersion(0), currentChannel(0), - dataType(0), filename(""), data(nullptr), len(0) {} + tagRecord() : mac{0}, alias(""), lastseen(0), nextupdate(0), contentMode(0), pending(false), md5{0}, md5pending{0}, expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0), lastfullupdate(0), isExternal(false), pendingIdle(0), hasCustomLUT(false), rotate(0), lut(0), tagSoftwareVersion(0), currentChannel(0), dataType(0), filename(""), data(nullptr), len(0) {} uint8_t mac[8]; String alias; @@ -53,7 +52,7 @@ class tagRecord { uint8_t* data; uint32_t len; - static tagRecord* findByMAC(uint8_t mac[8]); + static tagRecord* findByMAC(const uint8_t mac[8]); }; struct Config { @@ -87,15 +86,15 @@ extern std::vector tagDB; extern std::unordered_map hwtype; extern std::unordered_map varDB; extern DynamicJsonDocument APconfig; -String tagDBtoJson(uint8_t mac[8] = nullptr, uint8_t startPos = 0); -bool deleteRecord(uint8_t mac[8]); -void fillNode(JsonObject &tag, tagRecord* &taginfo); +String tagDBtoJson(const uint8_t mac[8] = nullptr, uint8_t startPos = 0); +bool deleteRecord(const uint8_t mac[8]); +void fillNode(JsonObject& tag, tagRecord*& taginfo); void saveDB(String filename); void loadDB(String filename); void destroyDB(); uint32_t getTagCount(); uint32_t getTagCount(uint32_t& timeoutcount); -void mac2hex(uint8_t* mac, char* hexBuffer); +void mac2hex(const uint8_t* mac, char* hexBuffer); bool hex2mac(const String& hexString, uint8_t* mac); void clearPending(tagRecord* taginfo); void initAPconfig(); diff --git a/ESP32_AP-Flasher/include/util.h b/ESP32_AP-Flasher/include/util.h new file mode 100644 index 00000000..602b8675 --- /dev/null +++ b/ESP32_AP-Flasher/include/util.h @@ -0,0 +1,102 @@ +#pragma once + +#include +#include +#include + +#include "web.h" + +namespace util { + +/// @brief Can be used to wrap a stream and see what's going on +class DebugStream : public Stream { + public: + DebugStream(Stream &stream) : _stream(stream) {} + + int available() override { + return _stream.available(); + } + + int read() override { + int data = _stream.read(); + Serial.write(data); + return data; + } + + int peek() override { + int data = _stream.peek(); + Serial.print("Peek: "); + Serial.println(data); + return data; + } + + void flush() override { + _stream.flush(); + Serial.println("Flush"); + } + + size_t write(uint8_t data) override { + Serial.write(data); + return _stream.write(data); + } + + size_t write(const uint8_t *buffer, size_t size) override { + for (size_t i = 0; i < size; i++) { + Serial.print("Write: "); + Serial.println(buffer[i]); + } + return _stream.write(buffer, size); + } + + private: + Stream &_stream; +}; + +/// @brief Prints free heap, allocatbale heap and free stack +static void printHeap() { + const uint32_t freeStack = uxTaskGetStackHighWaterMark(NULL); + Serial.printf("Free heap: %d allocatable: %d stack: %d\n", ESP.getFreeHeap(), ESP.getMaxAllocHeap(), freeStack); +} + +/// @brief Prints the maximum continuous heap space +static void printLargestFreeBlock() { + Serial.println("Maximum Continuous Heap Space: " + String(heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT))); +} + +/// @brief Do a GET request to the given url and fill the given json with the response +/// @param url Request URL +/// @param json Json document to fill +/// @param timeout Request timeout +/// @param redirects Redirects handling +/// @return True on success, false on error (httpCode != 200 || deserialization error) +static bool httpGetJson(String &url, JsonDocument &json, const uint16_t timeout) //, const followRedirects_t redirects = followRedirects_t::HTTPC_DISABLE_FOLLOW_REDIRECTS) +{ + HTTPClient http; + http.begin(url); + http.setTimeout(timeout); + // http.setFollowRedirects(redirects); + const int httpCode = http.GET(); + if (httpCode != 200) { + http.end(); + wsErr("http " + httpCode); + return false; + } + + DeserializationError error = deserializeJson(json, http.getString()); + http.end(); + if (error) { + Serial.println(error.c_str()); + return false; + } + return true; +} + +/// @brief Check if the given string is empty or contains "null" +/// +/// @param str String to check +/// @return True if empty or null, false if not +static inline bool isEmptyOrNull(const String &str) { + return str.isEmpty() || str == "null"; +} + +} // namespace util diff --git a/ESP32_AP-Flasher/include/web.h b/ESP32_AP-Flasher/include/web.h index bc7c6214..f360a5b3 100644 --- a/ESP32_AP-Flasher/include/web.h +++ b/ESP32_AP-Flasher/include/web.h @@ -9,9 +9,9 @@ void doJsonUpload(AsyncWebServerRequest *request); extern void networkProcess(void *parameter); void wsLog(String text); void wsErr(String text); -void wsSendTaginfo(uint8_t *mac, uint8_t syncMode); +void wsSendTaginfo(const uint8_t *mac, uint8_t syncMode); void wsSendSysteminfo(); -void wsSendAPitem(struct APlist* apitem); +void wsSendAPitem(struct APlist *apitem); void wsSerial(String text); uint8_t wsClientCount(); diff --git a/ESP32_AP-Flasher/src/contentmanager.cpp b/ESP32_AP-Flasher/src/contentmanager.cpp index 954717c5..9c615898 100644 --- a/ESP32_AP-Flasher/src/contentmanager.cpp +++ b/ESP32_AP-Flasher/src/contentmanager.cpp @@ -33,6 +33,7 @@ #include "settings.h" #include "tag_db.h" #include "truetype.h" +#include "util.h" #include "web.h" // https://csvjson.com/json_beautifier @@ -43,22 +44,22 @@ void contentRunner() { time_t now; time(&now); - for (int32_t c = 0; c < tagDB.size(); c++) { - tagRecord *taginfo = nullptr; - taginfo = tagDB.at(c); - + for (tagRecord *taginfo : tagDB) { if (taginfo->RSSI && (now >= taginfo->nextupdate || taginfo->wakeupReason == WAKEUP_REASON_GPIO || taginfo->wakeupReason == WAKEUP_REASON_NFC) && config.runStatus == RUNSTATUS_RUN && Storage.freeSpace() > 31000) { drawNew(taginfo->mac, (taginfo->wakeupReason == WAKEUP_REASON_GPIO), taginfo); taginfo->wakeupReason = 0; } if (taginfo->expectedNextCheckin > now - 10 && taginfo->expectedNextCheckin < now + 30 && taginfo->pendingIdle == 0 && taginfo->pending == false) { - uint16_t minutesUntilNextUpdate = 0; - minutesUntilNextUpdate = (taginfo->nextupdate - now) / 60; - if (minutesUntilNextUpdate > config.maxsleep) minutesUntilNextUpdate = config.maxsleep; + uint16_t minutesUntilNextUpdate = (taginfo->nextupdate - now) / 60; + if (minutesUntilNextUpdate > config.maxsleep) { + minutesUntilNextUpdate = config.maxsleep; + } if (minutesUntilNextUpdate > 1 && (wsClientCount() == 0 || config.stopsleep == 0)) { taginfo->pendingIdle = minutesUntilNextUpdate; - if (taginfo->isExternal == false) prepareIdleReq(taginfo->mac, minutesUntilNextUpdate); + if (taginfo->isExternal == false) { + prepareIdleReq(taginfo->mac, minutesUntilNextUpdate); + } } } @@ -67,23 +68,20 @@ void contentRunner() { } void checkVars() { - DynamicJsonDocument doc(500); - for (int32_t c = 0; c < tagDB.size(); c++) { - tagRecord *tag = nullptr; - tag = tagDB.at(c); + DynamicJsonDocument cfgobj(500); + for (tagRecord *tag : tagDB) { if (tag->contentMode == 19) { - deserializeJson(doc, tag->modeConfigJson); - JsonObject cfgobj = doc.as(); - if (cfgobj["filename"]) { - String jsonfile = cfgobj["filename"].as(); + deserializeJson(cfgobj, tag->modeConfigJson); + const String jsonfile = cfgobj["filename"].as(); + if (!util::isEmptyOrNull(jsonfile)) { File file = contentFS->open(jsonfile, "r"); if (file) { - size_t fileSize = file.size(); + const size_t fileSize = file.size(); std::unique_ptr fileContent(new char[fileSize + 1]); file.readBytes(fileContent.get(), fileSize); file.close(); fileContent[fileSize] = '\0'; - char *contentPtr = fileContent.get(); + const char *contentPtr = fileContent.get(); for (const auto &entry : varDB) { if (entry.second.changed && strstr(contentPtr, entry.first.c_str()) != nullptr) { Serial.println("updating " + jsonfile + " because of var " + entry.first.c_str()); @@ -105,7 +103,27 @@ void checkVars() { } } -void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { +/// @brief Draw a counter +/// @param mac Destination mac +/// @param buttonPressed Was the button pressed (true) or not (false) +/// @param taginfo Tag information +/// @param cfgobj Tag config as json object +/// @param filename Filename +/// @param imageParams Image parameters +/// @param nextupdate Next counter update +/// @param nextCheckin Next tag checkin +void drawCounter(const uint8_t mac[8], const bool buttonPressed, tagRecord *&taginfo, JsonObject &cfgobj, String &filename, imgParam &imageParams, const uint32_t nextupdate, const uint16_t nextCheckin) { + int32_t counter = cfgobj["counter"].as(); + if (buttonPressed) { + counter = 0; + } + drawNumber(filename, counter, (int32_t)cfgobj["thresholdred"], taginfo, imageParams); + taginfo->nextupdate = nextupdate; + updateTagImage(filename, mac, (buttonPressed ? 0 : nextCheckin), taginfo, imageParams); + cfgobj["counter"] = counter + 1; +} + +void drawNew(const uint8_t mac[8], const bool buttonPressed, tagRecord *&taginfo) { time_t now; time(&now); @@ -120,7 +138,8 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { WiFi.macAddress(wifimac); memset(&wifimac[6], 0, 2); - if ((taginfo->wakeupReason == WAKEUP_REASON_FIRSTBOOT || taginfo->wakeupReason == WAKEUP_REASON_WDT_RESET) && taginfo->contentMode == 0 && memcmp(mac, wifimac, 8) == 0) { + const bool isAp = memcmp(mac, wifimac, 8) == 0; + if ((taginfo->wakeupReason == WAKEUP_REASON_FIRSTBOOT || taginfo->wakeupReason == WAKEUP_REASON_WDT_RESET) && taginfo->contentMode == 0 && isAp) { taginfo->contentMode = 21; taginfo->nextupdate = 0; } @@ -129,14 +148,16 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { mac2hex(mac, hexmac); String filename = "/" + String(hexmac) + ".raw"; #ifdef YELLOW_IPS_AP - if (memcmp(mac, wifimac, 8) == 0) filename = "direct"; + if (isAp) { + filename = "direct"; + } #endif struct tm time_info; getLocalTime(&time_info); time_info.tm_hour = time_info.tm_min = time_info.tm_sec = 0; time_info.tm_mday++; - time_t midnight = mktime(&time_info); + const time_t midnight = mktime(&time_info); DynamicJsonDocument doc(500); deserializeJson(doc, taginfo->modeConfigJson); @@ -164,20 +185,25 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { switch (taginfo->contentMode) { case 0: // Image - - if (cfgobj["filename"].as() && cfgobj["filename"].as() != "null" && !cfgobj["#fetched"].as()) { - if (cfgobj["dither"] && cfgobj["dither"] == "1") imageParams.dither = true; - jpg2buffer(cfgobj["filename"].as(), filename, imageParams); - if (imageParams.hasRed) imageParams.dataType = DATATYPE_IMG_RAW_2BPP; + { + const String configFilename = cfgobj["filename"].as(); + if (!util::isEmptyOrNull(configFilename) && !cfgobj["#fetched"].as()) { + imageParams.dither = cfgobj["dither"] && cfgobj["dither"] == "1"; + jpg2buffer(configFilename, filename, imageParams); + 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") contentFS->remove("/" + cfgobj["filename"].as()); + if (cfgobj["delete"].as() == "1") { + contentFS->remove("/" + configFilename); + } } else { wsErr("Error accessing " + filename); } } taginfo->nextupdate = 3216153600; - break; + } break; case 1: // Today @@ -187,21 +213,11 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { break; case 2: // CountDays - - if (buttonPressed) cfgobj["counter"] = 0; - drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"], taginfo, imageParams); - taginfo->nextupdate = midnight; - updateTagImage(filename, mac, (buttonPressed ? 0 : 15), taginfo, imageParams); - cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1; + drawCounter(mac, buttonPressed, taginfo, cfgobj, filename, imageParams, midnight, 15); break; case 3: // CountHours - - if (buttonPressed) cfgobj["counter"] = 0; - drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"], taginfo, imageParams); - taginfo->nextupdate = now + 3600; - updateTagImage(filename, mac, (buttonPressed ? 0 : 5), taginfo, imageParams); - cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1; + drawCounter(mac, buttonPressed, taginfo, cfgobj, filename, imageParams, now + 3600, 5); break; case 4: // Weather @@ -226,7 +242,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { case 5: // Firmware filename = cfgobj["filename"].as(); - if (filename && filename != "null" && !cfgobj["#fetched"].as()) { + if (!util::isEmptyOrNull(filename) && !cfgobj["#fetched"].as()) { if (prepareDataAvail(&filename, DATATYPE_FW_UPDATE, mac, cfgobj["timetolive"].as())) { cfgobj["#fetched"] = true; } else { @@ -243,13 +259,14 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { case 7: // ImageUrl { - int httpcode = getImgURL(filename, cfgobj["url"], (time_t)cfgobj["#fetched"], imageParams, String(hexmac)); + const int httpcode = getImgURL(filename, cfgobj["url"], (time_t)cfgobj["#fetched"], imageParams, String(hexmac)); + const int interval = cfgobj["interval"].as(); if (httpcode == 200) { - taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 3 ? 15 : cfgobj["interval"].as()); - updateTagImage(filename, mac, cfgobj["interval"].as(), taginfo, imageParams); + taginfo->nextupdate = now + 60 * (interval < 3 ? 15 : interval); + updateTagImage(filename, mac, interval, taginfo, imageParams); cfgobj["#fetched"] = now; } else if (httpcode == 304) { - taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 3 ? 15 : cfgobj["interval"].as()); + taginfo->nextupdate = now + 60 * (interval < 3 ? 15 : interval); } else { taginfo->nextupdate = now + 300; } @@ -259,8 +276,9 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { case 9: // RSSFeed if (getRssFeed(filename, cfgobj["url"], cfgobj["title"], taginfo, imageParams)) { - taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 3 ? 60 : cfgobj["interval"].as()); - updateTagImage(filename, mac, cfgobj["interval"].as(), taginfo, imageParams); + const int interval = cfgobj["interval"].as(); + taginfo->nextupdate = now + 60 * (interval < 3 ? 60 : interval); + updateTagImage(filename, mac, interval, taginfo, imageParams); } else { taginfo->nextupdate = now + 300; } @@ -276,8 +294,9 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { case 11: // Calendar: if (getCalFeed(filename, cfgobj["apps_script_url"], cfgobj["title"], taginfo, imageParams)) { - taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 3 ? 15 : cfgobj["interval"].as()); - updateTagImage(filename, mac, cfgobj["interval"].as(), taginfo, imageParams); + const int interval = cfgobj["interval"].as(); + taginfo->nextupdate = now + 60 * (interval < 3 ? 15 : interval); + updateTagImage(filename, mac, interval, taginfo, imageParams); } else { taginfo->nextupdate = now + 300; } @@ -311,7 +330,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { case 16: // buienradar { - uint8_t refresh = drawBuienradar(filename, cfgobj, taginfo, imageParams); + const uint8_t refresh = drawBuienradar(filename, cfgobj, taginfo, imageParams); taginfo->nextupdate = now + refresh * 60; updateTagImage(filename, mac, refresh, taginfo, imageParams); break; @@ -333,22 +352,24 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { case 19: // json template { - if (cfgobj["filename"]) { - int result = getJsonTemplateFile(filename, cfgobj["filename"], taginfo, imageParams); + const String configFilename = cfgobj["filename"].as(); + if (!util::isEmptyOrNull(configFilename)) { + const int result = getJsonTemplateFile(filename, configFilename, taginfo, imageParams); if (result) { updateTagImage(filename, mac, cfgobj["interval"].as(), taginfo, imageParams); } else { - wsErr("error opening file " + cfgobj["filename"].as()); + wsErr("error opening file " + configFilename); } taginfo->nextupdate = 3216153600; } else { - int httpcode = getJsonTemplateUrl(filename, cfgobj["url"], (time_t)cfgobj["#fetched"], String(hexmac), taginfo, imageParams); + const int httpcode = getJsonTemplateUrl(filename, cfgobj["url"], (time_t)cfgobj["#fetched"], String(hexmac), taginfo, imageParams); + const int interval = cfgobj["interval"].as(); if (httpcode == 200) { - taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 3 ? 15 : cfgobj["interval"].as()); - updateTagImage(filename, mac, cfgobj["interval"].as(), taginfo, imageParams); + taginfo->nextupdate = now + 60 * (interval < 3 ? 15 : interval); + updateTagImage(filename, mac, interval, taginfo, imageParams); cfgobj["#fetched"] = now; } else if (httpcode == 304) { - taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 3 ? 15 : cfgobj["interval"].as()); + taginfo->nextupdate = now + 60 * (interval < 3 ? 15 : interval); } else { taginfo->nextupdate = now + 600; } @@ -369,7 +390,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { taginfo->modeConfigJson = doc.as(); } -bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin, tagRecord *&taginfo, imgParam &imageParams) { +bool updateTagImage(String &filename, const uint8_t *dst, uint16_t nextCheckin, tagRecord *&taginfo, imgParam &imageParams) { if (taginfo->hwType == SOLUM_SEG_UK) { sendAPSegmentedData(dst, (String)imageParams.segments, imageParams.symbols, imageParams.invert, (taginfo->isExternal == false)); } else { @@ -397,10 +418,11 @@ void replaceVariables(String &format) { while ((openBraceIndex = format.indexOf('{', startIndex)) != -1 && (closeBraceIndex = format.indexOf('}', openBraceIndex + 1)) != -1) { - std::string variableName = format.substring(openBraceIndex + 1, closeBraceIndex).c_str(); - std::string varKey = "{" + variableName + "}"; - if (varDB.count(variableName) > 0) { - format.replace(varKey.c_str(), varDB.at(variableName).value); + const std::string variableName = format.substring(openBraceIndex + 1, closeBraceIndex).c_str(); + const std::string varKey = "{" + variableName + "}"; + auto var = varDB.find(variableName); + if (var != varDB.end()) { + format.replace(varKey.c_str(), var->second.value); } startIndex = closeBraceIndex + 1; } @@ -469,7 +491,7 @@ void initSprite(TFT_eSprite &spr, int w, int h, imgParam &imageParams) { spr.setRotation(3); if (spr.getPointer() == nullptr) { wsErr("low on memory. Fallback to 1bpp"); - Serial.println("Maximum Continuous Heap Space: " + String(heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT))); + util::printLargestFreeBlock(); spr.setColorDepth(1); spr.setBitmapColor(TFT_WHITE, TFT_BLACK); imageParams.bufferbpp = 1; @@ -487,9 +509,9 @@ void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams) { struct tm timeinfo; localtime_r(&now, &timeinfo); - int weekday_number = timeinfo.tm_wday; - int month_number = timeinfo.tm_mon; - int year_number = timeinfo.tm_year + 1900; + // const int weekday_number = timeinfo.tm_wday; + const int month_number = timeinfo.tm_mon; + const int year_number = timeinfo.tm_year + 1900; if (taginfo->hwType == SOLUM_SEG_UK) { sprintf(imageParams.segments, "%2d%2d%-2.2s%04d", timeinfo.tm_mday, month_number + 1, languageDays[getCurrentLanguage()][timeinfo.tm_wday], year_number); @@ -504,13 +526,17 @@ void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams) { initSprite(spr, imageParams.width, imageParams.height, imageParams); - if (loc["date"]) { - drawString(spr, languageDays[getCurrentLanguage()][timeinfo.tm_wday], loc["weekday"][0], loc["weekday"][1], loc["weekday"][2], TC_DATUM, TFT_RED); - drawString(spr, String(timeinfo.tm_mday) + " " + languageMonth[getCurrentLanguage()][timeinfo.tm_mon], loc["date"][0], loc["date"][1], loc["date"][2], TC_DATUM); + const auto &date = loc["date"]; + const auto &weekday = loc["weekday"]; + if (date) { + drawString(spr, languageDays[getCurrentLanguage()][timeinfo.tm_wday], weekday[0], weekday[1], weekday[2], TC_DATUM, TFT_RED); + drawString(spr, String(timeinfo.tm_mday) + " " + languageMonth[getCurrentLanguage()][timeinfo.tm_mon], date[0], date[1], date[2], TC_DATUM); } else { - drawString(spr, languageDays[getCurrentLanguage()][timeinfo.tm_wday], loc["weekday"][0], loc["weekday"][1], loc["weekday"][2], TC_DATUM, TFT_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, TFT_RED); + const auto &month = loc["month"]; + const auto &day = loc["month"]; + drawString(spr, languageDays[getCurrentLanguage()][timeinfo.tm_wday], weekday[0], weekday[1], weekday[2], TC_DATUM, TFT_BLACK); + drawString(spr, String(languageMonth[getCurrentLanguage()][timeinfo.tm_mon]), month[0], month[1], month[2], TC_DATUM); + drawString(spr, String(timeinfo.tm_mday), day[0], day[1], day[2], TC_DATUM, TFT_RED); } spr2buffer(spr, filename, imageParams); @@ -561,96 +587,97 @@ void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord spr.deleteSprite(); } +/// @brief Get a weather icon +/// @param id Icon identifier/index +/// @param isNight Use night icons (true) or not (false) +/// @return String reference to icon +const String &getWeatherIcon(const uint8_t id, const bool isNight = false) { + static const 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 (isNight && id <= 3) { + static const String nightIcons[] = {"\uf02e", "\uf083", "\uf086"}; + return nightIcons[id]; + } + return weatherIcons[id]; +} + void drawWeather(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgParam &imageParams) { wsLog("get weather"); getLocation(cfgobj); - HTTPClient http; String lat = cfgobj["#lat"]; String lon = cfgobj["#lon"]; String tz = cfgobj["#tz"]; - http.begin("https://api.open-meteo.com/v1/forecast?latitude=" + lat + "&longitude=" + lon + "¤t_weather=true&windspeed_unit=ms&timezone=" + tz); - http.setTimeout(5000); - int httpCode = http.GET(); - if (httpCode == 200) { - StaticJsonDocument<1000> doc; - DeserializationError error = deserializeJson(doc, http.getString()); - if (error) { - Serial.println(error.c_str()); - } - - auto temperature = doc["current_weather"]["temperature"].as(); - auto windspeed = doc["current_weather"]["windspeed"].as(); - auto winddirection = doc["current_weather"]["winddirection"].as(); - uint8_t weathercode = doc["current_weather"]["weathercode"].as(); - uint8_t isday = doc["current_weather"]["is_day"].as(); - if (weathercode > 40) weathercode -= 40; - int wind = windSpeedToBeaufort(windspeed); - - doc.clear(); - - if (taginfo->hwType == SOLUM_SEG_UK) { - String weatherText[] = {"sun", "sun", "sun", "CLDY", "CLDY", "FOG", "", "", "FOG", "", "", - "DRZL", "", "DRZL", "", "DRZL", "ice", "ice", "", "", "", - "rain", "", "rain", "", "rain", "ice", "ice", "", "", "", - "SNOW", "", "SNOW", "", "SNOW", "", "SNOW", "", "", "rain", - "rain", "rain", "", "", "SNOW", "SNOW", "", "", "", "", - "", "", "", "", "STRM", "HAIL", "", "", "HAIL"}; - if (temperature < -9.9) { - sprintf(imageParams.segments, "%3d^%2d%-4.4s", static_cast(temperature), wind, weatherText[weathercode].c_str()); - imageParams.symbols = 0x00; - } else { - sprintf(imageParams.segments, "%3d^%2d%-4.4s", static_cast(temperature * 10), wind, weatherText[weathercode].c_str()); - imageParams.symbols = 0x04; - } - http.end(); - return; - } - - StaticJsonDocument<512> loc; - getTemplate(loc, 4, taginfo->hwType); - - 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"; - weatherIcons[2] = "\uf086"; - } - - TFT_eSprite spr = TFT_eSprite(&tft); - tft.setTextWrap(false, false); - - initSprite(spr, imageParams.width, imageParams.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 ? TFT_RED : TFT_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 ? TFT_RED : TFT_BLACK)); - - int iconcolor = TFT_BLACK; - if (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 96 || weathercode == 99) { - iconcolor = TFT_RED; - } - drawString(spr, weatherIcons[weathercode], loc["icon"][0], loc["icon"][1], "/fonts/weathericons.ttf", loc["icon"][3], iconcolor, loc["icon"][2]); - drawString(spr, windDirectionIcon(winddirection), loc["dir"][0], loc["dir"][1], "/fonts/weathericons.ttf", TC_DATUM, TFT_BLACK, loc["dir"][2]); - if (weathercode > 10) { - drawString(spr, "\uf084", loc["umbrella"][0], loc["umbrella"][1], "/fonts/weathericons.ttf", TC_DATUM, TFT_RED, loc["umbrella"][2]); - } - - spr2buffer(spr, filename, imageParams); - spr.deleteSprite(); - } else { - wsErr("OpenMeteo http " + httpCode); + StaticJsonDocument<1000> doc; + const bool success = util::httpGetJson("https://api.open-meteo.com/v1/forecast?latitude=" + lat + "&longitude=" + lon + "¤t_weather=true&windspeed_unit=ms&timezone=" + tz, doc, 5000); + if (!success) { + return; } - http.end(); + + const auto ¤tWeather = doc["current_weather"]; + const double temperature = currentWeather["temperature"].as(); + const int windspeed = currentWeather["windspeed"].as(); + const int winddirection = currentWeather["winddirection"].as(); + const uint8_t isday = currentWeather["is_day"].as(); + uint8_t weathercode = currentWeather["weathercode"].as(); + if (weathercode > 40) weathercode -= 40; + const int beaufort = windSpeedToBeaufort(windspeed); + + doc.clear(); + + if (taginfo->hwType == SOLUM_SEG_UK) { + static const String weatherText[] = {"sun", "sun", "sun", "CLDY", "CLDY", "FOG", "", "", "FOG", "", "", + "DRZL", "", "DRZL", "", "DRZL", "ice", "ice", "", "", "", + "rain", "", "rain", "", "rain", "ice", "ice", "", "", "", + "SNOW", "", "SNOW", "", "SNOW", "", "SNOW", "", "", "rain", + "rain", "rain", "", "", "SNOW", "SNOW", "", "", "", "", + "", "", "", "", "STRM", "HAIL", "", "", "HAIL"}; + if (temperature < -9.9) { + sprintf(imageParams.segments, "%3d^%2d%-4.4s", static_cast(temperature), beaufort, weatherText[weathercode].c_str()); + imageParams.symbols = 0x00; + } else { + sprintf(imageParams.segments, "%3d^%2d%-4.4s", static_cast(temperature * 10), beaufort, weatherText[weathercode].c_str()); + imageParams.symbols = 0x04; + } + return; + } + + getTemplate(doc, 4, taginfo->hwType); + + TFT_eSprite spr = TFT_eSprite(&tft); + tft.setTextWrap(false, false); + + initSprite(spr, imageParams.width, imageParams.height, imageParams); + const auto &location = doc["location"]; + drawString(spr, cfgobj["location"], location[0], location[1], location[2]); + const auto &wind = doc["wind"]; + drawString(spr, String(beaufort), wind[0], wind[1], wind[2], TR_DATUM, (beaufort > 4 ? TFT_RED : TFT_BLACK)); + + char tmpOutput[5]; + dtostrf(temperature, 2, 1, tmpOutput); + const auto &temp = doc["temp"]; + drawString(spr, String(tmpOutput), temp[0], temp[1], temp[2], TL_DATUM, (temperature < 0 ? TFT_RED : TFT_BLACK)); + + const int iconcolor = (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 96 || weathercode == 99) + ? TFT_RED + : TFT_BLACK; + const auto &icon = doc["icon"]; + drawString(spr, getWeatherIcon(weathercode, isday == 0), icon[0], icon[1], "/fonts/weathericons.ttf", icon[3], iconcolor, icon[2]); + const auto &dir = doc["dir"]; + drawString(spr, windDirectionIcon(winddirection), dir[0], dir[1], "/fonts/weathericons.ttf", TC_DATUM, TFT_BLACK, dir[2]); + if (weathercode > 10) { + const auto &umbrella = doc["umbrella"]; + drawString(spr, "\uf084", umbrella[0], umbrella[1], "/fonts/weathericons.ttf", TC_DATUM, TFT_RED, umbrella[2]); + } + + spr2buffer(spr, filename, imageParams); + spr.deleteSprite(); } void drawForecast(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgParam &imageParams) { @@ -658,85 +685,71 @@ void drawForecast(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, img wsLog("get weather"); getLocation(cfgobj); - HTTPClient http; String lat = cfgobj["#lat"]; String lon = cfgobj["#lon"]; String tz = cfgobj["#tz"]; - http.begin("https://api.open-meteo.com/v1/forecast?latitude=" + lat + "&longitude=" + lon + "&daily=weathercode,temperature_2m_max,temperature_2m_min,precipitation_sum,windspeed_10m_max,winddirection_10m_dominant&windspeed_unit=ms&timeformat=unixtime&timezone=" + tz); - - http.setTimeout(5000); - int httpCode = http.GET(); - - if (httpCode == 200) { - DynamicJsonDocument doc(2000); - DeserializationError error = deserializeJson(doc, http.getString()); - if (error) { - 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"}; - - tft.setTextWrap(false, false); - - StaticJsonDocument<512> loc; - getTemplate(loc, 8, taginfo->hwType); - initSprite(spr, imageParams.width, imageParams.height, imageParams); - - drawString(spr, cfgobj["location"], loc["location"][0], loc["location"][1], loc["location"][2], TL_DATUM, TFT_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, TFT_BLACK); - - uint8_t weathercode = doc["daily"]["weathercode"][dag].as(); - if (weathercode > 40) weathercode -= 40; - - int iconcolor = TFT_BLACK; - if (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 96 || weathercode == 99) { - iconcolor = TFT_RED; - } - drawString(spr, weatherIcons[weathercode], loc["icon"][0].as() + dag * loc["column"][1].as(), loc["icon"][1], "/fonts/weathericons.ttf", TC_DATUM, iconcolor, loc["icon"][2]); - - drawString(spr, windDirectionIcon(doc["daily"]["winddirection_10m_dominant"][dag]), loc["wind"][0].as() + dag * loc["column"][1].as(), loc["wind"][1], "/fonts/weathericons.ttf", TC_DATUM, TFT_BLACK, loc["icon"][2]); - - 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], *contentFS); - - if (loc["rain"]) { - int8_t rain = round(doc["daily"]["precipitation_sum"][dag].as()); - if (rain > 0) { - drawString(spr, String(rain) + "mm", dag * loc["column"][1].as() + loc["rain"][0].as(), loc["rain"][1], "", TC_DATUM, (rain > 10 ? TFT_RED : TFT_BLACK)); - } - } - - drawString(spr, String(tmin) + " ", dag * loc["column"][1].as() + loc["day"][0].as(), loc["day"][4], "", TR_DATUM, (tmin < 0 ? TFT_RED : TFT_BLACK)); - drawString(spr, String(" ") + String(tmax), dag * loc["column"][1].as() + loc["day"][0].as(), loc["day"][4], "", TL_DATUM, (tmax < 0 ? TFT_RED : TFT_BLACK)); - drawString(spr, String(" ") + String(wind), dag * loc["column"][1].as() + loc["day"][0].as(), loc["day"][3], "", TL_DATUM, (wind > 5 ? TFT_RED : TFT_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, TFT_BLACK); - } - } - } - - spr2buffer(spr, filename, imageParams); - spr.deleteSprite(); - } else { - wsErr("OpenMeteo http " + httpCode); + DynamicJsonDocument doc(2000); + const bool success = util::httpGetJson("https://api.open-meteo.com/v1/forecast?latitude=" + lat + "&longitude=" + lon + "&daily=weathercode,temperature_2m_max,temperature_2m_min,precipitation_sum,windspeed_10m_max,winddirection_10m_dominant&windspeed_unit=ms&timeformat=unixtime&timezone=" + tz, doc, 5000); + if (!success) { + return; } - http.end(); + + tft.setTextWrap(false, false); + + StaticJsonDocument<512> loc; + getTemplate(loc, 8, taginfo->hwType); + initSprite(spr, imageParams.width, imageParams.height, imageParams); + + const auto &location = loc["location"]; + drawString(spr, cfgobj["location"], location[0], location[1], location[2], TL_DATUM, TFT_BLACK); + const auto &daily = doc["daily"]; + const auto &column = loc["column"]; + const int column1 = column[1].as(); + const auto &day = loc["day"]; + for (uint8_t dag = 0; dag < column[0]; dag++) { + const time_t weatherday = daily["time"][dag].as(); + const struct tm *datum = localtime(&weatherday); + + drawString(spr, String(languageDaysShort[getCurrentLanguage()][datum->tm_wday]), dag * column1 + day[0].as(), day[1], day[2], TC_DATUM, TFT_BLACK); + + uint8_t weathercode = daily["weathercode"][dag].as(); + if (weathercode > 40) weathercode -= 40; + + const int iconcolor = (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 96 || weathercode == 99) + ? TFT_RED + : TFT_BLACK; + drawString(spr, getWeatherIcon(weathercode), loc["icon"][0].as() + dag * column1, loc["icon"][1], "/fonts/weathericons.ttf", TC_DATUM, iconcolor, loc["icon"][2]); + + drawString(spr, windDirectionIcon(daily["winddirection_10m_dominant"][dag]), loc["wind"][0].as() + dag * column1, loc["wind"][1], "/fonts/weathericons.ttf", TC_DATUM, TFT_BLACK, loc["icon"][2]); + + const int8_t tmin = round(daily["temperature_2m_min"][dag].as()); + const int8_t tmax = round(daily["temperature_2m_max"][dag].as()); + const uint8_t wind = windSpeedToBeaufort(daily["windspeed_10m_max"][dag].as()); + + spr.loadFont(day[2], *contentFS); + + if (loc["rain"]) { + const int8_t rain = round(daily["precipitation_sum"][dag].as()); + if (rain > 0) { + drawString(spr, String(rain) + "mm", dag * column1 + loc["rain"][0].as(), loc["rain"][1], "", TC_DATUM, (rain > 10 ? TFT_RED : TFT_BLACK)); + } + } + + drawString(spr, String(tmin) + " ", dag * column1 + day[0].as(), day[4], "", TR_DATUM, (tmin < 0 ? TFT_RED : TFT_BLACK)); + drawString(spr, String(" ") + String(tmax), dag * column1 + day[0].as(), day[4], "", TL_DATUM, (tmax < 0 ? TFT_RED : TFT_BLACK)); + drawString(spr, String(" ") + String(wind), dag * column1 + day[0].as(), day[3], "", TL_DATUM, (wind > 5 ? TFT_RED : TFT_BLACK)); + spr.unloadFont(); + if (dag > 0) { + for (int i = loc["line"][0]; i < loc["line"][1]; i += 3) { + spr.drawPixel(dag * column1, i, TFT_BLACK); + } + } + } + + spr2buffer(spr, filename, imageParams); + spr.deleteSprite(); } int getImgURL(String &filename, String URL, time_t fetched, imgParam &imageParams, String MAC) { @@ -749,7 +762,7 @@ int getImgURL(String &filename, String URL, time_t fetched, imgParam &imageParam http.addHeader("If-Modified-Since", formatHttpDate(fetched)); http.addHeader("X-ESL-MAC", MAC); http.setTimeout(5000); // timeout in ms - int httpCode = http.GET(); + const int httpCode = http.GET(); if (httpCode == 200) { File f = contentFS->open("/temp/temp.jpg", "w"); if (f) { @@ -789,7 +802,7 @@ bool getRssFeed(String &filename, String URL, String title, tagRecord *&taginfo, getTemplate(loc, 9, taginfo->hwType); initSprite(spr, imageParams.width, imageParams.height, imageParams); - if (title == "" || title == "null") title = "RSS feed"; + if (util::isEmptyOrNull(title)) title = "RSS feed"; drawString(spr, title, loc["title"][0], loc["title"][1], loc["title"][2], TL_DATUM, TFT_BLACK); setU8G2Font(loc["font"], u8f); @@ -870,7 +883,7 @@ bool getCalFeed(String &filename, String URL, String title, tagRecord *&taginfo, getTemplate(loc, 11, taginfo->hwType); initSprite(spr, imageParams.width, imageParams.height, imageParams); - if (title == "" || title == "null") title = "Calendar"; + if (util::isEmptyOrNull(title)) title = "Calendar"; drawString(spr, title, loc["title"][0], loc["title"][1], loc["title"][2], TL_DATUM, TFT_BLACK); drawString(spr, dateString, loc["date"][0], loc["date"][1], loc["title"][2], TR_DATUM, TFT_BLACK); @@ -879,10 +892,10 @@ bool getCalFeed(String &filename, String URL, String title, tagRecord *&taginfo, 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"]; - time_t starttime = obj["start"]; - time_t endtime = obj["end"]; + const JsonObject &obj = doc[i]; + const String eventtitle = obj["title"]; + const time_t starttime = obj["start"]; + const time_t endtime = obj["end"]; setU8G2Font(loc["line"][3], u8f); if (starttime <= now && endtime > now) { u8f.setForegroundColor(TFT_WHITE); @@ -914,17 +927,16 @@ void drawQR(String &filename, String qrcontent, String title, tagRecord *&taginf uint8_t qrcodeData[qrcode_getBufferSize(2)]; // https://github.com/ricmoo/QRCode qrcode_initText(&qrcode, qrcodeData, 2, ECC_MEDIUM, text); - int size = qrcode.size; - int xpos = 0, ypos = 0, dotsize = 1; StaticJsonDocument<512> loc; getTemplate(loc, 10, taginfo->hwType); initSprite(spr, imageParams.width, imageParams.height, imageParams); drawString(spr, title, loc["title"][0], loc["title"][1], loc["title"][2]); - dotsize = int((imageParams.height - loc["pos"][1].as()) / size); - xpos = loc["pos"][0].as() - dotsize * size / 2; - ypos = loc["pos"][1]; + const int size = qrcode.size; + const int dotsize = int((imageParams.height - loc["pos"][1].as()) / size); + const int xpos = loc["pos"][0].as() - dotsize * size / 2; + const int ypos = loc["pos"][1]; for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { @@ -978,20 +990,37 @@ uint8_t drawBuienradar(String &filename, JsonObject &cfgobj, tagRecord *&taginfo drawString(spr, "Buienradar", loc["title"][0], loc["title"][1], loc["title"][2]); + const auto &bars = loc["bars"]; + const auto &cols = loc["cols"]; + const int cols0 = cols[0].as(); + const int cols1 = cols[1].as(); + const int cols2 = cols[2].as(); + const String cols3 = cols[3].as(); + const int bars0 = bars[0].as(); + const int bars1 = bars[1].as(); + const int bars2 = bars[2].as(); for (int i = 0; i < 24; i++) { - int startPos = i * 11; + const 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 < 70) value = 70; - if (value > 180) value = 180; - if (value > 70 && i < 12) refresh = 5; - if (value > 70 && refresh > 5) refresh = 15; + const String timestring = response.substring(startPos + 4, startPos + 9); + const int minutes = timestring.substring(3).toInt(); + if (value < 70) { + value = 70; + } else if (value > 180) { + value = 180; + } + if (value > 70) { + if (i < 12) { + refresh = 5; + } else if (refresh > 5) { + refresh = 15; + } + } - spr.fillRect(i * loc["cols"][2].as() + loc["bars"][0].as(), loc["bars"][1].as() - (value - 70), loc["bars"][2], (value - 70), (value > 130 ? TFT_RED : TFT_BLACK)); + spr.fillRect(i * cols2 + bars0, bars1 - (value - 70), bars2, (value - 70), (value > 130 ? TFT_RED : TFT_BLACK)); if (minutes % 15 == 0) { - drawString(spr, timestring, i * loc["cols"][2].as() + loc["cols"][0].as(), loc["cols"][1], loc["cols"][3]); + drawString(spr, timestring, i * cols2 + cols0, cols1, cols3); } } @@ -1017,8 +1046,8 @@ void drawAPinfo(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgPa getTemplate(loc, 21, taginfo->hwType); initSprite(spr, imageParams.width, imageParams.height, imageParams); - JsonArray jsonArray = loc.as(); - for (JsonVariant elem : jsonArray) { + const JsonArray jsonArray = loc.as(); + for (const JsonVariant &elem : jsonArray) { drawElement(elem, spr); } @@ -1048,7 +1077,7 @@ int getJsonTemplateUrl(String &filename, String URL, time_t fetched, String MAC, http.addHeader("X-ESL-MAC", MAC); http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); http.setTimeout(5000); - int httpCode = http.GET(); + const int httpCode = http.GET(); if (httpCode == 200) { drawJsonStream(http.getStream(), filename, taginfo, imageParams); } else { @@ -1064,7 +1093,6 @@ void drawJsonStream(Stream &stream, String &filename, tagRecord *&taginfo, imgPa TFT_eSprite spr = TFT_eSprite(&tft); initSprite(spr, imageParams.width, imageParams.height, imageParams); DynamicJsonDocument doc(300); - if (stream.find("[")) { do { DeserializationError error = deserializeJson(doc, stream); @@ -1085,11 +1113,10 @@ void drawJsonStream(Stream &stream, String &filename, tagRecord *&taginfo, imgPa void drawElement(const JsonObject &element, TFT_eSprite &spr) { if (element.containsKey("text")) { const JsonArray &textArray = element["text"]; - uint16_t align = textArray[5] | 0; - uint16_t size = textArray[6] | 0; - String bgcolorstr = textArray[7].as(); - - uint16_t bgcolor = (bgcolorstr.length() > 0) ? getColor(bgcolorstr) : TFT_WHITE; + const uint16_t align = textArray[5] | 0; + const uint16_t size = textArray[6] | 0; + const String bgcolorstr = textArray[7].as(); + const uint16_t bgcolor = (bgcolorstr.length() > 0) ? getColor(bgcolorstr) : TFT_WHITE; drawString(spr, textArray[2], textArray[0].as(), textArray[1].as(), textArray[3], align, getColor(textArray[4]), size, bgcolor); } else if (element.containsKey("box")) { const JsonArray &boxArray = element["box"]; @@ -1103,10 +1130,10 @@ void drawElement(const JsonObject &element, TFT_eSprite &spr) { } } -uint16_t getColor(String color) { - if (color == "0" or color == "white") return TFT_WHITE; - if (color == "1" or color == "" or color == "black") return TFT_BLACK; - if (color == "2" or color == "red") return TFT_RED; +uint16_t getColor(const String &color) { + if (color == "0" || color == "white") return TFT_WHITE; + if (color == "1" || color == "" || color == "black") return TFT_BLACK; + if (color == "2" || color == "red") return TFT_RED; uint16_t r, g, b; if (color.length() == 7 && color[0] == '#' && sscanf(color.c_str(), "#%2hx%2hx%2hx", &r, &g, &b) == 3) { @@ -1118,15 +1145,15 @@ uint16_t getColor(String color) { char *formatHttpDate(time_t t) { static char buf[40]; struct tm *timeinfo; - timeinfo = localtime(&t); // Get the local time - time_t utcTime = mktime(timeinfo); // Convert to UTC + timeinfo = localtime(&t); // Get the local time + const time_t utcTime = mktime(timeinfo); // Convert to UTC timeinfo = gmtime(&utcTime); strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", timeinfo); return buf; } String urlEncode(const char *msg) { - const char *hex = "0123456789ABCDEF"; + static const char *hex = "0123456789ABCDEF"; String encodedMsg = ""; while (*msg != '\0') { @@ -1143,10 +1170,11 @@ String urlEncode(const char *msg) { return encodedMsg; } -int windSpeedToBeaufort(float windSpeed) { +int windSpeedToBeaufort(const float windSpeed) { + constexpr static const float speeds[] = {0.3, 1.5, 3.3, 5.5, 8, 10.8, 13.9, 17.2, 20.8, 24.5, 28.5, 32.7}; + constexpr static const int numSpeeds = sizeof(speeds) / sizeof(speeds[0]); int beaufort = 0; - float speeds[] = {0.3, 1.5, 3.3, 5.5, 8, 10.8, 13.9, 17.2, 20.8, 24.5, 28.5, 32.7}; - for (int i = 0; i < 12; i++) { + for (int i = 0; i < numSpeeds; i++) { if (windSpeed >= speeds[i]) { beaufort = i + 1; } @@ -1154,8 +1182,8 @@ int windSpeedToBeaufort(float windSpeed) { return beaufort; } -String windDirectionIcon(int degrees) { - String directions[] = {"\uf044", "\uf043", "\uf048", "\uf087", "\uf058", "\uf057", "\uf04d", "\uf088"}; +String windDirectionIcon(const int degrees) { + static const String directions[] = {"\uf044", "\uf043", "\uf048", "\uf087", "\uf058", "\uf057", "\uf04d", "\uf088"}; int index = (degrees + 22) / 45; if (index >= 8) { index = 0; @@ -1164,34 +1192,21 @@ String windDirectionIcon(int degrees) { } void getLocation(JsonObject &cfgobj) { - HTTPClient http; - StaticJsonDocument<1000> doc; + const String lat = cfgobj["#lat"]; + const String lon = cfgobj["#lon"]; - String lat = cfgobj["#lat"]; - String lon = cfgobj["#lon"]; - String tz = cfgobj["#tz"]; - - if (lat == "null" || lon == "null") { - http.begin("https://geocoding-api.open-meteo.com/v1/search?name=" + urlEncode(cfgobj["location"]) + "&count=1"); - http.setTimeout(5000); - int httpCode = http.GET(); - - if (httpCode == 200) { - DeserializationError error = deserializeJson(doc, http.getStream()); - http.end(); - lat = doc["results"][0]["latitude"].as(); - lon = doc["results"][0]["longitude"].as(); - tz = doc["results"][0]["timezone"].as(); - cfgobj["#lat"] = lat; - cfgobj["#lon"] = lon; - cfgobj["#tz"] = tz; - } else { - wsErr("getLocation http " + httpCode); + if (util::isEmptyOrNull(lat) || util::isEmptyOrNull(lon)) { + wsLog("get location"); + StaticJsonDocument<1000> doc; + if (util::httpGetJson("https://geocoding-api.open-meteo.com/v1/search?name=" + urlEncode(cfgobj["location"]) + "&count=1", doc, 5000)) { + cfgobj["#lat"] = doc["results"][0]["latitude"].as(); + cfgobj["#lon"] = doc["results"][0]["longitude"].as(); + cfgobj["#tz"] = doc["results"][0]["timezone"].as(); } } } -void prepareNFCReq(uint8_t *dst, const char *url) { +void prepareNFCReq(const uint8_t *dst, const char *url) { uint8_t *data; size_t len = strlen(url); data = new uint8_t[len + 8]; @@ -1213,7 +1228,7 @@ void prepareNFCReq(uint8_t *dst, const char *url) { prepareDataAvail(data, len, DATATYPE_NFC_RAW_CONTENT, dst); } -void prepareLUTreq(uint8_t *dst, String input) { +void prepareLUTreq(const uint8_t *dst, const String &input) { const char *delimiters = ", \t"; const int maxValues = 76; uint8_t waveform[maxValues]; @@ -1223,11 +1238,11 @@ void prepareLUTreq(uint8_t *dst, String input) { waveform[i++] = static_cast(strtol(ptr, nullptr, 16)); ptr = strtok(nullptr, delimiters); } - size_t waveformLen = sizeof(waveform); + const size_t waveformLen = sizeof(waveform); prepareDataAvail(waveform, waveformLen, DATATYPE_CUSTOM_LUT_OTA, dst); } -void prepareConfigFile(uint8_t *dst, JsonObject config) { +void prepareConfigFile(const uint8_t *dst, const JsonObject &config) { struct tagsettings tagSettings; tagSettings.settingsVer = 1; tagSettings.enableFastBoot = config["fastboot"].as(); @@ -1244,7 +1259,7 @@ void prepareConfigFile(uint8_t *dst, JsonObject config) { prepareDataAvail((uint8_t *)&tagSettings, sizeof(tagSettings), 0xA8, dst); } -void getTemplate(JsonDocument &json, uint8_t id, uint8_t hwtype) { +void getTemplate(JsonDocument &json, const uint8_t id, const uint8_t hwtype) { StaticJsonDocument<80> filter; StaticJsonDocument<2048> doc; @@ -1258,7 +1273,7 @@ void getTemplate(JsonDocument &json, uint8_t id, uint8_t hwtype) { if (jsonFile) { filter[templateKey][idstr] = true; filter["usetemplate"] = true; - DeserializationError error = deserializeJson(doc, jsonFile, DeserializationOption::Filter(filter)); + const DeserializationError error = deserializeJson(doc, jsonFile, DeserializationOption::Filter(filter)); jsonFile.close(); if (!error && doc.containsKey(templateKey) && doc[templateKey].containsKey(idstr)) { json.set(doc[templateKey][idstr]); @@ -1276,7 +1291,11 @@ void getTemplate(JsonDocument &json, uint8_t id, uint8_t 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); + if (title == "glasstown_nbp_tf") { + u8f.setFont(u8g2_font_glasstown_nbp_tf); + } else if (title == "7x14_tf") { + u8f.setFont(u8g2_font_7x14_tf); + } else if (title == "t0_14b_tf") { + u8f.setFont(u8g2_font_t0_14b_tf); + } } diff --git a/ESP32_AP-Flasher/src/main.cpp b/ESP32_AP-Flasher/src/main.cpp index 22520bc3..3197c1a9 100644 --- a/ESP32_AP-Flasher/src/main.cpp +++ b/ESP32_AP-Flasher/src/main.cpp @@ -3,11 +3,11 @@ #include #include -#include "storage.h" #include "contentmanager.h" #include "flasher.h" #include "serialap.h" #include "settings.h" +#include "storage.h" #include "system.h" #include "tag_db.h" @@ -18,6 +18,7 @@ #include "language.h" #include "leds.h" #include "udp.h" +#include "util.h" #include "web.h" void pinTest(); @@ -34,7 +35,7 @@ void delayedStart(void* parameter) { void timeTask(void* parameter) { wsSendSysteminfo(); - Serial.printf("Free heap: %.2f kB\n", ESP.getFreeHeap() / 1024.0f); + util::printHeap(); while (1) { time_t now; time(&now); @@ -53,10 +54,10 @@ void timeTask(void* parameter) { void setup() { Serial.begin(115200); Serial.print(">\n"); - #ifdef YELLOW_IPS_AP - extern void yellow_ap_display_init(void); - yellow_ap_display_init(); - #endif +#ifdef YELLOW_IPS_AP + extern void yellow_ap_display_init(void); + yellow_ap_display_init(); +#endif xTaskCreate(ledTask, "ledhandler", 2000, NULL, 2, NULL); vTaskDelay(10 / portTICK_PERIOD_MS); @@ -160,13 +161,13 @@ void loop() { while (1) { // pinTest(); while (1) { - #ifdef YELLOW_IPS_AP - extern void yellow_ap_display_loop(void); - yellow_ap_display_loop(); - #else - vTaskDelay(10000 / portTICK_PERIOD_MS); - // pinTest(); - #endif +#ifdef YELLOW_IPS_AP + extern void yellow_ap_display_loop(void); + yellow_ap_display_loop(); +#else + vTaskDelay(10000 / portTICK_PERIOD_MS); + // pinTest(); +#endif } #ifdef OPENEPAPERLINK_PCB if (extTagConnected()) { diff --git a/ESP32_AP-Flasher/src/makeimage.cpp b/ESP32_AP-Flasher/src/makeimage.cpp index d0cdf908..e7692f33 100644 --- a/ESP32_AP-Flasher/src/makeimage.cpp +++ b/ESP32_AP-Flasher/src/makeimage.cpp @@ -5,8 +5,9 @@ #include #include -#include "storage.h" #include "leds.h" +#include "storage.h" +#include "util.h" TFT_eSPI tft = TFT_eSPI(); TFT_eSprite spr = TFT_eSprite(&tft); @@ -40,7 +41,7 @@ void jpg2buffer(String filein, String fileout, imgParam &imageParams) { spr.createSprite(w, h); if (spr.getPointer() == nullptr) { wsErr("low on memory. Fallback to 1bpp"); - Serial.println("Maximum Continuous Heap Space: " + String(heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT))); + util::printLargestFreeBlock(); spr.setColorDepth(1); spr.setBitmapColor(TFT_WHITE, TFT_BLACK); imageParams.bufferbpp = 1; @@ -229,7 +230,7 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) { #endif if (!buffer) { Serial.println("Failed to allocate buffer"); - Serial.println("Maximum Continuous Heap Space: " + String(heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT))); + util::printLargestFreeBlock(); return; } spr2color(spr, imageParams, buffer, buffer_size, false); diff --git a/ESP32_AP-Flasher/src/newproto.cpp b/ESP32_AP-Flasher/src/newproto.cpp index ea65856b..9dcc4e1a 100644 --- a/ESP32_AP-Flasher/src/newproto.cpp +++ b/ESP32_AP-Flasher/src/newproto.cpp @@ -3,14 +3,13 @@ #include #include #include -#include "storage.h" #include #include -#include "storage.h" #include "commstructs.h" #include "serialap.h" #include "settings.h" +#include "storage.h" #include "system.h" #include "tag_db.h" #include "udp.h" @@ -47,7 +46,7 @@ uint8_t* getDataForFile(fs::File* file) { return ret; } -void prepareCancelPending(uint8_t dst[8]) { +void prepareCancelPending(const uint8_t dst[8]) { struct pendingData pending = {0}; memcpy(pending.targetMac, dst, 8); sendCancelPending(&pending); @@ -63,7 +62,7 @@ void prepareCancelPending(uint8_t dst[8]) { wsSendTaginfo(dst, SYNC_TAGSTATUS); } -void prepareIdleReq(uint8_t* dst, uint16_t nextCheckin) { +void prepareIdleReq(const uint8_t* dst, uint16_t nextCheckin) { if (nextCheckin > config.maxsleep) nextCheckin = config.maxsleep; if (nextCheckin > 0) { struct pendingData pending = {0}; @@ -79,9 +78,8 @@ void prepareIdleReq(uint8_t* dst, uint16_t nextCheckin) { } } -void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, uint8_t* dst) { - tagRecord* taginfo = nullptr; - taginfo = tagRecord::findByMAC(dst); +void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, const uint8_t* dst) { + tagRecord* taginfo = tagRecord::findByMAC(dst); if (taginfo == nullptr) { wsErr("Tag not found, this shouldn't happen."); return; @@ -117,9 +115,9 @@ void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, uint8_t* ds wsSendTaginfo(dst, SYNC_TAGSTATUS); } -bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t nextCheckin) { +bool prepareDataAvail(String* filename, uint8_t dataType, const uint8_t* dst, uint16_t nextCheckin) { if (nextCheckin > config.maxsleep) nextCheckin = config.maxsleep; - if (wsClientCount() && config.stopsleep == 1) nextCheckin=0; + if (wsClientCount() && config.stopsleep == 1) nextCheckin = 0; #ifdef YELLOW_IPS_AP if (*filename == "direct") { char dst_path[64]; @@ -129,7 +127,7 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t } #endif - tagRecord* taginfo = nullptr; + tagRecord* taginfo = nullptr; taginfo = tagRecord::findByMAC(dst); if (taginfo == nullptr) { wsErr("Tag not found, this shouldn't happen."); @@ -312,7 +310,7 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) { taginfo->dataType = pending->availdatainfo.dataType; taginfo->pending = true; taginfo->len = len; - } + } } http.end(); break; @@ -489,9 +487,12 @@ void processDataReq(struct espAvailDataReq* eadr, bool local) { if (local) { const char* reason = ""; - if (eadr->adr.wakeupReason == WAKEUP_REASON_FIRSTBOOT) reason = "Booting"; - else if (eadr->adr.wakeupReason == WAKEUP_REASON_NETWORK_SCAN) reason = "Network scan"; - else if (eadr->adr.wakeupReason == WAKEUP_REASON_WDT_RESET) reason = "Watchdog reset"; + if (eadr->adr.wakeupReason == WAKEUP_REASON_FIRSTBOOT) + reason = "Booting"; + else if (eadr->adr.wakeupReason == WAKEUP_REASON_NETWORK_SCAN) + reason = "Network scan"; + else if (eadr->adr.wakeupReason == WAKEUP_REASON_WDT_RESET) + reason = "Watchdog reset"; sprintf(buffer, "%02X%02X%02X%02X%02X%02X%02X%02X %s", eadr->src[7], eadr->src[6], eadr->src[5], eadr->src[4], eadr->src[3], eadr->src[2], eadr->src[1], eadr->src[0], reason); logLine(buffer); } @@ -539,7 +540,7 @@ void refreshAllPending() { } }; -void updateContent(uint8_t* dst) { +void updateContent(const uint8_t* dst) { tagRecord* taginfo = nullptr; taginfo = tagRecord::findByMAC(dst); if (taginfo != nullptr) { @@ -564,7 +565,7 @@ void setAPchannel() { } } -bool sendAPSegmentedData(uint8_t* dst, String data, uint16_t icons, bool inverted, bool local) { +bool sendAPSegmentedData(const uint8_t* dst, String data, uint16_t icons, bool inverted, bool local) { struct pendingData pending = {0}; memcpy(pending.targetMac, dst, 8); pending.availdatainfo.dataType = DATATYPE_UK_SEGMENTED; @@ -584,7 +585,7 @@ bool sendAPSegmentedData(uint8_t* dst, String data, uint16_t icons, bool inverte } } -bool showAPSegmentedInfo(uint8_t* dst, bool local) { +bool showAPSegmentedInfo(const uint8_t* dst, bool local) { struct pendingData pending = {0}; memcpy(pending.targetMac, dst, 8); pending.availdatainfo.dataType = DATATYPE_UK_SEGMENTED; @@ -604,7 +605,7 @@ bool showAPSegmentedInfo(uint8_t* dst, bool local) { } } -bool sendTagCommand(uint8_t* dst, uint8_t cmd, bool local) { +bool sendTagCommand(const uint8_t* dst, uint8_t cmd, bool local) { struct pendingData pending = {0}; memcpy(pending.targetMac, dst, 8); pending.availdatainfo.dataType = DATATYPE_COMMAND_DATA; @@ -680,7 +681,6 @@ bool checkMirror(struct tagRecord* taginfo, struct pendingData* pending) { JsonObject cfgobj = doc.as(); uint8_t mac[8] = {0}; if (hex2mac(cfgobj["mac"], mac) && memcmp(mac, taginfo->mac, sizeof(mac)) == 0) { - if (taginfo->data == nullptr) { fs::File file = contentFS->open(taginfo->filename); if (!file) { @@ -694,7 +694,7 @@ bool checkMirror(struct tagRecord* taginfo, struct pendingData* pending) { taginfo2->expectedNextCheckin = taginfo->expectedNextCheckin; taginfo2->filename = taginfo->filename; taginfo2->len = taginfo->len; - taginfo2->data = taginfo->data; // copy buffer pointer + taginfo2->data = taginfo->data; // copy buffer pointer taginfo2->dataType = taginfo->dataType; taginfo2->pending = true; taginfo2->nextupdate = 3216153600; diff --git a/ESP32_AP-Flasher/src/ota.cpp b/ESP32_AP-Flasher/src/ota.cpp index aad99f86..c3aff0b8 100644 --- a/ESP32_AP-Flasher/src/ota.cpp +++ b/ESP32_AP-Flasher/src/ota.cpp @@ -9,6 +9,7 @@ #include "storage.h" #include "tag_db.h" +#include "util.h" #include "web.h" #ifndef BUILD_ENV_NAME @@ -176,13 +177,8 @@ void firmwareUpdateTask(void* parameter) { vTaskDelete(NULL); } -inline void printHeap() { - const uint32_t freeStack = uxTaskGetStackHighWaterMark(NULL); - Serial.printf("Free heap: %d allocatable: %d stack: %d\n", ESP.getFreeHeap(), ESP.getMaxAllocHeap(), freeStack); -} - void updateFirmware(const char* url, const char* expectedMd5, const size_t size) { - printHeap(); + util::printHeap(); config.runStatus = RUNSTATUS_STOP; vTaskDelay(3000 / portTICK_PERIOD_MS); @@ -197,9 +193,9 @@ void updateFirmware(const char* url, const char* expectedMd5, const size_t size) httpClient.begin(url); httpClient.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS); - printHeap(); + util::printHeap(); const int httpCode = httpClient.GET(); - printHeap(); + util::printHeap(); if (httpCode == HTTP_CODE_OK) { if (Update.begin(size)) { diff --git a/ESP32_AP-Flasher/src/tag_db.cpp b/ESP32_AP-Flasher/src/tag_db.cpp index f98d64de..a99b3e34 100644 --- a/ESP32_AP-Flasher/src/tag_db.cpp +++ b/ESP32_AP-Flasher/src/tag_db.cpp @@ -9,6 +9,7 @@ #include "language.h" #include "storage.h" +#include "util.h" std::vector tagDB; std::unordered_map varDB; @@ -20,7 +21,7 @@ std::unordered_map hwdata = { Config config; // SemaphoreHandle_t tagDBOwner; -tagRecord* tagRecord::findByMAC(uint8_t mac[8]) { +tagRecord* tagRecord::findByMAC(const uint8_t mac[8]) { for (int32_t c = 0; c < tagDB.size(); c++) { tagRecord* tag = nullptr; tag = tagDB.at(c); @@ -31,10 +32,9 @@ tagRecord* tagRecord::findByMAC(uint8_t mac[8]) { return nullptr; } -bool deleteRecord(uint8_t mac[8]) { +bool deleteRecord(const uint8_t mac[8]) { for (int32_t c = 0; c < tagDB.size(); c++) { - tagRecord* tag = nullptr; - tag = tagDB.at(c); + tagRecord* tag = tagDB.at(c); if (memcmp(tag->mac, mac, 8) == 0) { if (tag->data != nullptr) { free(tag->data); @@ -48,7 +48,7 @@ bool deleteRecord(uint8_t mac[8]) { return false; } -void mac2hex(uint8_t* mac, char* hexBuffer) { +void mac2hex(const uint8_t* mac, char* hexBuffer) { sprintf(hexBuffer, "%02X%02X%02X%02X%02X%02X%02X%02X", mac[7], mac[6], mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]); } @@ -69,7 +69,7 @@ bool hex2mac(const String& hexString, uint8_t* mac) { } } -String tagDBtoJson(uint8_t mac[8], uint8_t startPos) { +String tagDBtoJson(const uint8_t mac[8], uint8_t startPos) { DynamicJsonDocument doc(5000); JsonArray tags = doc.createNestedArray("tags"); @@ -241,7 +241,7 @@ void loadDB(String filename) { void destroyDB() { Serial.println("destoying DB"); - Serial.printf("before, free heap: %d\n", ESP.getFreeHeap()); + util::printHeap(); for (uint32_t c = 0; c < tagDB.size(); c++) { tagRecord* tag = nullptr; tag = tagDB.at(c); @@ -252,7 +252,7 @@ void destroyDB() { delete tagDB[c]; tagDB.erase(tagDB.begin() + c); } - Serial.printf("after, free heap: %d\n", ESP.getFreeHeap()); + util::printHeap(); } uint32_t getTagCount() { diff --git a/ESP32_AP-Flasher/src/web.cpp b/ESP32_AP-Flasher/src/web.cpp index a1b92e88..01dd48c9 100644 --- a/ESP32_AP-Flasher/src/web.cpp +++ b/ESP32_AP-Flasher/src/web.cpp @@ -60,11 +60,12 @@ void wsErr(String text) { if (wsMutex) xSemaphoreGive(wsMutex); } -size_t dbSize(){ +size_t dbSize() { size_t size = tagDB.size() * sizeof(tagRecord); - for(auto &tag : tagDB) { - if (tag->data) + for (auto &tag : tagDB) { + if (tag->data) { size += tag->len; + } size += tag->modeConfigJson.length(); } return size; @@ -106,7 +107,7 @@ void wsSendSysteminfo() { xSemaphoreGive(wsMutex); } -void wsSendTaginfo(uint8_t *mac, uint8_t syncMode) { +void wsSendTaginfo(const uint8_t *mac, uint8_t syncMode) { if (syncMode != SYNC_DELETE) { String json = ""; json = tagDBtoJson(mac);