diff --git a/esp32_fw/data/fonts/tw20.vlw b/esp32_fw/data/fonts/tw20.vlw new file mode 100644 index 00000000..884fc494 Binary files /dev/null and b/esp32_fw/data/fonts/tw20.vlw differ diff --git a/esp32_fw/data/fonts/twbold20.vlw b/esp32_fw/data/fonts/twbold20.vlw new file mode 100644 index 00000000..5ed00c42 Binary files /dev/null and b/esp32_fw/data/fonts/twbold20.vlw differ diff --git a/esp32_fw/data/fonts/weathericons50.vlw b/esp32_fw/data/fonts/weathericons50.vlw new file mode 100644 index 00000000..4019f131 Binary files /dev/null and b/esp32_fw/data/fonts/weathericons50.vlw differ diff --git a/esp32_fw/data/fonts/weathericons78.vlw b/esp32_fw/data/fonts/weathericons78.vlw index a1d9d0b6..073f249c 100644 Binary files a/esp32_fw/data/fonts/weathericons78.vlw and b/esp32_fw/data/fonts/weathericons78.vlw differ diff --git a/esp32_fw/data/www/edit.html b/esp32_fw/data/www/edit.html index f5b566f5..4adac442 100644 --- a/esp32_fw/data/www/edit.html +++ b/esp32_fw/data/www/edit.html @@ -595,6 +595,7 @@ case "jpg": case "gif": case "bmp": + case "pending": return true; } } diff --git a/esp32_fw/data/www/main.js b/esp32_fw/data/www/main.js index 5d0276da..27d3529f 100644 --- a/esp32_fw/data/www/main.js +++ b/esp32_fw/data/www/main.js @@ -139,8 +139,9 @@ function updatecards() { if (item.dataset.lastseen && item.dataset.lastseen > 1672531200) { let idletime = (Date.now() / 1000) + servertimediff - item.dataset.lastseen; $('#tag' + tagmac + ' .lastseen').innerHTML = "last seen"+displayTime(Math.floor(idletime))+" ago"; - if ((Date.now() / 1000) + servertimediff > item.dataset.nextcheckin) { + if ((Date.now() / 1000) + servertimediff - 60 > item.dataset.nextcheckin) { $('#tag' + tagmac + ' .warningicon').style.display='inline-block'; + $('#tag' + tagmac).classList.remove("tagpending") $('#tag' + tagmac).style.background = '#ffffcc'; } if (idletime > 24*3600) { diff --git a/esp32_fw/include/newproto.h b/esp32_fw/include/newproto.h index 54ffe327..c41b3972 100644 --- a/esp32_fw/include/newproto.h +++ b/esp32_fw/include/newproto.h @@ -4,6 +4,7 @@ extern void addCRC(void* p, uint8_t len); extern bool checkCRC(void* p, uint8_t len); extern void processBlockRequest(struct espBlockRequest* br); +extern void prepareIdleReq(uint8_t* dst, uint16_t nextCheckin); extern bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t nextCheckin); extern void processJoinNetwork(struct espJoinNetwork* xjn); extern void processXferComplete(struct espXferComplete* xfc); diff --git a/esp32_fw/include/pendingdata.h b/esp32_fw/include/pendingdata.h index 1bbc1e9f..88b09054 100644 --- a/esp32_fw/include/pendingdata.h +++ b/esp32_fw/include/pendingdata.h @@ -11,7 +11,7 @@ class pendingdata { //uint8_t dst[8]; uint64_t ver; String md5; - uint16_t timeout; + uint32_t timeout; uint8_t datatimeout; uint8_t* data = nullptr; uint32_t len; diff --git a/esp32_fw/include/serial.h b/esp32_fw/include/serial.h index d9476e6c..76ed03a1 100644 --- a/esp32_fw/include/serial.h +++ b/esp32_fw/include/serial.h @@ -4,4 +4,5 @@ void zbsTx(uint8_t* packetdata, uint8_t len); void zbsRxTask(void* parameter); void sendCancelPending(struct pendingData* pending); -void sendDataAvail(struct pendingData* pending); \ No newline at end of file +void sendDataAvail(struct pendingData* pending); +void Ping(); \ No newline at end of file diff --git a/esp32_fw/include/settings.h b/esp32_fw/include/settings.h index bfe32c49..d1ab28cd 100644 --- a/esp32_fw/include/settings.h +++ b/esp32_fw/include/settings.h @@ -1,19 +1,16 @@ #include -#define CHECK_IN_DELAY 900000 -#define RETRY_DELAY 1000 -#define FAILED_TILL_BLANK 2 -#define FAILED_TILL_REASSOCIATE 1 - // how long the we should keep the transfer metadata -#define PENDING_TIMEOUT 600 +#define PENDING_TIMEOUT 24*3600 // this determines how long images will be cached; #define PENDING_DATA_TIMEOUT 60 +// maximum time (in minutes) that a tag is put to sleep if no update is expected. +#define MIN_RESPONSE_TIME 10 // flasher options #define CUSTOM_MAC_HDR 0x0000 -/* +/* Lolin32 lite connections to AP tag #define RXD1 16 #define TXD1 17 @@ -24,10 +21,13 @@ #define ZBS_Reset 2 #define ZBS_POWER1 13 #define ZBS_POWER2 15 -*/ -#define RXD1 13 -#define TXD1 12 +#define ONBOARD_LED 22 +//*/ + +//* +#define RXD1 13 +#define TXD1 12 #define ZBS_SS 21 #define ZBS_CLK 18 @@ -37,5 +37,8 @@ #define ZBS_POWER1 15 #define ZBS_POWER2 2 +#define ONBOARD_LED 22 +//*/ + #define MAX_WRITE_ATTEMPTS 5 diff --git a/esp32_fw/include/tag_db.h b/esp32_fw/include/tag_db.h index 703423f3..4e8f147b 100644 --- a/esp32_fw/include/tag_db.h +++ b/esp32_fw/include/tag_db.h @@ -6,17 +6,6 @@ #pragma pack(push, 1) #pragma once -enum contentModes { - Image, - Today, - CountDays, - CountHours, - Weather, - Firmware, - Memo, - ImageUrl, -}; - #define SOLUM_154_033 0 #define SOLUM_29_033 1 #define SOLUM_42_033 2 @@ -29,13 +18,13 @@ enum contentModes { class tagRecord { public: uint16_t nextCheckinpending; - tagRecord() : mac{0}, alias(""), lastseen(0), nextupdate(0), contentMode(Image), pending(false), md5{0}, md5pending{0}, CheckinInMinPending(0), expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0) {} + tagRecord() : mac{0}, alias(""), lastseen(0), nextupdate(0), contentMode(0), pending(false), md5{0}, md5pending{0}, CheckinInMinPending(0), expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0) {} uint8_t mac[6]; String alias; uint32_t lastseen; uint32_t nextupdate; - contentModes contentMode; + uint8_t contentMode; bool pending; uint8_t md5[16]; uint8_t md5pending[16]; diff --git a/esp32_fw/src/contentmanager.cpp b/esp32_fw/src/contentmanager.cpp index a4c08405..2d0b1d30 100644 --- a/esp32_fw/src/contentmanager.cpp +++ b/esp32_fw/src/contentmanager.cpp @@ -12,6 +12,18 @@ #include "makeimage.h" #include "web.h" +enum contentModes { + Image, + Today, + CountDays, + CountHours, + Weather, + Firmware, + Memo, + ImageUrl, +}; + + void contentRunner() { time_t now; time(&now); @@ -76,8 +88,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { case Today: drawDate(filename, taginfo); - // updateTagImage(filename, mac, (midnight - now) / 60 - 10); - updateTagImage(filename, mac, 600); + updateTagImage(filename, mac, (midnight - now) / 60 - 10); taginfo->nextupdate = midnight; break; @@ -85,7 +96,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { if (buttonPressed) cfgobj["counter"] = 0; drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"], taginfo); - updateTagImage(filename, mac, (buttonPressed?0:600)); + updateTagImage(filename, mac, (buttonPressed?0:15)); cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1; taginfo->nextupdate = midnight; break; @@ -94,9 +105,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { if (buttonPressed) cfgobj["counter"] = 0; drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"], taginfo); - // updateTagImage(&filename, mac, (3600 - now % 3600) / 60); - // taginfo->nextupdate = now + 3600 - (now % 3600); - updateTagImage(filename, mac, (buttonPressed?0:600)); + updateTagImage(filename, mac, (buttonPressed?0:5)); cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1; taginfo->nextupdate = now + 3600; break; @@ -109,7 +118,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { // https://github.com/erikflowers/weather-icons drawWeather(filename, cfgobj["location"], taginfo); - updateTagImage(filename, mac, 600); + updateTagImage(filename, mac, 15); taginfo->nextupdate = now + 3600; break; @@ -134,7 +143,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { drawIdentify(filename, taginfo); updateTagImage(filename, mac, 0); - taginfo->nextupdate = now + 24*3600; + taginfo->nextupdate = now + 12*3600; break; case ImageUrl: @@ -142,8 +151,10 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { if (getImgURL(filename, cfgobj["url"], (time_t)cfgobj["#fetched"])) { updateTagImage(filename, mac, cfgobj["interval"].as()); cfgobj["#fetched"] = now; + taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 5 ? 5 : cfgobj["interval"].as()); + } else { + taginfo->nextupdate = now + 300; } - taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 5 ? 5 : cfgobj["interval"].as()) ; break; } @@ -449,7 +460,7 @@ bool getImgURL(String &filename, String URL, time_t fetched) { } } http.end(); - return (httpCode == 200); + return (httpCode == 200 || httpCode == 304); } char *formatHttpDate(time_t t) { diff --git a/esp32_fw/src/main.cpp b/esp32_fw/src/main.cpp index 391c9ae5..9dc18d5d 100644 --- a/esp32_fw/src/main.cpp +++ b/esp32_fw/src/main.cpp @@ -8,6 +8,7 @@ #include "makeimage.h" #include "pendingdata.h" #include "serial.h" +#include "settings.h" #include "soc/rtc_wdt.h" #include "tag_db.h" #include "web.h" @@ -21,7 +22,9 @@ void timeTask(void* parameter) { Serial.println("Waiting for valid time from NTP-server"); } else { if (now % 10 == 0) wsSendSysteminfo(); - if (now % 300 == 0) saveDB("/current/tagDB.json"); + if (now % 60 == 3) Ping(); + if (now % 300 == 6) saveDB("/current/tagDB.json"); + contentRunner(); } vTaskDelay(1000 / portTICK_PERIOD_MS); @@ -32,6 +35,9 @@ void setup() { Serial.begin(115200); Serial.print(">\n"); + pinMode(ONBOARD_LED, OUTPUT); + digitalWrite(ONBOARD_LED, HIGH); + configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", "0.nl.pool.ntp.org", "europe.pool.ntp.org", "time.nist.gov"); // https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv diff --git a/esp32_fw/src/makeimage.cpp b/esp32_fw/src/makeimage.cpp index 4bcd7955..54e8660d 100644 --- a/esp32_fw/src/makeimage.cpp +++ b/esp32_fw/src/makeimage.cpp @@ -29,7 +29,7 @@ void jpg2grays(String filein, String fileout) { } spr.setColorDepth(8); spr.fillSprite(TFT_WHITE); - TJpgDec.drawFsJpg(0, 0, filein); + TJpgDec.drawFsJpg(0, 0, filein, LittleFS); spr2grays(spr, w, h, fileout); spr.deleteSprite(); @@ -236,8 +236,7 @@ void spr2grays(TFT_eSprite &spr, long w, long h, String &fileout) { f_out.write(0); } f_out.close(); - Serial.println(millis() - t); - Serial.println("finished writing BMP"); + Serial.println("finished writing BMP " + String(millis() - t) + "ms"); } void bmp2grays(String filein, String fileout) { @@ -429,6 +428,5 @@ void bmp2grays(String filein, String fileout) { } f_in.close(); f_out.close(); - Serial.println(millis() - t); - Serial.println("finished writing BMP2"); + Serial.println("finished converting BMP " + String(millis() - t) + "ms"); } \ No newline at end of file diff --git a/esp32_fw/src/newproto.cpp b/esp32_fw/src/newproto.cpp index a69acb13..bbd2f148 100644 --- a/esp32_fw/src/newproto.cpp +++ b/esp32_fw/src/newproto.cpp @@ -49,14 +49,34 @@ void prepareCancelPending(uint64_t ver) { sendCancelPending(&pending); } -bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t nextCheckin) { +void prepareIdleReq(uint8_t* dst, uint16_t nextCheckin) { + if (nextCheckin > MIN_RESPONSE_TIME) { + // to prevent very long sleeps of the tag + nextCheckin = MIN_RESPONSE_TIME; + } + struct pendingData pending = {0}; + memcpy(pending.targetMac, dst, 8); + pending.availdatainfo.dataType = DATATYPE_NOUPDATE; + pending.availdatainfo.nextCheckIn = nextCheckin; + pending.attemptsLeft = 10; - if (nextCheckin > 1440) { + char buffer[64]; + uint8_t src[8]; + *((uint64_t*)src) = swap64(*((uint64_t*)dst)); + sprintf(buffer, "idle request %02X%02X%02X%02X%02X%02X %d minutes\n\0", src[2], src[3], src[4], src[5], src[6], src[7], nextCheckin); + Serial.print(buffer); + + sendDataAvail(&pending); +} + +bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t nextCheckin) { + if (nextCheckin > MIN_RESPONSE_TIME) { //to prevent very long sleeps of the tag - nextCheckin = 0; + nextCheckin = MIN_RESPONSE_TIME; } *filename = "/" + *filename; + LittleFS.begin(); if (!LittleFS.exists(*filename)) return false; fs::File file = LittleFS.open(*filename); @@ -137,7 +157,6 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t pendinginfo->data = nullptr; pendinginfo->timeout = PENDING_TIMEOUT; //pendinginfo->data = getDataForFile(&file); - pendinginfo->timeout = 1800; // ***fixme... a tag can sleep for a long time when ttl is used. pendingfiles.push_back(pendinginfo); if (dataType != DATATYPE_UPDATE) { @@ -157,7 +176,7 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t wsLog("new image pending: " + String(dst_path)); if (taginfo != nullptr) { taginfo->pending = true; - taginfo->CheckinInMinPending = nextCheckin + 1; + taginfo->CheckinInMinPending = nextCheckin; memcpy(taginfo->md5pending, md5bytes, sizeof(md5bytes)); } } @@ -244,14 +263,31 @@ void processXferComplete(struct espXferComplete* xfc) { tagRecord* taginfo = nullptr; taginfo = tagRecord::findByMAC(mac); if (taginfo != nullptr) { + + uint16_t minutesUntilNextUpdate = 0; + if (taginfo->nextupdate > now + 60 * taginfo->CheckinInMinPending + 3) { + minutesUntilNextUpdate = (taginfo->nextupdate - now) / 60 - taginfo->CheckinInMinPending; + if (minutesUntilNextUpdate > taginfo->CheckinInMinPending) minutesUntilNextUpdate = taginfo->CheckinInMinPending; + if (minutesUntilNextUpdate > MIN_RESPONSE_TIME) minutesUntilNextUpdate = MIN_RESPONSE_TIME; + + taginfo->expectedNextCheckin = now + 60 * minutesUntilNextUpdate + 60; + if (minutesUntilNextUpdate > 0) prepareIdleReq (xfc->src, minutesUntilNextUpdate); + taginfo->CheckinInMinPending = minutesUntilNextUpdate; + } else { + taginfo->expectedNextCheckin = now + 60; + taginfo->CheckinInMinPending = 0; + } + taginfo->pending = false; - taginfo->expectedNextCheckin = now + 60 * taginfo->CheckinInMinPending + 30; memcpy(taginfo->md5, taginfo->md5pending, sizeof(taginfo->md5pending)); } wsSendTaginfo(mac); } void processDataReq(struct espAvailDataReq* eadr) { + + digitalWrite(ONBOARD_LED, LOW); + char buffer[64]; uint8_t src[8]; *((uint64_t*)src) = swap64(*((uint64_t*)eadr->src)); @@ -270,7 +306,19 @@ void processDataReq(struct espAvailDataReq* eadr) { time_t now; time(&now); taginfo->lastseen = now; - taginfo->expectedNextCheckin = now + 300; + + uint16_t minutesUntilNextUpdate = 0; + if (taginfo->nextupdate > now + 60 * taginfo->CheckinInMinPending + 3 && taginfo->pending == false) { + minutesUntilNextUpdate = (taginfo->nextupdate - now) / 60 - taginfo->CheckinInMinPending; + if (minutesUntilNextUpdate > taginfo->CheckinInMinPending) minutesUntilNextUpdate = taginfo->CheckinInMinPending; + if (minutesUntilNextUpdate > MIN_RESPONSE_TIME) minutesUntilNextUpdate = MIN_RESPONSE_TIME; + taginfo->expectedNextCheckin = now + 60 * minutesUntilNextUpdate + 60; + if (minutesUntilNextUpdate > 0) prepareIdleReq(eadr->src, minutesUntilNextUpdate); + taginfo->CheckinInMinPending = minutesUntilNextUpdate; + } else { + taginfo->expectedNextCheckin = now + 60 * taginfo->CheckinInMinPending + 60; + } + if (eadr->adr.lastPacketRSSI != 0) { taginfo->LQI = eadr->adr.lastPacketLQI; taginfo->hwType = eadr->adr.hwType; @@ -287,4 +335,6 @@ void processDataReq(struct espAvailDataReq* eadr) { sprintf(buffer, "", 4) == 0) && waitingForVersion) { - waitingForVersion = false; + waitingForVersion = 0; pktindex = 0; RXState = ZBS_RX_WAIT_VER; charindex = 0; @@ -182,7 +187,8 @@ void SerialRXLoop() { if (charindex == 4) { charindex = 0; version = (uint16_t)strtoul(cmdbuffer, NULL, 16); - uint16_t fsversion; // BREAK here! break; + /* + uint16_t fsversion; lookupFirmwareFile(fsversion); if ((fsversion) && (version != fsversion)) { Serial.printf("ZBS/Zigbee FW version: %04X, version on SPIFFS: %04X\n", version, fsversion); @@ -194,6 +200,7 @@ void SerialRXLoop() { } else { Serial.printf("ZBS/Zigbee FW version: %04X\n", version); } + */ RXState = ZBS_RX_WAIT_HEADER; } break; @@ -208,6 +215,7 @@ void zbsRxTask(void* parameter) { simplePowerOn(); Serial1.print("VER?"); + waitingForVersion = esp_timer_get_time(); while (1) { SerialRXLoop(); @@ -216,13 +224,12 @@ void zbsRxTask(void* parameter) { } vTaskDelay(1 / portTICK_PERIOD_MS); if (waitingForVersion) { - if (millis() > 30000) { - waitingForVersion = false; - performDeviceFlash(); - // Serial.printf("We've been waiting for communication from the tag, but got nothing. This is expected if this tag hasn't been flashed yet. We'll try to flash it.\n"); - // doAPUpdate(); + if (esp_timer_get_time() - waitingForVersion > 10000*1000ULL) { + waitingForVersion = esp_timer_get_time(); //performDeviceFlash(); - // SDAtest(); + Serial.println("I wasn't able to connect to a ZBS tag, trying to reboot the tag."); + Serial.println("If this problem persists, please check wiring and definitions in the settings.h file, and presence of the right firmware"); + simplePowerOn(); } } } diff --git a/esp32_fw/src/tag_db.cpp b/esp32_fw/src/tag_db.cpp index 65c4f1aa..e6b6224f 100644 --- a/esp32_fw/src/tag_db.cpp +++ b/esp32_fw/src/tag_db.cpp @@ -149,7 +149,7 @@ void loadDB(String filename) { } taginfo->pending = false; taginfo->alias = tag["alias"].as(); - taginfo->contentMode = static_cast(tag["contentMode"]); + taginfo->contentMode = tag["contentMode"]; taginfo->LQI = tag["LQI"]; taginfo->RSSI = tag["RSSI"]; taginfo->temperature = tag["temperature"]; diff --git a/esp32_fw/src/web.cpp b/esp32_fw/src/web.cpp index fc01ad0f..e18d6f74 100644 --- a/esp32_fw/src/web.cpp +++ b/esp32_fw/src/web.cpp @@ -282,8 +282,11 @@ void init_web() { if (taginfo != nullptr) { taginfo->alias = request->getParam("alias", true)->value(); taginfo->modeConfigJson = request->getParam("modecfgjson", true)->value(); - taginfo->contentMode = (contentModes)atoi(request->getParam("contentmode", true)->value().c_str()); + taginfo->contentMode = atoi(request->getParam("contentmode", true)->value().c_str()); taginfo->nextupdate = 0; + taginfo->CheckinInMinPending = 0; + memset(taginfo->md5, 0, 16 * sizeof(uint8_t)); + memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t)); wsSendTaginfo(mac); saveDB("/current/tagDB.json"); request->send(200, "text/plain", "Ok, saved");