diff --git a/ap_fw/apfw.bin b/ap_fw/apfw.bin index 8c6b0502..59e55a7a 100644 Binary files a/ap_fw/apfw.bin and b/ap_fw/apfw.bin differ diff --git a/ap_fw/main.c b/ap_fw/main.c index a4c7ed4a..242e8d6d 100644 --- a/ap_fw/main.c +++ b/ap_fw/main.c @@ -138,7 +138,7 @@ struct espAvailDataReq { } __packed; #define TIMER_TICKS_PER_MS 1333UL -uint16_t __xdata version = 0x0006; +uint16_t __xdata version = 0x0007; #define RAW_PKT_PADDING 2 static uint8_t __xdata mRxBuf[COMMS_MAX_PACKET_SZ]; @@ -245,7 +245,8 @@ uint8_t getBlockDataLength() { // pendingdata slot stuff int8_t findSlotForMac(const uint8_t *mac) { for (uint8_t __xdata c = 0; c < MAX_PENDING_MACS; c++) { - if (u64_isEq((uint64_t __xdata *)mac, (uint64_t __xdata *)&(pendingDataArr[c].targetMac))) { // this costs 1 sloc :( + //if (u64_isEq((uint64_t __xdata *)mac, (uint64_t __xdata *)&(pendingDataArr[c].targetMac))) { // this costs 1 sloc :( + if (memcmp(mac, ((uint8_t __xdata *)&(pendingDataArr[c].targetMac)), 8) == 0) { if (pendingDataArr[c].attemptsLeft != 0) { return c; } @@ -320,7 +321,9 @@ void processSerial(uint8_t lastchar) { bytesRemain--; if (bytesRemain == 0) { if (checkCRC(serialbuffer, sizeof(struct pendingData))) { - int8_t slot = findFreeSlot(); + struct pendingData *pd = (struct pendingData *)serialbuffer; + int8_t slot = findSlotForMac(pd->targetMac); + if (slot == -1) slot = findFreeSlot(); if (slot != -1) { xMemCopyShort(&(pendingDataArr[slot]), serialbuffer, sizeof(struct pendingData)); pr("ACK>\n"); @@ -396,7 +399,17 @@ void espNotifyXferComplete(const uint8_t *src) { uartTx(((uint8_t *)exfc)[c]); } } -void espNotifyTimeOut() { +void espNotifyTimeOut(const uint8_t *src) { + struct espXferComplete exfc; + xMemCopy8(&exfc.src, src); + uartTx('X'); + uartTx('T'); + uartTx('O'); + uartTx('>'); + addCRC(&exfc, sizeof(exfc)); + for (uint8_t c = 0; c < sizeof(exfc); c++) { + uartTx(((uint8_t *)exfc)[c]); + } } // process data from tag @@ -738,7 +751,7 @@ void main(void) { for (uint8_t __xdata c = 0; c < MAX_PENDING_MACS; c++) { if (pendingDataArr[c].attemptsLeft == 1) { - espNotifyTimeOut(); + espNotifyTimeOut(pendingDataArr[c].targetMac); pendingDataArr[c].attemptsLeft = 0; } else if (pendingDataArr[c].attemptsLeft > 1) { pendingDataArr[c].attemptsLeft--; diff --git a/esp32_fw/data/www/main.js b/esp32_fw/data/www/main.js index 27d3529f..504bd8cf 100644 --- a/esp32_fw/data/www/main.js +++ b/esp32_fw/data/www/main.js @@ -139,7 +139,7 @@ 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 - 60 > item.dataset.nextcheckin) { + if ((Date.now() / 1000) + servertimediff - 300 > item.dataset.nextcheckin) { $('#tag' + tagmac + ' .warningicon').style.display='inline-block'; $('#tag' + tagmac).classList.remove("tagpending") $('#tag' + tagmac).style.background = '#ffffcc'; diff --git a/esp32_fw/include/newproto.h b/esp32_fw/include/newproto.h index c41b3972..5d98df56 100644 --- a/esp32_fw/include/newproto.h +++ b/esp32_fw/include/newproto.h @@ -8,4 +8,5 @@ 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); +extern void processXferTimeout(struct espXferComplete* xfc); extern void processDataReq(struct espAvailDataReq* adr); \ No newline at end of file diff --git a/esp32_fw/src/contentmanager.cpp b/esp32_fw/src/contentmanager.cpp index 2d0b1d30..fb5e96df 100644 --- a/esp32_fw/src/contentmanager.cpp +++ b/esp32_fw/src/contentmanager.cpp @@ -88,26 +88,26 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { case Today: drawDate(filename, taginfo); - updateTagImage(filename, mac, (midnight - now) / 60 - 10); taginfo->nextupdate = midnight; + updateTagImage(filename, mac, (midnight - now) / 60 - 10); break; case CountDays: if (buttonPressed) cfgobj["counter"] = 0; drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"], taginfo); - updateTagImage(filename, mac, (buttonPressed?0:15)); - cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1; taginfo->nextupdate = midnight; + updateTagImage(filename, mac, (buttonPressed ? 0 : 15)); + cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1; break; case CountHours: if (buttonPressed) cfgobj["counter"] = 0; drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"], taginfo); - updateTagImage(filename, mac, (buttonPressed?0:5)); - cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1; taginfo->nextupdate = now + 3600; + updateTagImage(filename, mac, (buttonPressed ? 0 : 5)); + cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1; break; case Weather: @@ -118,8 +118,8 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { // https://github.com/erikflowers/weather-icons drawWeather(filename, cfgobj["location"], taginfo); - updateTagImage(filename, mac, 15); taginfo->nextupdate = now + 3600; + updateTagImage(filename, mac, 15); break; case Firmware: @@ -142,16 +142,16 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { case Memo: drawIdentify(filename, taginfo); + taginfo->nextupdate = now + 12 * 3600; updateTagImage(filename, mac, 0); - taginfo->nextupdate = now + 12*3600; break; case ImageUrl: if (getImgURL(filename, cfgobj["url"], (time_t)cfgobj["#fetched"])) { + taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 5 ? 5 : cfgobj["interval"].as()); 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; } diff --git a/esp32_fw/src/newproto.cpp b/esp32_fw/src/newproto.cpp index bbd2f148..efad9f80 100644 --- a/esp32_fw/src/newproto.cpp +++ b/esp32_fw/src/newproto.cpp @@ -58,7 +58,7 @@ void prepareIdleReq(uint8_t* dst, uint16_t nextCheckin) { memcpy(pending.targetMac, dst, 8); pending.availdatainfo.dataType = DATATYPE_NOUPDATE; pending.availdatainfo.nextCheckIn = nextCheckin; - pending.attemptsLeft = 10; + pending.attemptsLeft = 10 + MIN_RESPONSE_TIME; char buffer[64]; uint8_t src[8]; @@ -124,6 +124,7 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t md5.getBytes(md5bytes); } + uint16_t attempts = 60; uint8_t src[8]; *((uint64_t*)src) = swap64(*((uint64_t*)dst)); uint8_t mac[6]; @@ -131,11 +132,20 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t tagRecord* taginfo = nullptr; taginfo = tagRecord::findByMAC(mac); if (taginfo != nullptr) { + if (memcmp(md5bytes, taginfo->md5pending, 16) == 0) { wsLog("new image is the same as current image. not updating tag."); wsSendTaginfo(mac); return true; } + + time_t now; + time(&now); + uint16_t minutesUntilNextCheckin = 0; + if (taginfo->expectedNextCheckin > now) minutesUntilNextCheckin = (taginfo->expectedNextCheckin - now) / 60; + attempts += minutesUntilNextCheckin; + } else { + wsErr("Tag not found, this shouldn't happen."); } // the message that will be sent to the AP to tell the tag there is data pending @@ -145,7 +155,7 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t pending.availdatainfo.dataVer = *((uint64_t*)md5bytes); pending.availdatainfo.dataSize = file.size(); pending.availdatainfo.nextCheckIn = nextCheckin; - pending.attemptsLeft = 10; + pending.attemptsLeft = attempts; sendDataAvail(&pending); // data for the cache on the esp32; needs to hold the data longer than the maximum timeout on the AP @@ -284,6 +294,29 @@ void processXferComplete(struct espXferComplete* xfc) { wsSendTaginfo(mac); } +void processXferTimeout(struct espXferComplete* xfc) { + char buffer[64]; + uint8_t src[8]; + *((uint64_t*)src) = swap64(*((uint64_t*)xfc->src)); + sprintf(buffer, "< %02X%02X%02X%02X%02X%02X xfer timeout\n\0", src[2], src[3], src[4], src[5], src[6], src[7]); + wsErr((String)buffer); + Serial.print(buffer); + uint8_t mac[6]; + memcpy(mac, src + 2, sizeof(mac)); + + time_t now; + time(&now); + tagRecord* taginfo = nullptr; + taginfo = tagRecord::findByMAC(mac); + if (taginfo != nullptr) { + taginfo->expectedNextCheckin = now + 60; + taginfo->CheckinInMinPending = 0; + taginfo->pending = false; + memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t)); + } + wsSendTaginfo(mac); +} + void processDataReq(struct espAvailDataReq* eadr) { digitalWrite(ONBOARD_LED, LOW); diff --git a/esp32_fw/src/serial.cpp b/esp32_fw/src/serial.cpp index 6b2c1379..2331151a 100644 --- a/esp32_fw/src/serial.cpp +++ b/esp32_fw/src/serial.cpp @@ -19,6 +19,7 @@ #define ZBS_RX_WAIT_XFERCOMPLETE 8 #define ZBS_RX_WAIT_DATA_REQ 9 #define ZBS_RX_WAIT_JOINNETWORK 10 +#define ZBS_RX_WAIT_XFERTIMEOUT 11 uint8_t restartBlockRequest = 0; @@ -111,6 +112,7 @@ void Ping() { void SerialRXLoop() { if (Serial1.available()) { lastchar = Serial1.read(); + //Serial.write(lastchar); switch (RXState) { case ZBS_RX_WAIT_HEADER: Serial.write(lastchar); @@ -119,8 +121,7 @@ void SerialRXLoop() { cmdbuffer[c] = cmdbuffer[c + 1]; } cmdbuffer[3] = lastchar; - if ((strncmp(cmdbuffer, "VER>", 4) == 0) && waitingForVersion) { - waitingForVersion = 0; + if ((strncmp(cmdbuffer, "VER>", 4) == 0)) { pktindex = 0; RXState = ZBS_RX_WAIT_VER; charindex = 0; @@ -151,6 +152,12 @@ void SerialRXLoop() { packetp = (uint8_t*)calloc(sizeof(struct espXferComplete) + 8, 1); memset(cmdbuffer, 0x00, 4); } + if (strncmp(cmdbuffer, "XTO>", 4) == 0) { + RXState = ZBS_RX_WAIT_XFERTIMEOUT; + pktindex = 0; + packetp = (uint8_t*)calloc(sizeof(struct espXferComplete) + 8, 1); + memset(cmdbuffer, 0x00, 4); + } break; case ZBS_RX_BLOCK_REQUEST: packetp[pktindex] = lastchar; @@ -171,6 +178,16 @@ void SerialRXLoop() { RXState = ZBS_RX_WAIT_HEADER; } break; + case ZBS_RX_WAIT_XFERTIMEOUT: + packetp[pktindex] = lastchar; + pktindex++; + if (pktindex == sizeof(struct espXferComplete)) { + struct espXferComplete* xfc = (struct espXferComplete*)packetp; + processXferTimeout(xfc); + free(packetp); + RXState = ZBS_RX_WAIT_HEADER; + } + break; case ZBS_RX_WAIT_DATA_REQ: packetp[pktindex] = lastchar; pktindex++; @@ -182,6 +199,7 @@ void SerialRXLoop() { } break; case ZBS_RX_WAIT_VER: + waitingForVersion = 0; cmdbuffer[charindex] = lastchar; charindex++; if (charindex == 4) { @@ -214,6 +232,8 @@ void zbsRxTask(void* parameter) { Serial1.begin(230400, SERIAL_8N1, RXD1, TXD1); simplePowerOn(); + bool firstrun = true; + Serial1.print("VER?"); waitingForVersion = esp_timer_get_time(); while (1) { @@ -223,14 +243,30 @@ void zbsRxTask(void* parameter) { Serial1.write(Serial.read()); } vTaskDelay(1 / portTICK_PERIOD_MS); + if (waitingForVersion) { if (esp_timer_get_time() - waitingForVersion > 10000*1000ULL) { - waitingForVersion = esp_timer_get_time(); + waitingForVersion = 0; //performDeviceFlash(); 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(); } } + + if (version && firstrun) { + Serial.printf("ZBS/Zigbee FW version: %04X\n", version); + uint16_t fsversion; + lookupFirmwareFile(fsversion); + if ((fsversion) && (version != fsversion)) { + Serial.printf("Firmware version on LittleFS: %04X\n", fsversion); + Serial.printf("Performing flash update in about 30 seconds"); + vTaskDelay(30000 / portTICK_PERIOD_MS); + performDeviceFlash(); + } else if (!fsversion) { + Serial.println("No ZBS/Zigbee FW binary found on SPIFFS, please upload a zigbeebase000X.bin - format binary to enable flashing"); + } + firstrun = false; + } } }