diff --git a/ESP32_AP-Flasher/data/tagtypes/31.json b/ESP32_AP-Flasher/data/tagtypes/31.json index 02e0a984..ca80ffa7 100644 --- a/ESP32_AP-Flasher/data/tagtypes/31.json +++ b/ESP32_AP-Flasher/data/tagtypes/31.json @@ -13,7 +13,7 @@ }, "shortlut": 0, "options": ["button", "led"], - "contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20], + "contentids": [ 22, 23, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 26], "template": { "1": { "weekday": [148, 5, "Signika-SB.ttf", 60], diff --git a/ESP32_AP-Flasher/data/www/edit.html.gz b/ESP32_AP-Flasher/data/www/edit.html.gz index e397af05..4af04e1a 100644 Binary files a/ESP32_AP-Flasher/data/www/edit.html.gz and b/ESP32_AP-Flasher/data/www/edit.html.gz differ diff --git a/ESP32_AP-Flasher/data/www/index.html.gz b/ESP32_AP-Flasher/data/www/index.html.gz index 18e26bb4..20623cac 100644 Binary files a/ESP32_AP-Flasher/data/www/index.html.gz and b/ESP32_AP-Flasher/data/www/index.html.gz differ diff --git a/ESP32_AP-Flasher/data/www/main.css.gz b/ESP32_AP-Flasher/data/www/main.css.gz index d643fbff..7b94706c 100644 Binary files a/ESP32_AP-Flasher/data/www/main.css.gz and b/ESP32_AP-Flasher/data/www/main.css.gz differ diff --git a/ESP32_AP-Flasher/data/www/main.js.gz b/ESP32_AP-Flasher/data/www/main.js.gz index 269c0856..362647ec 100644 Binary files a/ESP32_AP-Flasher/data/www/main.js.gz and b/ESP32_AP-Flasher/data/www/main.js.gz differ diff --git a/ESP32_AP-Flasher/include/commstructs.h b/ESP32_AP-Flasher/include/commstructs.h index 9193dbc3..d39b9db8 100644 --- a/ESP32_AP-Flasher/include/commstructs.h +++ b/ESP32_AP-Flasher/include/commstructs.h @@ -1,4 +1,7 @@ -#include +#ifndef NEWPROTO_H +#define NEWPROTO_H + +#include #pragma pack(push, 1) #include "../../oepl-definitions.h" @@ -25,7 +28,7 @@ struct APlist { #define SYNC_USERCFG 1 #define SYNC_TAGSTATUS 2 #define SYNC_DELETE 3 -#define SYNC_VERSION 0xAA00 +#define SYNC_VERSION 0xAA01 struct TagInfo { uint16_t structVersion = SYNC_VERSION; @@ -34,14 +37,16 @@ struct TagInfo { char alias[32]; uint32_t lastseen; uint32_t nextupdate; - bool pending; + uint16_t pendingCount; uint32_t expectedNextCheckin; uint8_t hwType; uint8_t wakeupReason; uint8_t capabilities; uint16_t pendingIdle; uint8_t contentMode; + uint8_t reserved[8]; } __packed; +#pragma pack(pop) -#pragma pack(pop) \ No newline at end of file +#endif // NEWPROTO_H \ No newline at end of file diff --git a/ESP32_AP-Flasher/include/newproto.h b/ESP32_AP-Flasher/include/newproto.h index 39c07f88..61dc3c95 100644 --- a/ESP32_AP-Flasher/include/newproto.h +++ b/ESP32_AP-Flasher/include/newproto.h @@ -1,4 +1,14 @@ #include +#include + +#include "commstructs.h" + +struct PendingItem { + struct pendingData pendingdata; + char filename[50]; + uint8_t* data; + uint32_t len; +}; extern void addCRC(void* p, uint8_t len); extern bool checkCRC(void* p, uint8_t len); @@ -23,3 +33,13 @@ bool checkMirror(struct tagRecord* taginfo, struct pendingData* pending); void refreshAllPending(); void updateContent(const uint8_t* dst); void setAPchannel(); + +void enqueueItem(const PendingItem& item); +bool dequeueItem(const uint8_t* targetMac); +bool dequeueItem(const uint8_t* targetMac, const uint64_t dataVer); +uint16_t countQueueItem(const uint8_t* targetMac); +extern PendingItem* getQueueItem(const uint8_t* targetMac); +extern PendingItem* getQueueItem(const uint8_t* targetMac, const uint64_t dataVer); +void checkQueue(const uint8_t* targetMac); +bool queueDataAvail(struct pendingData* pending); +uint8_t* getDataForFile(fs::File& file); diff --git a/ESP32_AP-Flasher/include/serialap.h b/ESP32_AP-Flasher/include/serialap.h index d04e25d1..0c44e3ac 100644 --- a/ESP32_AP-Flasher/include/serialap.h +++ b/ESP32_AP-Flasher/include/serialap.h @@ -18,7 +18,7 @@ struct APInfoS { uint8_t channel; uint8_t mac[8]; uint8_t power; - uint8_t pending; + uint8_t pendingBuffer; uint8_t nop; }; @@ -34,4 +34,4 @@ bool sendChannelPower(struct espSetChannelPower* scp); void rxSerialTask2(void* parameter); void APTagReset(); bool bringAPOnline(); -void setAPstate(bool isOnline, uint8_t state); \ No newline at end of file +void setAPstate(bool isOnline, uint8_t state); diff --git a/ESP32_AP-Flasher/include/tag_db.h b/ESP32_AP-Flasher/include/tag_db.h index 4dbf78b6..bc6f1422 100644 --- a/ESP32_AP-Flasher/include/tag_db.h +++ b/ESP32_AP-Flasher/include/tag_db.h @@ -14,7 +14,7 @@ class tagRecord { public: - tagRecord() : mac{0}, version(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), apIp(IPAddress(0, 0, 0, 0)), pendingIdle(0), hasCustomLUT(false), rotate(0), lut(0), tagSoftwareVersion(0), currentChannel(0), dataType(0), filename(""), data(nullptr), len(0), invert(0) {} + tagRecord() : mac{0}, version(0), alias(""), lastseen(0), nextupdate(0), contentMode(0), pendingCount(0), md5{0}, expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0), lastfullupdate(0), isExternal(false), apIp(IPAddress(0, 0, 0, 0)), pendingIdle(0), hasCustomLUT(false), rotate(0), lut(0), tagSoftwareVersion(0), currentChannel(0), dataType(0), filename(""), data(nullptr), len(0), invert(0) {} uint8_t mac[8]; uint8_t version; @@ -22,9 +22,8 @@ class tagRecord { uint32_t lastseen; uint32_t nextupdate; uint8_t contentMode; - bool pending; + uint16_t pendingCount; uint8_t md5[16]; - uint8_t md5pending[16]; uint32_t expectedNextCheckin; String modeConfigJson; uint8_t LQI; diff --git a/ESP32_AP-Flasher/include/tagdata.h b/ESP32_AP-Flasher/include/tagdata.h index 0fed8e90..feb69fc0 100644 --- a/ESP32_AP-Flasher/include/tagdata.h +++ b/ESP32_AP-Flasher/include/tagdata.h @@ -3,6 +3,8 @@ /// @brief Custom tag data parser and helpers #pragma once +#ifndef SAVE_SPACE + #include #include @@ -137,4 +139,6 @@ template , bool> = t inline T bytesTo(const uint8_t *data, int length) { return T(data, data + length); } -} // namespace TagData \ No newline at end of file +} // namespace TagData + +#endif \ No newline at end of file diff --git a/ESP32_AP-Flasher/platformio.ini b/ESP32_AP-Flasher/platformio.ini index 90b402dd..927c1b58 100644 --- a/ESP32_AP-Flasher/platformio.ini +++ b/ESP32_AP-Flasher/platformio.ini @@ -57,6 +57,7 @@ build_flags = -D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y -D HAS_RGB_LED -D BOARD_HAS_PSRAM + -D SAVE_SPACE -D POWER_NO_SOFT_POWER -D FLASHER_AP_SS=11 -D FLASHER_AP_CLK=9 @@ -93,6 +94,7 @@ build_flags = -D CONFIG_SPIRAM_USE_MALLOC=1 -D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y -D BOARD_HAS_PSRAM + -D SAVE_SPACE -D FLASHER_AP_SS=38 -D FLASHER_AP_CLK=40 -D FLASHER_AP_MOSI=39 @@ -184,6 +186,7 @@ build_flags = ${env.build_flags} -D CORE_DEBUG_LEVEL=0 -D SIMPLE_AP + -D SAVE_SPACE -D FLASHER_AP_SS=5 -D FLASHER_AP_CLK=18 -D FLASHER_AP_MOSI=23 diff --git a/ESP32_AP-Flasher/src/contentmanager.cpp b/ESP32_AP-Flasher/src/contentmanager.cpp index 7d3b3ac6..39134693 100644 --- a/ESP32_AP-Flasher/src/contentmanager.cpp +++ b/ESP32_AP-Flasher/src/contentmanager.cpp @@ -51,7 +51,7 @@ void contentRunner() { // taginfo->wakeupReason = 0; } - if (taginfo->expectedNextCheckin > now - 10 && taginfo->expectedNextCheckin < now + 30 && taginfo->pendingIdle == 0 && taginfo->pending == false) { + if (taginfo->expectedNextCheckin > now - 10 && taginfo->expectedNextCheckin < now + 30 && taginfo->pendingIdle == 0 && taginfo->pendingCount == 0) { int16_t minutesUntilNextUpdate = (taginfo->nextupdate - now) / 60; if (minutesUntilNextUpdate > config.maxsleep) { minutesUntilNextUpdate = config.maxsleep; @@ -240,10 +240,7 @@ void drawNew(const uint8_t mac[8], tagRecord *&taginfo) { jpg2buffer(configFilename, filename, imageParams); } else { - filename = "/current/" + String(hexmac) + ".pending"; - if (!contentFS->exists(filename)) { - filename = "/current/" + String(hexmac) + ".raw"; - } + filename = "/current/" + String(hexmac) + ".raw"; if (contentFS->exists(filename)) { prepareDataAvail(filename, imageParams.dataType, imageParams.lut, mac, cfgobj["timetolive"].as(), true); wsLog("Resending image " + filename); @@ -944,8 +941,6 @@ void drawForecast(String &filename, JsonObject &cfgobj, const tagRecord *taginfo int getImgURL(String &filename, String URL, time_t fetched, imgParam &imageParams, String MAC) { // https://images.klari.net/kat-bw29.jpg - Storage.begin(); - HTTPClient http; logLine("http getImgURL " + URL); http.begin(URL); @@ -1363,7 +1358,6 @@ bool getCalFeed(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgPa void drawQR(String &filename, String qrcontent, String title, tagRecord *&taginfo, imgParam &imageParams) { #ifdef CONTENT_QR TFT_eSprite spr = TFT_eSprite(&tft); - Storage.begin(); const char *text = qrcontent.c_str(); QRCode qrcode; diff --git a/ESP32_AP-Flasher/src/flasher.cpp b/ESP32_AP-Flasher/src/flasher.cpp index 39256e90..e6562188 100644 --- a/ESP32_AP-Flasher/src/flasher.cpp +++ b/ESP32_AP-Flasher/src/flasher.cpp @@ -64,11 +64,11 @@ uint8_t pinsExt[] = {FLASHER_EXT_CLK, FLASHER_EXT_MISO, FLASHER_EXT_MOSI, FLASHE flasher::flasher() { zbs = new ZBS_interface; - Storage.end(); + // Storage.end(); } flasher::~flasher() { delete zbs; - Storage.begin(); + // Storage.begin(); } static uint8_t validatePowerPinCount(int8_t *powerPin, uint8_t pinCount) { diff --git a/ESP32_AP-Flasher/src/ips_display.cpp b/ESP32_AP-Flasher/src/ips_display.cpp index a9f987ee..380a64bb 100644 --- a/ESP32_AP-Flasher/src/ips_display.cpp +++ b/ESP32_AP-Flasher/src/ips_display.cpp @@ -112,7 +112,7 @@ void yellow_ap_display_loop(void) { } if (millis() - last_update >= 1000) { tagRecord* tag = tagDB.at(tftid); - if (tag->pending) { + if (tag->pendingCount > 0) { String filename = tag->filename; fs::File file = contentFS->open(filename); if (!file) { diff --git a/ESP32_AP-Flasher/src/main.cpp b/ESP32_AP-Flasher/src/main.cpp index b0344a56..a5a5c3fb 100644 --- a/ESP32_AP-Flasher/src/main.cpp +++ b/ESP32_AP-Flasher/src/main.cpp @@ -127,7 +127,11 @@ void setup() { #ifdef HAS_RGB_LED rgbIdle(); #endif + +#ifndef SAVE_SPACE TagData::loadParsers("/parsers.json"); +#endif + if (!loadDB("/current/tagDB.json")) { Serial.println("unable to load tagDB, reverting to backup"); loadDB("/current/tagDB.json.bak"); diff --git a/ESP32_AP-Flasher/src/makeimage.cpp b/ESP32_AP-Flasher/src/makeimage.cpp index 3bd5af55..c80e5ec3 100644 --- a/ESP32_AP-Flasher/src/makeimage.cpp +++ b/ESP32_AP-Flasher/src/makeimage.cpp @@ -18,7 +18,6 @@ bool spr_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap) } void jpg2buffer(String filein, String fileout, imgParam &imageParams) { - Storage.begin(); TJpgDec.setSwapBytes(true); TJpgDec.setJpgScale(1); TJpgDec.setCallback(spr_output); @@ -227,7 +226,6 @@ void spr2color(TFT_eSprite &spr, imgParam &imageParams, uint8_t *buffer, size_t void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) { long t = millis(); - Storage.begin(); #ifdef YELLOW_IPS_AP extern uint8_t YellowSense; diff --git a/ESP32_AP-Flasher/src/newproto.cpp b/ESP32_AP-Flasher/src/newproto.cpp index cc1fd57e..9f72495a 100644 --- a/ESP32_AP-Flasher/src/newproto.cpp +++ b/ESP32_AP-Flasher/src/newproto.cpp @@ -6,7 +6,11 @@ #include #include -#include "commstructs.h" +#include +#include +#include +#include + #include "serialap.h" #include "settings.h" #include "storage.h" @@ -19,6 +23,8 @@ extern uint16_t sendBlock(const void* data, const uint16_t len); extern UDPcomm udpsync; +std::vector pendingQueue; +std::mutex queueMutex; void addCRC(void* p, uint8_t len) { uint8_t total = 0; @@ -44,6 +50,7 @@ uint8_t* getDataForFile(fs::File& file) { file.readBytes((char*)ret, fileSize); } else { Serial.printf("malloc failed for file with size %d\n", fileSize); + wsErr("malloc failed while reading file"); util::printHeap(); } return ret; @@ -61,7 +68,9 @@ void prepareCancelPending(const uint8_t dst[8]) { return; } clearPending(taginfo); - + while (dequeueItem(dst)) { + }; + taginfo->pendingCount = countQueueItem(dst); wsSendTaginfo(dst, SYNC_TAGSTATUS); } @@ -74,7 +83,7 @@ void prepareIdleReq(const uint8_t* dst, uint16_t nextCheckin) { pending.attemptsLeft = 10 + config.maxsleep; Serial.printf(">SDA %02X%02X%02X%02X%02X%02X%02X%02X NOP\n", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]); - sendDataAvail(&pending); + queueDataAvail(&pending); } } @@ -92,26 +101,35 @@ void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, const uint8 wsErr("no memory allocation for data"); return; } + + uint8_t md5bytes[16]; + { + MD5Builder md5; + md5.begin(); + md5.add(data, len); + md5.calculate(); + md5.getBytes(md5bytes); + } + memcpy(taginfo->data, data, len); - taginfo->pending = true; + taginfo->pendingCount++; taginfo->len = len; taginfo->pendingIdle = 0; taginfo->filename = String(); taginfo->dataType = dataType; - memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t)); struct pendingData pending = {0}; memcpy(pending.targetMac, dst, 8); pending.availdatainfo.dataSize = len; pending.availdatainfo.dataType = dataType; pending.availdatainfo.nextCheckIn = 0; - pending.availdatainfo.dataVer = millis(); + pending.availdatainfo.dataVer = *((uint64_t*)md5bytes); pending.attemptsLeft = 10; if (taginfo->isExternal) { udpsync.netSendDataAvail(&pending); } else { - sendDataAvail(&pending); + queueDataAvail(&pending); } wsSendTaginfo(dst, SYNC_TAGSTATUS); @@ -137,7 +155,6 @@ bool prepareDataAvail(String& filename, uint8_t dataType, uint8_t dataTypeArgume } filename = "/" + filename; - Storage.begin(); if (!contentFS->exists(filename)) { wsErr("File not found. " + filename); @@ -164,49 +181,30 @@ bool prepareDataAvail(String& filename, uint8_t dataType, uint8_t dataTypeArgume file.close(); uint16_t attempts = 60 * 24; - if (memcmp(md5bytes, taginfo->md5pending, 16) == 0) { - wsLog("new image is the same as current or already pending image. not updating tag."); - wsSendTaginfo(dst, SYNC_TAGSTATUS); - if (contentFS->exists(filename) && resend == false) { - contentFS->remove(filename); - } - return true; - } - if (dataType != DATATYPE_FW_UPDATE) { char dst_path[64]; - sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X.pending\0", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]); + sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X_%lu.pending\0", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0], millis()); if (contentFS->exists(dst_path)) { contentFS->remove(dst_path); } if (resend == false) { contentFS->rename(filename, dst_path); filename = String(dst_path); - wsLog("new image: " + String(dst_path)); + wsLog("new image: " + filename); } time_t now; time(&now); taginfo->pendingIdle = nextCheckin * 60 + 60; clearPending(taginfo); - taginfo->filename = filename; - taginfo->len = filesize; - taginfo->dataType = dataType; - taginfo->pending = true; - memcpy(taginfo->md5pending, md5bytes, sizeof(md5bytes)); } else { - char dst_path[64]; - sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X.raw\0", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]); - contentFS->remove(dst_path); - sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X.pending\0", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]); - contentFS->remove(dst_path); wsLog("firmware upload pending"); clearPending(taginfo); - taginfo->filename = filename; - taginfo->len = filesize; - taginfo->dataType = dataType; - taginfo->pending = true; } + taginfo->filename = filename; + taginfo->len = filesize; + taginfo->dataType = dataType; + taginfo->pendingCount++; struct pendingData pending = {0}; memcpy(pending.targetMac, dst, 8); @@ -219,7 +217,7 @@ bool prepareDataAvail(String& filename, uint8_t dataType, uint8_t dataTypeArgume checkMirror(taginfo, &pending); if (taginfo->isExternal == false) { Serial.printf(">SDA %02X%02X%02X%02X%02X%02X%02X%02X TYPE 0x%02X\n", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0], pending.availdatainfo.dataType); - sendDataAvail(&pending); + queueDataAvail(&pending); } else { udpsync.netSendDataAvail(&pending); } @@ -238,13 +236,11 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) { case DATATYPE_IMG_DIFF: case DATATYPE_IMG_RAW_1BPP: case DATATYPE_IMG_RAW_2BPP: { - Storage.begin(); - char hexmac[17]; mac2hex(pending->targetMac, hexmac); - String filename = "/current/" + String(hexmac) + ".pending"; - String imageUrl = "http://" + remoteIP.toString() + filename; - wsLog("GET " + imageUrl); + String filename = "/current/" + String(hexmac) + "_" + String(millis()) + ".pending"; + String imageUrl = "http://" + remoteIP.toString() + "/getdata?mac=" + String(hexmac); + wsLog("prepareExternalDataAvail GET " + imageUrl); HTTPClient http; logLine("http prepareExternalDataAvail " + imageUrl); http.begin(imageUrl); @@ -293,8 +289,7 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) { taginfo->filename = filename; taginfo->len = filesize; taginfo->dataType = pending->availdatainfo.dataType; - taginfo->pending = true; - memcpy(taginfo->md5pending, md5bytes, sizeof(md5bytes)); + taginfo->pendingCount++; break; } case DATATYPE_NFC_RAW_CONTENT: @@ -316,7 +311,7 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) { WiFiClient* stream = http.getStreamPtr(); stream->readBytes(taginfo->data, len); taginfo->dataType = pending->availdatainfo.dataType; - taginfo->pending = true; + taginfo->pendingCount++; taginfo->len = len; } } @@ -327,16 +322,15 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) { return; } } - // taginfo->contentMode = 12; - // taginfo->nextupdate = 3216153600; checkMirror(taginfo, pending); - sendDataAvail(pending); + queueDataAvail(pending); wsSendTaginfo(pending->targetMac, SYNC_NOSYNC); } } void processBlockRequest(struct espBlockRequest* br) { + uint32_t t = millis(); if (config.runStatus == RUNSTATUS_STOP) { return; } @@ -345,40 +339,38 @@ void processBlockRequest(struct espBlockRequest* br) { return; } - tagRecord* taginfo = tagRecord::findByMAC(br->src); - if (taginfo == nullptr) { - if (config.lock) return; + PendingItem* queueItem = getQueueItem(br->src, br->ver); + if (queueItem == nullptr) { prepareCancelPending(br->src); Serial.printf("blockrequest: couldn't find taginfo %02X%02X%02X%02X%02X%02X%02X%02X\n", br->src[7], br->src[6], br->src[5], br->src[4], br->src[3], br->src[2], br->src[1], br->src[0]); return; } - - if (taginfo->data == nullptr) { - // not cached. open file, cache the data - fs::File file = contentFS->open(taginfo->filename); + if (queueItem->data == nullptr) { + fs::File file = contentFS->open(queueItem->filename); if (!file) { Serial.print("No current file. Canceling request\n"); prepareCancelPending(br->src); return; } - taginfo->data = getDataForFile(file); + queueItem->data = getDataForFile(file); + Serial.println("Reading file " + String(queueItem->filename) + " in " + String(millis() - t) + "ms"); file.close(); } // check if we're not exceeding max blocks (to prevent sendBlock from exceeding its boundary) - uint8_t totalblocks = (taginfo->len / BLOCK_DATA_SIZE); - if (taginfo->len % BLOCK_DATA_SIZE) totalblocks++; + uint8_t totalblocks = (queueItem->len / BLOCK_DATA_SIZE); + if (queueItem->len % BLOCK_DATA_SIZE) totalblocks++; if (br->blockId >= totalblocks) { br->blockId = totalblocks - 1; } - uint32_t len = taginfo->len - (BLOCK_DATA_SIZE * br->blockId); + uint32_t len = queueItem->len - (BLOCK_DATA_SIZE * br->blockId); if (len > BLOCK_DATA_SIZE) len = BLOCK_DATA_SIZE; - uint16_t checksum = sendBlock(taginfo->data + (br->blockId * BLOCK_DATA_SIZE), len); + uint16_t checksum = sendBlock(queueItem->data + (br->blockId * BLOCK_DATA_SIZE), len); char buffer[150]; - sprintf(buffer, "%02X%02X%02X%02X%02X%02X%02X%02X block request %s block %d, len %d checksum %u\0", br->src[7], br->src[6], br->src[5], br->src[4], br->src[3], br->src[2], br->src[1], br->src[0], taginfo->filename.c_str(), br->blockId, len, checksum); + sprintf(buffer, "%02X%02X%02X%02X%02X%02X%02X%02X block request %s block %d, len %d checksum %u\0", br->src[7], br->src[6], br->src[5], br->src[4], br->src[3], br->src[2], br->src[1], br->src[0], queueItem->filename, br->blockId, len, checksum); wsLog((String)buffer); - Serial.printf("filename.c_str(), br->blockId, len, checksum); + Serial.printf("filename, br->blockId, len, checksum); } void processXferComplete(struct espXferComplete* xfc, bool local) { @@ -395,27 +387,34 @@ void processXferComplete(struct espXferComplete* xfc, bool local) { Serial.printf("src[7], xfc->src[6], xfc->src[5], xfc->src[4], xfc->src[3], xfc->src[2], xfc->src[1], xfc->src[0]); } - char src_path[64]; - char dst_path[64]; - sprintf(src_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X.pending\0", xfc->src[7], xfc->src[6], xfc->src[5], xfc->src[4], xfc->src[3], xfc->src[2], xfc->src[1], xfc->src[0]); - sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X.raw\0", xfc->src[7], xfc->src[6], xfc->src[5], xfc->src[4], xfc->src[3], xfc->src[2], xfc->src[1], xfc->src[0]); - if (contentFS->exists(dst_path) && contentFS->exists(src_path)) { - contentFS->remove(dst_path); - } - if (contentFS->exists(src_path)) { - if (config.preview) { - contentFS->rename(src_path, dst_path); - } else { - contentFS->remove(src_path); - } - } - time_t now; time(&now); + + char dst_path[64]; + sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X.raw\0", xfc->src[7], xfc->src[6], xfc->src[5], xfc->src[4], xfc->src[3], xfc->src[2], xfc->src[1], xfc->src[0]); + + uint8_t md5bytes[16]; + PendingItem* queueItem = getQueueItem(xfc->src); + if (queueItem != nullptr) { + if (contentFS->exists(dst_path) && contentFS->exists(queueItem->filename)) { + contentFS->remove(dst_path); + } + if (contentFS->exists(queueItem->filename)) { + if (config.preview && (queueItem->pendingdata.availdatainfo.dataType == DATATYPE_IMG_RAW_2BPP || queueItem->pendingdata.availdatainfo.dataType == DATATYPE_IMG_RAW_1BPP)) { + contentFS->rename(queueItem->filename, String(dst_path)); + } else { + contentFS->remove(queueItem->filename); + } + } + memcpy(md5bytes, &queueItem->pendingdata.availdatainfo.dataVer, sizeof(uint64_t)); + dequeueItem(xfc->src); + } + tagRecord* taginfo = tagRecord::findByMAC(xfc->src); if (taginfo != nullptr) { - memcpy(taginfo->md5, taginfo->md5pending, sizeof(taginfo->md5pending)); clearPending(taginfo); + memcpy(taginfo->md5, md5bytes, sizeof(md5bytes)); + taginfo->pendingCount = countQueueItem(xfc->src); taginfo->wakeupReason = 0; if (taginfo->contentMode == 12 && local == false) { if (contentFS->exists(dst_path)) { @@ -428,6 +427,10 @@ void processXferComplete(struct espXferComplete* xfc, bool local) { taginfo->nextupdate = now; } } + + // more in the queue? + checkQueue(xfc->src); + wsSendTaginfo(xfc->src, SYNC_TAGSTATUS); if (local) udpsync.netProcessXferComplete(xfc); } @@ -451,9 +454,13 @@ void processXferTimeout(struct espXferComplete* xfc, bool local) { tagRecord* taginfo = tagRecord::findByMAC(xfc->src); if (taginfo != nullptr) { taginfo->pendingIdle = 60; - memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t)); clearPending(taginfo); + while (dequeueItem(xfc->src)) { + }; } + + checkQueue(xfc->src); + wsSendTaginfo(xfc->src, SYNC_TAGSTATUS); if (local) udpsync.netProcessXferTimeout(xfc); } @@ -469,7 +476,7 @@ void processDataReq(struct espAvailDataReq* eadr, bool local, IPAddress remoteIP if (config.lock == 1 || (config.lock == 2 && eadr->adr.wakeupReason != WAKEUP_REASON_FIRSTBOOT)) return; taginfo = new tagRecord; memcpy(taginfo->mac, eadr->src, sizeof(taginfo->mac)); - taginfo->pending = false; + taginfo->pendingCount = 0; tagDB.push_back(taginfo); } time_t now; @@ -505,9 +512,7 @@ void processDataReq(struct espAvailDataReq* eadr, bool local, IPAddress remoteIP if (eadr->adr.lastPacketRSSI != 0) { if (eadr->adr.wakeupReason >= 0xE0) { - if (!taginfo->pending) taginfo->nextupdate = 0; - memset(taginfo->md5, 0, 16 * sizeof(uint8_t)); - memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t)); + if (taginfo->pendingCount == 0) taginfo->nextupdate = 0; if (local) { const char* reason = ""; @@ -523,13 +528,6 @@ void processDataReq(struct espAvailDataReq* eadr, bool local, IPAddress remoteIP logLine(buffer); } } - - /* - if (local && taginfo->batteryMv != eadr->adr.batteryMv) { - sprintf(buffer, "%02X%02X%02X%02X%02X%02X%02X%02X battery went from %.2fV to %.2fV", eadr->src[7], eadr->src[6], eadr->src[5], eadr->src[4], eadr->src[3], eadr->src[2], eadr->src[1], eadr->src[0], static_cast(taginfo->batteryMv) / 1000.0, static_cast(eadr->adr.batteryMv) / 1000.0); - logLine(buffer); - } - */ taginfo->LQI = eadr->adr.lastPacketLQI; taginfo->hwType = eadr->adr.hwType; @@ -545,8 +543,6 @@ void processDataReq(struct espAvailDataReq* eadr, bool local, IPAddress remoteIP if (local) { sprintf(buffer, "src[7], eadr->src[6], eadr->src[5], eadr->src[4], eadr->src[3], eadr->src[2], eadr->src[1], eadr->src[0]); Serial.print(buffer); - } else { - // sprintf(buffer, "src[7], eadr->src[6], eadr->src[5], eadr->src[4], eadr->src[3], eadr->src[2], eadr->src[1], eadr->src[0]); } if (local) { @@ -571,17 +567,17 @@ void processTagReturnData(struct espTagReturnData* trd, uint8_t len, bool local) sprintf(buffer, "TRD Data: len=%d, type=%d, ver=0x%08X\n", payloadLength, trd->returnData.dataType, trd->returnData.dataVer); wsLog((String)buffer); +#ifndef SAVE_SPACE TagData::parse(trd->src, trd->returnData.dataType, trd->returnData.data, payloadLength); +#endif } void refreshAllPending() { for (int16_t c = 0; c < tagDB.size(); c++) { tagRecord* taginfo = tagDB.at(c); - if (taginfo->pending && taginfo->version == 0) { + if (taginfo->pendingCount > 0 && taginfo->version == 0) { clearPending(taginfo); taginfo->nextupdate = 0; - memset(taginfo->md5, 0, 16 * sizeof(uint8_t)); - memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t)); wsSendTaginfo(taginfo->mac, SYNC_TAGSTATUS); } } @@ -592,8 +588,6 @@ void updateContent(const uint8_t* dst) { if (taginfo != nullptr) { clearPending(taginfo); taginfo->nextupdate = 0; - memset(taginfo->md5, 0, 16 * sizeof(uint8_t)); - memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t)); wsSendTaginfo(taginfo->mac, SYNC_TAGSTATUS); } } @@ -622,7 +616,7 @@ bool sendAPSegmentedData(const uint8_t* dst, String data, uint16_t icons, bool i pending.attemptsLeft = 120; Serial.printf(">AP Segmented Data %02X%02X%02X%02X%02X%02X%02X%02X\n\0", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]); if (local) { - return sendDataAvail(&pending); + return queueDataAvail(&pending); } else { udpsync.netSendDataAvail(&pending); return true; @@ -640,7 +634,7 @@ bool showAPSegmentedInfo(const uint8_t* dst, bool local) { pending.attemptsLeft = 120; Serial.printf(">SDA %02X%02X%02X%02X%02X%02X%02X%02X\n\0", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]); if (local) { - return sendDataAvail(&pending); + return queueDataAvail(&pending); } else { udpsync.netSendDataAvail(&pending); return true; @@ -659,8 +653,15 @@ bool sendTagCommand(const uint8_t* dst, uint8_t cmd, bool local, const uint8_t* } pending.attemptsLeft = 120; Serial.printf(">Tag CMD %02X%02X%02X%02X%02X%02X%02X%02X\n\0", dst[7], dst[6], dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]); + + tagRecord* taginfo = tagRecord::findByMAC(dst); + if (taginfo != nullptr) { + taginfo->pendingCount++; + wsSendTaginfo(taginfo->mac, SYNC_TAGSTATUS); + } + if (local) { - return sendDataAvail(&pending); + return queueDataAvail(&pending); } else { udpsync.netSendDataAvail(&pending); return true; @@ -674,7 +675,7 @@ void updateTaginfoitem(struct TagInfo* taginfoitem, IPAddress remoteIP) { if (config.lock) return; taginfo = new tagRecord; memcpy(taginfo->mac, taginfoitem->mac, sizeof(taginfo->mac)); - taginfo->pending = false; + taginfo->pendingCount = 0; tagDB.push_back(taginfo); } tagRecord initialTagInfo = *taginfo; @@ -687,7 +688,7 @@ void updateTaginfoitem(struct TagInfo* taginfoitem, IPAddress remoteIP) { case SYNC_TAGSTATUS: taginfo->lastseen = taginfoitem->lastseen; taginfo->nextupdate = taginfoitem->nextupdate; - taginfo->pending = taginfoitem->pending; + taginfo->pendingCount = taginfoitem->pendingCount; taginfo->expectedNextCheckin = taginfoitem->expectedNextCheckin; taginfo->hwType = taginfoitem->hwType; taginfo->wakeupReason = taginfoitem->wakeupReason; @@ -739,24 +740,23 @@ bool checkMirror(struct tagRecord* taginfo, struct pendingData* pending) { taginfo2->len = taginfo->len; taginfo2->data = taginfo->data; // copy buffer pointer taginfo2->dataType = taginfo->dataType; - taginfo2->pending = true; + taginfo2->pendingCount++; taginfo2->nextupdate = 3216153600; - memcpy(taginfo2->md5pending, taginfo->md5pending, sizeof(taginfo->md5pending)); struct pendingData pending2 = {0}; memcpy(pending2.targetMac, taginfo2->mac, 8); pending2.availdatainfo.dataType = taginfo2->dataType; - pending2.availdatainfo.dataVer = *((uint64_t*)taginfo2->md5pending); + pending2.availdatainfo.dataVer = pending->availdatainfo.dataVer; pending2.availdatainfo.dataSize = taginfo2->len; pending2.availdatainfo.dataTypeArgument = pending->availdatainfo.dataTypeArgument; pending2.availdatainfo.nextCheckIn = pending->availdatainfo.nextCheckIn; pending2.attemptsLeft = pending->attemptsLeft; if (taginfo2->isExternal == false) { - sendDataAvail(&pending2); + queueDataAvail(&pending2); } else { char dst_path[64]; - sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X.pending\0", taginfo2->mac[7], taginfo2->mac[6], taginfo2->mac[5], taginfo2->mac[4], taginfo2->mac[3], taginfo2->mac[2], taginfo2->mac[1], taginfo2->mac[0]); + sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X_%lu.pending", taginfo2->mac[7], taginfo2->mac[6], taginfo2->mac[5], taginfo2->mac[4], taginfo2->mac[3], taginfo2->mac[2], taginfo2->mac[1], taginfo2->mac[0], millis()); xSemaphoreTake(fsMutex, portMAX_DELAY); File file = contentFS->open(dst_path, "w"); if (file) { @@ -775,3 +775,123 @@ bool checkMirror(struct tagRecord* taginfo, struct pendingData* pending) { } return false; } + +void enqueueItem(struct PendingItem& item) { + std::lock_guard lock(queueMutex); + pendingQueue.push_back(item); +} + +bool dequeueItem(const uint8_t* targetMac) { + return dequeueItem(targetMac, 0); +} + +bool dequeueItem(const uint8_t* targetMac, const uint64_t dataVer) { + std::lock_guard lock(queueMutex); + auto it = std::find_if(pendingQueue.begin(), pendingQueue.end(), + [targetMac, dataVer](const PendingItem& item) { + bool macMatches = memcmp(item.pendingdata.targetMac, targetMac, sizeof(item.pendingdata.targetMac)) == 0; + bool dataVerMatches = (dataVer == 0) || (dataVer == item.pendingdata.availdatainfo.dataVer); + return macMatches && dataVerMatches; + }); + if (it != pendingQueue.end()) { + if (it->data != nullptr) { + int datacount = 0; + for (const PendingItem& item : pendingQueue) { + if (item.data == it->data) { + datacount++; + } + } + if (datacount == 1) { + free(it->data); + } + it->data = nullptr; + } + pendingQueue.erase(it); + return true; + } + return false; +} + +uint16_t countQueueItem(const uint8_t* targetMac) { + std::unique_lock lock(queueMutex); + int count = std::count_if(pendingQueue.begin(), pendingQueue.end(), + [targetMac](const PendingItem& item) { + return memcmp(item.pendingdata.targetMac, targetMac, sizeof(item.pendingdata.targetMac)) == 0; + }); + return count; +} + +PendingItem* getQueueItem(const uint8_t* targetMac) { + return getQueueItem(targetMac, 0); +} + +PendingItem* getQueueItem(const uint8_t* targetMac, const uint64_t dataVer) { + auto it = std::find_if(pendingQueue.begin(), pendingQueue.end(), + [targetMac, dataVer](const PendingItem& item) { + bool macMatches = memcmp(item.pendingdata.targetMac, targetMac, sizeof(item.pendingdata.targetMac)) == 0; + bool dataVerMatches = (dataVer == 0) || (dataVer == item.pendingdata.availdatainfo.dataVer); + return macMatches && dataVerMatches; + }); + if (it != pendingQueue.end()) { + return &(*it); + } else { + return nullptr; + } +} + +void checkQueue(const uint8_t* targetMac) { + uint16_t queueCount; + queueCount = countQueueItem(targetMac); + if (queueCount > 0) { + Serial.printf("more from queue: total %d elements\n", pendingQueue.size()); + PendingItem* queueItem = getQueueItem(targetMac); + if (queueItem == nullptr) { + return; + } + if (queueCount > 1) queueItem->pendingdata.availdatainfo.nextCheckIn = 0; + sendDataAvail(&queueItem->pendingdata); + } +} + +bool queueDataAvail(struct pendingData* pending) { + PendingItem newPending; + newPending.pendingdata.availdatainfo = pending->availdatainfo; + newPending.pendingdata.attemptsLeft = pending->attemptsLeft; + std::copy(pending->targetMac, pending->targetMac + sizeof(pending->targetMac), newPending.pendingdata.targetMac); + + tagRecord* taginfo = tagRecord::findByMAC(pending->targetMac); + + if (taginfo == nullptr) { + return false; + } + + std::strcpy(newPending.filename, taginfo->filename.c_str()); + if (taginfo->data != nullptr) { + // move data pointer + newPending.data = taginfo->data; + taginfo->data = nullptr; + } else { + newPending.data = nullptr; + + // optional: read data early, don't wait for block request. + fs::File file = contentFS->open(newPending.filename); + if (file) { + newPending.data = getDataForFile(file); + Serial.println("Reading file " + String(newPending.filename)); + file.close(); + } + } + newPending.len = taginfo->len; + + if (countQueueItem(pending->targetMac) == 0) { + enqueueItem(newPending); + // first in line, send to tag + Serial.printf("queue item added, first in line, total %d elements\n", pendingQueue.size()); + sendDataAvail(pending); + } else { + enqueueItem(newPending); + Serial.printf("queue item added, total %d elements\n", pendingQueue.size()); + } + + return true; +} diff --git a/ESP32_AP-Flasher/src/serialap.cpp b/ESP32_AP-Flasher/src/serialap.cpp index c415c8ce..fcda1401 100644 --- a/ESP32_AP-Flasher/src/serialap.cpp +++ b/ESP32_AP-Flasher/src/serialap.cpp @@ -1,8 +1,8 @@ -#include "serialap.h" - #include #include +#include "serialap.h" + #include "commstructs.h" #include "contentmanager.h" #include "flasher.h" @@ -15,7 +15,6 @@ #include "zbs_interface.h" QueueHandle_t rxCmdQueue; - SemaphoreHandle_t txActive; // If a command is sent, it will wait for a reply here @@ -28,7 +27,7 @@ volatile uint8_t cmdReplyValue = CMD_REPLY_WAIT; #define AP_SERIAL_PORT Serial1 volatile bool rxSerialStopTask2 = false; - uint8_t channelList[6]; +uint8_t channelList[6]; struct espSetChannelPower curChannel = {0, 11, 10}; #define RX_CMD_RQB 0x01 @@ -43,8 +42,6 @@ struct espSetChannelPower curChannel = {0, 11, 10}; volatile uint32_t lastAPActivity = 0; struct APInfoS apInfo; -extern uint8_t* getDataForFile(File& file); - struct rxCmd { uint8_t* data; uint8_t len; @@ -98,7 +95,7 @@ bool waitCmdReply() { break; case CMD_REPLY_ACK: lastAPActivity = millis(); - if(apInfo.isOnline == false) + if (apInfo.isOnline == false) setAPstate(true, AP_STATE_ONLINE); return true; break; @@ -150,8 +147,7 @@ void setAPstate(bool isOnline, uint8_t state) { CRGB::Yellow, CRGB::Aqua, CRGB::Red, - CRGB::YellowGreen - }; + CRGB::YellowGreen}; rgbIdleColor = colorMap[state]; rgbIdlePeriod = (isOnline ? 767 : 255); #endif @@ -222,6 +218,7 @@ blksend: txEnd(); return bd->checksum; } + bool sendDataAvail(struct pendingData* pending) { if (!apInfo.isOnline) return false; if (!txStart()) return false; @@ -462,7 +459,7 @@ void rxSerialTask(void* parameter) { packetp = (uint8_t*)calloc(sizeof(struct espBlockRequest) + 8, 1); memset(cmdbuffer, 0x00, 4); lastAPActivity = millis(); - if(apInfo.isOnline == false) + if (apInfo.isOnline == false) setAPstate(true, AP_STATE_ONLINE); } if (strncmp(cmdbuffer, "ADR>", 4) == 0) { @@ -472,7 +469,7 @@ void rxSerialTask(void* parameter) { packetp = (uint8_t*)calloc(sizeof(struct espAvailDataReq) + 8, 1); memset(cmdbuffer, 0x00, 4); lastAPActivity = millis(); - if(apInfo.isOnline == false) + if (apInfo.isOnline == false) setAPstate(true, AP_STATE_ONLINE); } if (strncmp(cmdbuffer, "XFC>", 4) == 0) { @@ -496,7 +493,7 @@ void rxSerialTask(void* parameter) { packetp = (uint8_t*)calloc(sizeof(struct espTagReturnData) + 8, 1); memset(cmdbuffer, 0x00, 4); lastAPActivity = millis(); - if(apInfo.isOnline == false) + if (apInfo.isOnline == false) setAPstate(true, AP_STATE_ONLINE); } break; @@ -535,7 +532,7 @@ void rxSerialTask(void* parameter) { case ZBS_RX_WAIT_TAG_RETURN_DATA: { packetp[pktindex] = lastchar; pktindex++; - if ((pktindex > 10) && (pktindex >= (packetp[9]+10))) { + if ((pktindex > 10) && (pktindex >= (packetp[9] + 10))) { addRXQueue(packetp, pktindex, RX_CMD_TRD); RXState = ZBS_RX_WAIT_HEADER; } @@ -582,7 +579,7 @@ void rxSerialTask(void* parameter) { charindex++; if (charindex == 2) { RXState = ZBS_RX_WAIT_HEADER; - apInfo.pending = (uint8_t)strtoul(cmdbuffer, NULL, 16); + apInfo.pendingBuffer = (uint8_t)strtoul(cmdbuffer, NULL, 16); } break; case ZBS_RX_WAIT_NOP: @@ -772,7 +769,6 @@ void APTask(void* parameter) { updateContent(apInfo.mac); } - uint16_t fsversion; if (FLASHER_AP_MOSI != -1) { fsversion = getAPUpdateVersion(apInfo.type); @@ -901,7 +897,7 @@ void APTask(void* parameter) { if (!reply) { attempts++; } else { - if(apInfo.isOnline == false) + if (apInfo.isOnline == false) setAPstate(true, AP_STATE_ONLINE); attempts = 0; } @@ -913,7 +909,7 @@ void APTask(void* parameter) { #ifdef HAS_RGB_LED showColorPattern(CRGB::Yellow, CRGB::Yellow, CRGB::Red); #endif - lastAPActivity = millis();// we set this to retrigger a recovery in AP_ACTIVITY_MAX_INTERVAL seconds + lastAPActivity = millis(); // we set this to retrigger a recovery in AP_ACTIVITY_MAX_INTERVAL seconds } else { setAPstate(true, AP_STATE_ONLINE); attempts = 0; diff --git a/ESP32_AP-Flasher/src/storage.cpp b/ESP32_AP-Flasher/src/storage.cpp index 545141ce..6ca786b3 100644 --- a/ESP32_AP-Flasher/src/storage.cpp +++ b/ESP32_AP-Flasher/src/storage.cpp @@ -176,7 +176,6 @@ void DynStorage::end() { } void listDir(fs::FS& fs, const char* dirname, uint8_t levels) { - Storage.begin(); Serial.printf(" \n "); Serial.printf("Listing directory: %s\n", dirname); diff --git a/ESP32_AP-Flasher/src/tag_db.cpp b/ESP32_AP-Flasher/src/tag_db.cpp index a19ea83a..c3c83979 100644 --- a/ESP32_AP-Flasher/src/tag_db.cpp +++ b/ESP32_AP-Flasher/src/tag_db.cpp @@ -103,7 +103,7 @@ void fillNode(JsonObject& tag, const tagRecord* taginfo) { tag["lastseen"] = taginfo->lastseen; tag["nextupdate"] = taginfo->nextupdate; tag["nextcheckin"] = taginfo->expectedNextCheckin; - tag["pending"] = taginfo->pending; + tag["pending"] = taginfo->pendingCount; tag["alias"] = taginfo->alias; tag["contentMode"] = taginfo->contentMode; tag["LQI"] = taginfo->LQI; @@ -128,7 +128,6 @@ void saveDB(const String& filename) { const long t = millis(); - Storage.begin(); xSemaphoreTake(fsMutex, portMAX_DELAY); fs::File existingFile = contentFS->open(filename, "r"); @@ -174,7 +173,6 @@ bool loadDB(const String& filename) { Serial.println("reading DB from " + String(filename)); const long t = millis(); - Storage.begin(); fs::File readfile = contentFS->open(filename, "r"); if (!readfile) { Serial.println("loadDB: Failed to open file"); @@ -206,14 +204,13 @@ bool loadDB(const String& filename) { taginfo->md5[i] = strtoul(md5.substring(i * 2, i * 2 + 2).c_str(), NULL, 16); } } - memcpy(taginfo->md5pending, taginfo->md5, sizeof(taginfo->md5)); taginfo->lastseen = (uint32_t)tag["lastseen"]; taginfo->nextupdate = (uint32_t)tag["nextupdate"]; taginfo->expectedNextCheckin = (uint32_t)tag["nextcheckin"]; if (taginfo->expectedNextCheckin < now) { taginfo->expectedNextCheckin = now + 1800; } - taginfo->pending = false; + taginfo->pendingCount = 0; taginfo->alias = tag["alias"].as(); taginfo->contentMode = tag["contentMode"]; taginfo->LQI = tag["LQI"]; @@ -303,11 +300,9 @@ void clearPending(tagRecord* taginfo) { } taginfo->data = nullptr; } - taginfo->pending = false; } void initAPconfig() { - Storage.begin(); DynamicJsonDocument APconfig(500); File configFile = contentFS->open("/current/apconfig.json", "r"); if (configFile) { diff --git a/ESP32_AP-Flasher/src/tagdata.cpp b/ESP32_AP-Flasher/src/tagdata.cpp index 41f8aef0..85806755 100644 --- a/ESP32_AP-Flasher/src/tagdata.cpp +++ b/ESP32_AP-Flasher/src/tagdata.cpp @@ -1,5 +1,7 @@ #include "tagdata.h" +#ifndef SAVE_SPACE + #include "tag_db.h" #include "util.h" @@ -8,7 +10,6 @@ std::unordered_map TagData::parsers = {}; void TagData::loadParsers(const String& filename) { const long start = millis(); - Storage.begin(); fs::File file = contentFS->open(filename, "r"); if (!file) { return; @@ -143,3 +144,5 @@ void TagData::parse(const uint8_t src[8], const size_t id, const uint8_t* data, Serial.printf("Set %s to %s\n", varName.c_str(), value.c_str()); } } + +#endif \ No newline at end of file diff --git a/ESP32_AP-Flasher/src/web.cpp b/ESP32_AP-Flasher/src/web.cpp index 95f11dda..caff3023 100644 --- a/ESP32_AP-Flasher/src/web.cpp +++ b/ESP32_AP-Flasher/src/web.cpp @@ -169,7 +169,7 @@ void wsSendTaginfo(const uint8_t *mac, uint8_t syncMode) { if (syncMode == SYNC_TAGSTATUS) { taginfoitem.lastseen = taginfo->lastseen; taginfoitem.nextupdate = taginfo->nextupdate; - taginfoitem.pending = taginfo->pending; + taginfoitem.pendingCount = taginfo->pendingCount; taginfoitem.expectedNextCheckin = taginfo->expectedNextCheckin; taginfoitem.hwType = taginfo->hwType; taginfoitem.wakeupReason = taginfo->wakeupReason; @@ -215,7 +215,6 @@ uint8_t wsClientCount() { void init_web() { wsMutex = xSemaphoreCreateMutex(); - Storage.begin(); WiFi.mode(WIFI_STA); WiFi.setTxPower(static_cast(config.wifiPower)); @@ -240,7 +239,6 @@ void init_web() { server.serveStatic("/current", *contentFS, "/current/").setCacheControl("max-age=604800"); server.serveStatic("/tagtypes", *contentFS, "/tagtypes/").setCacheControl("max-age=600"); - server.serveStatic("/", *contentFS, "/www/").setDefaultFile("index.html"); server.on( "/imgupload", HTTP_POST, [](AsyncWebServerRequest *request) { @@ -274,12 +272,19 @@ void init_web() { String dst = request->getParam("mac")->value(); uint8_t mac[8]; if (hex2mac(dst, mac)) { - const tagRecord *taginfo = tagRecord::findByMAC(mac); + tagRecord *taginfo = tagRecord::findByMAC(mac); if (taginfo != nullptr) { - if (taginfo->pending == true) { - request->send_P(200, "application/octet-stream", taginfo->data, taginfo->len); - return; + if (taginfo->data == nullptr) { + fs::File file = contentFS->open(taginfo->filename); + if (!file) { + request->send(404, "text/plain", "File not found"); + return; + } + taginfo->data = getDataForFile(file); + file.close(); } + request->send_P(200, "application/octet-stream", taginfo->data, taginfo->len); + return; } } } @@ -311,8 +316,6 @@ void init_web() { if (request->hasParam("invert", true)) { taginfo->invert = atoi(request->getParam("invert", true)->value().c_str()); } - // memset(taginfo->md5, 0, 16 * sizeof(uint8_t)); - // memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t)); wsSendTaginfo(mac, SYNC_USERCFG); // saveDB("/current/tagDB.json"); request->send(200, "text/plain", "Ok, saved"); @@ -337,7 +340,9 @@ void init_web() { } if (strcmp(cmdValue, "clear") == 0) { clearPending(taginfo); - memcpy(taginfo->md5pending, taginfo->md5, sizeof(taginfo->md5pending)); + while (dequeueItem(mac)) { + }; + taginfo->pendingCount = countQueueItem(mac); wsSendTaginfo(mac, SYNC_TAGSTATUS); } if (strcmp(cmdValue, "refresh") == 0) { @@ -660,6 +665,8 @@ void init_web() { request->send(404); }); + server.serveStatic("/", *contentFS, "/www/").setDefaultFile("index.html"); + DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "content-type"); diff --git a/ESP32_AP-Flasher/wwwroot/edit.html b/ESP32_AP-Flasher/wwwroot/edit.html index 90d2f95f..fcd15445 100644 --- a/ESP32_AP-Flasher/wwwroot/edit.html +++ b/ESP32_AP-Flasher/wwwroot/edit.html @@ -410,7 +410,7 @@ if (ext == "raw" || ext == "pending") { let storedTagTypes = localStorage.getItem("tagTypes"); let tagTypes = JSON.parse(storedTagTypes); - let mac = name.substring(name.lastIndexOf('/') + 1); + let mac = name.substring(name.lastIndexOf('/') + 1, name.lastIndexOf('/') + 17); let targetDiv = null; if (window.opener && !window.opener.closed) { targetDiv = window.opener.document.querySelector(`div[data-mac="${mac}"]`); diff --git a/ESP32_AP-Flasher/wwwroot/index.html b/ESP32_AP-Flasher/wwwroot/index.html index 7e93d334..eafdc2d8 100644 --- a/ESP32_AP-Flasher/wwwroot/index.html +++ b/ESP32_AP-Flasher/wwwroot/index.html @@ -6,8 +6,9 @@ Open EPaper Link Access Point + - + @@ -525,8 +526,6 @@ style="display: none; position: absolute; background: white; border: 1px solid gray; padding: 0; list-style: none;"> - - diff --git a/ESP32_AP-Flasher/wwwroot/main.css b/ESP32_AP-Flasher/wwwroot/main.css index b700454c..886761f2 100644 --- a/ESP32_AP-Flasher/wwwroot/main.css +++ b/ESP32_AP-Flasher/wwwroot/main.css @@ -626,12 +626,13 @@ select { .pendingicon { width: 20px; height: 20px; - background-color: rgb(7, 174, 230); + background-color: rgb(1, 154, 201); font-size: 1.2em; text-align: center; font-weight: bold; display: none; vertical-align: top; + color: white; } .warningicon { diff --git a/ESP32_AP-Flasher/wwwroot/main.js b/ESP32_AP-Flasher/wwwroot/main.js index 7990b67c..ee4a8599 100644 --- a/ESP32_AP-Flasher/wwwroot/main.js +++ b/ESP32_AP-Flasher/wwwroot/main.js @@ -40,7 +40,6 @@ let otamodule; let socket; let finishedInitialLoading = false; let getTagtypeBusy = false; -let webVersion = "0"; const loadConfig = new Event("loadConfig"); window.addEventListener("loadConfig", function () { @@ -57,14 +56,18 @@ window.addEventListener("loadConfig", function () { }); window.addEventListener("load", function () { - initVersionInfo(); + window.dispatchEvent(loadConfig); initTabs(); fetch('/content_cards.json') .then(response => response.json()) .then(data => { cardconfig = data; - loadTags(0); - connect(); + loadTags(0) + .then(() => { + finishedInitialLoading = true; + connect(); + }) + .catch(error => showMessage('loadTags error: ' + error)); setInterval(updatecards, 1000); }) .catch(error => { @@ -72,26 +75,17 @@ window.addEventListener("load", function () { alert("I can't load /www/content_cards.json.\r\nHave you upload it to the data partition?"); }); - window.dispatchEvent(loadConfig); - dropUpload(); populateTimes($('#apcnight1')); populateTimes($('#apcnight2')); -}); -function initVersionInfo() { - fetch('/version.txt') - .then(response => { - return response.text(); - }) - .then(data => { - webVersion = data; - console.log(webVersion); - }) - .catch(error => { - console.error('Fetch error:', error); - }); -} + document.addEventListener('DOMContentLoaded', function () { + var faviconLink = document.createElement('link'); + faviconLink.rel = 'icon'; + faviconLink.href = 'favicon.ico'; + document.head.appendChild(faviconLink); + }); +}); /* tabs */ let activeTab = ''; @@ -119,14 +113,14 @@ function initTabs() { }; function loadTags(pos) { - fetch("/get_db?pos=" + pos) + return fetch("/get_db?pos=" + pos) .then(response => response.json()) .then(data => { processTags(data.tags); - if (data.continu && data.continu > pos) loadTags(data.continu); - finishedInitialLoading = true; - }) - //.catch(error => showMessage('loadTags error: ' + error)); + if (data.continu && data.continu > pos) { + return loadTags(data.continu); + } + }); } function formatUptime(seconds) { @@ -280,7 +274,24 @@ function processTags(tagArray) { div.dataset.ver = element.ver; $('#tag' + localTagmac + ' .resolution').innerHTML += ` fw:${element.ver} 0x${element.ver.toString(16)}`; } + + if (!apConfig.preview || element.contentMode == 20) { + $('#tag' + tagmac + ' .tagimg').style.display = 'none' + } else if (div.dataset.hash != element.hash && div.dataset.hwtype > -1) { + let cachetag = element.hash; + if (element.hash != '00000000000000000000000000000000') { + if (element.isexternal && element.contentMode == 12) { + loadImage(tagmac, 'http://' + tagDB[tagmac].apip + '/current/' + tagmac + '.raw?' + cachetag); + } else { + loadImage(tagmac, '/current/' + tagmac + '.raw?' + cachetag); + } + } else { + $('#tag' + tagmac + ' .tagimg').style.display = 'none' + } + div.dataset.hash = element.hash; + } })(); + let statusline = ""; if (element.RSSI != 100) { if (element.ch > 0) statusline += `CH ${element.ch}, `; @@ -293,29 +304,13 @@ function processTags(tagArray) { } $('#tag' + tagmac + ' .received').innerHTML = statusline; $('#tag' + tagmac + ' .received').style.opacity = "1"; + } else { $('#tag' + tagmac + ' .model').innerHTML = "waiting for hardware type"; $('#tag' + tagmac + ' .received').style.opacity = "0"; $('#tag' + tagmac + ' .resolution').innerHTML = ""; } - if (!apConfig.preview || element.contentMode == 20) { - $('#tag' + tagmac + ' .tagimg').style.display = 'none' - } else if (div.dataset.hash != element.hash && div.dataset.hwtype > -1) { - let cachetag = element.hash; - if (element.hash != '00000000000000000000000000000000') { - //cachetag = Math.random(); - if (element.isexternal && element.contentMode == 12) { - loadImage(tagmac, 'http://' + tagDB[tagmac].apip + '/current/' + tagmac + '.raw?' + cachetag); - } else { - loadImage(tagmac, '/current/' + tagmac + '.raw?' + cachetag); - } - } else { - $('#tag' + tagmac + ' .tagimg').style.display = 'none' - } - div.dataset.hash = element.hash; - } - if (element.nextupdate > 1672531200 && element.nextupdate != 3216153600) { const date = new Date(element.nextupdate * 1000); const options = { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }; @@ -379,6 +374,7 @@ function processTags(tagArray) { break; } $('#tag' + tagmac + ' .pendingicon').style.display = (element.pending ? 'inline-block' : 'none'); + $('#tag' + tagmac + ' .pendingicon').innerHTML = element.pending; div.classList.add("tagflash"); (function (tagmac) { setTimeout(function () { $('#tag' + tagmac).classList.remove("tagflash"); }, 1400); @@ -438,9 +434,9 @@ function updatecards() { $('#dashboardTimeout').innerHTML = timeoutcount; } -$('#clearlog').onclick = function () { +$('#clearlog').addEventListener("click", (event) => { $('#messages').innerHTML = ''; -} +}); document.querySelectorAll('.closebtn').forEach(button => { button.addEventListener('click', (event) => { @@ -1301,7 +1297,7 @@ async function getTagtype(hwtype) { try { getTagtypeBusy = true; tagTypes[hwtype] = { busy: true }; - const response = await fetch('/tagtypes/' + hwtype.toString(16).padStart(2, '0').toUpperCase() + '.json?' + webVersion); + const response = await fetch('/tagtypes/' + hwtype.toString(16).padStart(2, '0').toUpperCase() + '.json'); if (!response.ok) { let data = { name: 'unknown id ' + hwtype.toString(16), width: 0, height: 0, bpp: 0, rotatebuffer: 0, colortable: [], busy: false }; tagTypes[hwtype] = data; @@ -1539,7 +1535,6 @@ function populateAPCard(msg) { } }); - // $('#ap' + apid + ' .apversion').innerHTML = msg.version; if (activeTab == 'aptab') { populateAPInfo(apip); }