diff --git a/ESP32_AP-Flasher/data/fonts/weathericons70.vlw b/ESP32_AP-Flasher/data/fonts/weathericons70.vlw index 1807f1ec..689a14cd 100644 Binary files a/ESP32_AP-Flasher/data/fonts/weathericons70.vlw and b/ESP32_AP-Flasher/data/fonts/weathericons70.vlw differ diff --git a/ESP32_AP-Flasher/data/fonts/weathericons78.vlw b/ESP32_AP-Flasher/data/fonts/weathericons78.vlw index 073f249c..a34ee456 100644 Binary files a/ESP32_AP-Flasher/data/fonts/weathericons78.vlw and b/ESP32_AP-Flasher/data/fonts/weathericons78.vlw differ diff --git a/ESP32_AP-Flasher/data/www/content_cards.json b/ESP32_AP-Flasher/data/www/content_cards.json index 078c4306..aa9e56f6 100644 --- a/ESP32_AP-Flasher/data/www/content_cards.json +++ b/ESP32_AP-Flasher/data/www/content_cards.json @@ -6,7 +6,8 @@ "hwtype": [ 0, 1, - 2 + 2, + 17 ], "param": [ { @@ -31,6 +32,7 @@ 0, 1, 2, + 17, 240 ], "param": [] @@ -43,6 +45,7 @@ 0, 1, 2, + 17, 240 ], "param": [ @@ -60,7 +63,8 @@ "hwtype": [ 0, 1, - 2 + 2, + 17 ] } ] @@ -73,6 +77,7 @@ 0, 1, 2, + 17, 240 ], "param": [ @@ -90,7 +95,8 @@ "hwtype": [ 0, 1, - 2 + 2, + 17 ] } ] @@ -103,6 +109,7 @@ 0, 1, 2, + 17, 240 ], "param": [ @@ -132,7 +139,8 @@ "desc": "Weather forecast for the next five days. Weather data by Open-Meteo.com", "hwtype": [ 1, - 2 + 2, + 17 ], "param": [ { @@ -161,7 +169,8 @@ "desc": "Gets an RSS feed, and display the first few lines of it", "hwtype": [ 1, - 2 + 2, + 17 ], "param": [ { @@ -191,7 +200,8 @@ "hwtype": [ 0, 1, - 2 + 2, + 17 ], "param": [ { @@ -215,7 +225,8 @@ "hwtype": [ 0, 1, - 2 + 2, + 17 ], "param": [ { @@ -238,7 +249,8 @@ "desc": "Displays the current and upcoming appointments (next 24 hours) from a Google calendar. To let this work, you need a small Google Apps Script to interface with your calendar. See documentation on github how to do that", "hwtype": [ 1, - 2 + 2, + 17 ], "param": [ { @@ -269,6 +281,7 @@ 0, 1, 2, + 17, 240 ], "param": [ @@ -289,7 +302,7 @@ { "id": 13, "name": "Set segments", - "desc": "Work in progress", + "desc": "Used for debugging. Work in progress", "hwtype": [ 240 ], @@ -313,5 +326,23 @@ "type": "text" } ] - } + }, + { + "id": 14, + "name": "Set NFC URL", + "desc": "Send the URL to the NFC chip. The URL is transmitted to a NFC reader (like your phone) if you hold it next to the tag", + "hwtype": [ + 0, + 17 + ], + "capabilities": 64, + "param": [ + { + "key": "url", + "name": "URL", + "desc": "Full URL", + "type": "text" + } + ] + } ] \ No newline at end of file diff --git a/ESP32_AP-Flasher/data/www/main.js b/ESP32_AP-Flasher/data/www/main.js index dd580290..c05dc1d9 100644 --- a/ESP32_AP-Flasher/data/www/main.js +++ b/ESP32_AP-Flasher/data/www/main.js @@ -10,7 +10,9 @@ const WAKEUP_REASON_WDT_RESET = 0xFE; const models = ["1.54\" 152x152px", "2.9\" 296x128px", "4.2\" 400x300px"]; models[240] = "Segmented tag" +models[17] = "2.9\" 296x128px (UC8151)" const displaySizeLookup = { 0: [152, 152], 1: [128, 296], 2: [400, 300] }; +displaySizeLookup[17] = [128, 296]; const colorTable = { 0: [255, 255, 255], 1: [0, 0, 0], 2: [255, 0, 0], 3: [255, 0, 0] }; const imageQueue = []; @@ -247,7 +249,7 @@ $('#taglist').addEventListener("click", (event) => { .then(data => { var tagdata = data.tags[0]; $('#cfgalias').value = tagdata.alias; - if (populateSelectTag(tagdata.hwType)) { + if (populateSelectTag(tagdata.hwType, tagdata.capabilities)) { $('#cfgcontent').parentNode.style.display = "block"; $('#cfgcontent').value = tagdata.contentMode; $('#cfgcontent').dataset.json = tagdata.modecfgjson; @@ -438,13 +440,14 @@ function contentselected() { $('#cfgsave').parentNode.style.display = 'block'; } -function populateSelectTag(hwtype) { +function populateSelectTag(hwtype, capabilities) { var selectTag = $("#cfgcontent"); selectTag.innerHTML = ""; var optionsAdded = false; cardconfig.forEach(item => { + var capcheck = item.capabilities ?? 0; var hwtypeArray = item.hwtype; - if (hwtypeArray.includes(hwtype)) { + if (hwtypeArray.includes(hwtype) && (capabilities & capcheck || capcheck == 0)) { var option = document.createElement("option"); option.value = item.id; option.text = item.name; diff --git a/ESP32_AP-Flasher/include/newproto.h b/ESP32_AP-Flasher/include/newproto.h index 5d2e1ecc..59b0f2d9 100644 --- a/ESP32_AP-Flasher/include/newproto.h +++ b/ESP32_AP-Flasher/include/newproto.h @@ -5,6 +5,7 @@ extern bool checkCRC(void* p, uint8_t len); extern void processBlockRequest(struct espBlockRequest* br); extern void prepareIdleReq(uint8_t* dst, uint16_t nextCheckin); +extern void prepareNFCReq(uint8_t* dst, const char* url); extern bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t nextCheckin); extern void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP); extern void processXferComplete(struct espXferComplete* xfc, bool local); diff --git a/ESP32_AP-Flasher/include/tag_db.h b/ESP32_AP-Flasher/include/tag_db.h index 68dd277f..3f0c1cb8 100644 --- a/ESP32_AP-Flasher/include/tag_db.h +++ b/ESP32_AP-Flasher/include/tag_db.h @@ -6,10 +6,6 @@ #pragma pack(push, 1) #pragma once -#define SOLUM_154_033 0 -#define SOLUM_29_033 1 -#define SOLUM_42_033 2 - #define WAKEUP_REASON_TIMED 0 #define WAKEUP_REASON_BOOTUP 1 #define WAKEUP_REASON_GPIO 2 diff --git a/ESP32_AP-Flasher/src/contentmanager.cpp b/ESP32_AP-Flasher/src/contentmanager.cpp index c7ba41c2..6ab6a076 100644 --- a/ESP32_AP-Flasher/src/contentmanager.cpp +++ b/ESP32_AP-Flasher/src/contentmanager.cpp @@ -53,7 +53,7 @@ void contentRunner() { uint8_t src[8]; *((uint64_t *)src) = swap64(*((uint64_t *)mac8)); - if (taginfo->RSSI && (now >= taginfo->nextupdate || taginfo->wakeupReason == WAKEUP_REASON_GPIO)) { + if (taginfo->RSSI && (now >= taginfo->nextupdate || taginfo->wakeupReason == WAKEUP_REASON_GPIO || taginfo->wakeupReason == WAKEUP_REASON_NFC)) { drawNew(src, (taginfo->wakeupReason == WAKEUP_REASON_GPIO), taginfo); taginfo->wakeupReason = 0; } @@ -241,6 +241,12 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { sendAPSegmentedData(mac, (String)buffer, 0x0000, false, (taginfo->isExternal == false)); taginfo->nextupdate = 3216153600; break; + + case 14: // NFC URL + + prepareNFCReq(mac, cfgobj["url"].as()); + taginfo->nextupdate = 3216153600; + break; } taginfo->modeConfigJson = doc.as(); @@ -299,18 +305,18 @@ void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams) { TFT_eSprite spr = TFT_eSprite(&tft); LittleFS.begin(); - if (taginfo->hwType == SOLUM_29_033) { + if (taginfo->hwType == SOLUM_29_SSD1619 || taginfo->hwType == SOLUM_29_UC8151) { initSprite(spr, 296, 128); drawString(spr, languageDays[getCurrentLanguage()][timeinfo.tm_wday], 296 / 2, 10, "fonts/calibrib62", TC_DATUM, PAL_RED); drawString(spr, String(timeinfo.tm_mday) + " " + languageMonth[getCurrentLanguage()][timeinfo.tm_mon], 296 / 2, 73, "fonts/calibrib50", TC_DATUM); - } else if (taginfo->hwType == SOLUM_154_033) { + } else if (taginfo->hwType == SOLUM_154_SSD1619) { initSprite(spr, 152, 152); drawString(spr, languageDays[getCurrentLanguage()][timeinfo.tm_wday], 152 / 2, 10, "fonts/calibrib30", TC_DATUM); drawString(spr, String(languageMonth[getCurrentLanguage()][timeinfo.tm_mon]), 152 / 2, 120, "fonts/calibrib30", TC_DATUM); drawString(spr, String(timeinfo.tm_mday), 152 / 2, 42, "fonts/numbers2-1", TC_DATUM, PAL_RED); - } else if (taginfo->hwType == SOLUM_42_033) { + } else if (taginfo->hwType == SOLUM_42_SSD1619) { initSprite(spr, 400, 300); drawString(spr, languageDays[getCurrentLanguage()][timeinfo.tm_wday], 400 / 2, 30, "fonts/calibrib62", TC_DATUM, PAL_RED); drawString(spr, String(timeinfo.tm_mday) + " " + languageMonth[getCurrentLanguage()][timeinfo.tm_mon], 400 / 2, 113, "fonts/calibrib50", TC_DATUM); @@ -349,7 +355,7 @@ void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord TFT_eSprite spr = TFT_eSprite(&tft); LittleFS.begin(); - if (taginfo->hwType == SOLUM_29_033) { + if (taginfo->hwType == SOLUM_29_SSD1619 || taginfo->hwType == SOLUM_29_UC8151) { initSprite(spr, 296, 128); spr.setTextDatum(MC_DATUM); if (count > thresholdred) { @@ -364,7 +370,7 @@ void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord spr.drawString(String(count), 296 / 2, 128 / 2 + 10); spr.unloadFont(); - } else if (taginfo->hwType == SOLUM_154_033) { + } else if (taginfo->hwType == SOLUM_154_SSD1619) { initSprite(spr, 152, 152); spr.setTextDatum(MC_DATUM); if (count > thresholdred) { @@ -379,7 +385,7 @@ void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord spr.drawString(String(count), 152 / 2, 152 / 2 + 7); spr.unloadFont(); - } else if (taginfo->hwType == SOLUM_42_033) { + } else if (taginfo->hwType == SOLUM_42_SSD1619) { initSprite(spr, 400, 300); spr.setTextDatum(MC_DATUM); if (count > thresholdred) { @@ -420,6 +426,7 @@ void drawWeather(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgP filter["current_weather"]["windspeed"] = true; filter["current_weather"]["winddirection"] = true; filter["current_weather"]["weathercode"] = true; + filter["current_weather"]["is_day"] = true; StaticJsonDocument<1000> doc; DeserializationError error = deserializeJson(doc, http.getString(), DeserializationOption::Filter(filter)); @@ -463,9 +470,9 @@ void drawWeather(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgP "\uf01a", "\uf01a", "-", "-", "\uf064", "\uf064", "-", "-", "-", "-", "-", "-", "-", "-", "\uf01e", "\uf01d", "-", "-", "\uf01e"}; if (isday == 0) { - weatherIcons[0] = "\0uf02e"; - weatherIcons[1] = "\0uf083"; - weatherIcons[2] = "\0uf086"; + weatherIcons[0] = "\uf02e"; + weatherIcons[1] = "\uf083"; + weatherIcons[2] = "\uf086"; } TFT_eSPI tft = TFT_eSPI(); @@ -473,7 +480,7 @@ void drawWeather(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgP LittleFS.begin(); tft.setTextWrap(false, false); - if (taginfo->hwType == SOLUM_29_033) { + if (taginfo->hwType == SOLUM_29_SSD1619 || taginfo->hwType == SOLUM_29_UC8151) { initSprite(spr, 296, 128); drawString(spr, cfgobj["location"], 5, 5, "fonts/bahnschrift30"); @@ -505,7 +512,7 @@ void drawWeather(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgP } spr.unloadFont(); - } else if (taginfo->hwType == SOLUM_154_033) { + } else if (taginfo->hwType == SOLUM_154_SSD1619) { initSprite(spr, 152, 152); spr.setTextDatum(TL_DATUM); @@ -541,7 +548,7 @@ void drawWeather(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgP } spr.unloadFont(); - } else if (taginfo->hwType == SOLUM_42_033) { + } else if (taginfo->hwType == SOLUM_42_SSD1619) { initSprite(spr, 400, 300); drawString(spr, cfgobj["location"], 10, 10, "fonts/bahnschrift30"); @@ -624,7 +631,7 @@ void drawForecast(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, img LittleFS.begin(); tft.setTextWrap(false, false); - if (taginfo->hwType == SOLUM_29_033) { + if (taginfo->hwType == SOLUM_29_SSD1619 || taginfo->hwType == SOLUM_29_UC8151) { initSprite(spr, 296, 128); spr.setTextFont(2); @@ -670,7 +677,7 @@ void drawForecast(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, img } } - } else if (taginfo->hwType == SOLUM_42_033) { + } else if (taginfo->hwType == SOLUM_42_SSD1619) { initSprite(spr, 400, 300); spr.setTextFont(2); spr.setTextColor(PAL_BLACK, PAL_WHITE); @@ -726,17 +733,17 @@ void drawIdentify(String &filename, tagRecord *&taginfo, imgParam &imageParams) TFT_eSprite spr = TFT_eSprite(&tft); LittleFS.begin(); - if (taginfo->hwType == SOLUM_29_033) { + if (taginfo->hwType == SOLUM_29_SSD1619 || taginfo->hwType == SOLUM_29_UC8151) { initSprite(spr, 296, 128); drawString(spr, taginfo->alias, 10, 10, "fonts/bahnschrift20"); drawString(spr, mac62hex(taginfo->mac), 10, 50, "fonts/bahnschrift20", TL_DATUM, PAL_RED); - } else if (taginfo->hwType == SOLUM_154_033) { + } else if (taginfo->hwType == SOLUM_154_SSD1619) { initSprite(spr, 152, 152); drawString(spr, taginfo->alias, 5, 5, "fonts/bahnschrift20"); drawString(spr, mac62hex(taginfo->mac), 10, 50, "fonts/bahnschrift20", TL_DATUM, PAL_RED); - } else if (taginfo->hwType == SOLUM_42_033) { + } else if (taginfo->hwType == SOLUM_42_SSD1619) { initSprite(spr, 400, 300); drawString(spr, taginfo->alias, 20, 20, "fonts/bahnschrift20"); drawString(spr, mac62hex(taginfo->mac), 20, 70, "fonts/bahnschrift20", TL_DATUM, PAL_RED); @@ -799,7 +806,7 @@ bool getRssFeed(String &filename, String URL, String title, tagRecord *&taginfo, U8g2_for_TFT_eSPI u8f; u8f.begin(spr); - if (taginfo->hwType == SOLUM_29_033) { + if (taginfo->hwType == SOLUM_29_SSD1619 || taginfo->hwType == SOLUM_29_UC8151) { initSprite(spr, 296, 128); if (title == "" || title == "null") title = "RSS feed"; drawString(spr, title, 5, 3, "fonts/bahnschrift20", TL_DATUM, PAL_RED); @@ -823,7 +830,7 @@ bool getRssFeed(String &filename, String URL, String title, tagRecord *&taginfo, u8f.setCursor(5, 34 + i * 13); u8f.print(reader.itemData[i]); } - } else if (taginfo->hwType == SOLUM_42_033) { + } else if (taginfo->hwType == SOLUM_42_SSD1619) { initSprite(spr, 400, 300); if (title == "" || title == "null") title = "RSS feed"; drawString(spr, title, 5, 5, "fonts/bahnschrift20", TL_DATUM, PAL_RED); @@ -905,7 +912,7 @@ bool getCalFeed(String &filename, String URL, String title, tagRecord *&taginfo, U8g2_for_TFT_eSPI u8f; u8f.begin(spr); - if (taginfo->hwType == SOLUM_29_033) { + if (taginfo->hwType == SOLUM_29_SSD1619 || taginfo->hwType == SOLUM_29_UC8151) { initSprite(spr, 296, 128); if (title == "" || title == "null") title = "Calendar"; @@ -941,7 +948,7 @@ bool getCalFeed(String &filename, String URL, String title, tagRecord *&taginfo, u8f.setCursor(50, 32 + i * 15); u8f.print(eventtitle); } - } else if (taginfo->hwType == SOLUM_42_033) { + } else if (taginfo->hwType == SOLUM_42_SSD1619) { initSprite(spr, 400, 300); if (title == "" || title == "null") title = "Calendar"; @@ -998,13 +1005,13 @@ void drawQR(String &filename, String qrcontent, String title, tagRecord *&taginf int size = qrcode.size; int xpos = 0, ypos = 0, dotsize = 1; - if (taginfo->hwType == SOLUM_29_033) { + if (taginfo->hwType == SOLUM_29_SSD1619 || taginfo->hwType == SOLUM_29_UC8151) { initSprite(spr, 296, 128); drawString(spr, title, 10, 5, "fonts/bahnschrift20"); dotsize = int((128 - 25) / size); xpos = 149 - dotsize * size / 2; ypos = 25; - } else if (taginfo->hwType == SOLUM_154_033) { + } else if (taginfo->hwType == SOLUM_154_SSD1619) { initSprite(spr, 152, 152); spr.setTextFont(2); spr.setTextColor(PAL_BLACK, PAL_WHITE); @@ -1012,7 +1019,7 @@ void drawQR(String &filename, String qrcontent, String title, tagRecord *&taginf dotsize = int((152 - 20) / size); xpos = 76 - dotsize * size / 2; ypos = 20; - } else if (taginfo->hwType == SOLUM_42_033) { + } else if (taginfo->hwType == SOLUM_42_SSD1619) { initSprite(spr, 400, 300); drawString(spr, title, 10, 10, "fonts/bahnschrift20"); dotsize = int((300 - 30) / size); diff --git a/ESP32_AP-Flasher/src/newproto.cpp b/ESP32_AP-Flasher/src/newproto.cpp index 94b1b64e..325ef0ae 100644 --- a/ESP32_AP-Flasher/src/newproto.cpp +++ b/ESP32_AP-Flasher/src/newproto.cpp @@ -85,6 +85,54 @@ void prepareIdleReq(uint8_t* dst, uint16_t nextCheckin) { } } +void prepareNFCReq(uint8_t* dst, const char* url) { + uint8_t src[8]; + *((uint64_t*)src) = swap64(*((uint64_t*)dst)); + uint8_t mac[6]; + memcpy(mac, src + 2, sizeof(mac)); + + tagRecord* taginfo = nullptr; + taginfo = tagRecord::findByMAC(mac); + if (taginfo == nullptr) { + wsErr("Tag not found, this shouldn't happen."); + return; + } + + clearPending(taginfo); + size_t len = strlen(url); + taginfo->data = new uint8_t[len + 8]; + + // TLV + taginfo->data[0] = 0x03; // NDEF message (TLV type) + taginfo->data[1] = 4 + len + 1; + + // ndef record + taginfo->data[2] = 0xD1; + taginfo->data[3] = 0x01; // well known record type + taginfo->data[4] = len + 1; // payload length + taginfo->data[5] = 0x55; // payload type (URI record) + taginfo->data[6] = 0x00; // URI identifier code (no prepending) + + memcpy(taginfo->data + 7, reinterpret_cast(url), len); + len = 7 + len; + taginfo->data[len] = 0xFE; + len = 1 + len; + + taginfo->pending = true; + taginfo->len = len; + + struct pendingData pending = {0}; + memcpy(pending.targetMac, dst, 8); + pending.availdatainfo.dataSize = len; + pending.availdatainfo.dataType = DATATYPE_NFC_RAW_CONTENT; + pending.availdatainfo.nextCheckIn = 0; + pending.availdatainfo.dataVer = millis(); + pending.attemptsLeft = 10; + + sendDataAvail(&pending); + wsSendTaginfo(mac); +} + bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t nextCheckin) { if (nextCheckin > MIN_RESPONSE_TIME) nextCheckin = MIN_RESPONSE_TIME; if (wsClientCount()) nextCheckin=0; @@ -141,7 +189,7 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t time_t now; time(&now); time_t last_midnight = now - now % (24 * 60 * 60) + 3 * 3600; // somewhere in the middle of the night - if (taginfo->lastfullupdate < last_midnight) { + if (taginfo->lastfullupdate < last_midnight || taginfo->hwType == SOLUM_29_UC8151) { lut = EPD_LUT_DEFAULT; // full update once a day taginfo->lastfullupdate = now; } @@ -164,7 +212,6 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t clearPending(taginfo); taginfo->pending = true; memcpy(taginfo->md5pending, md5bytes, sizeof(md5bytes)); - } else { wsLog("firmware upload pending"); taginfo->filename = *filename; diff --git a/ESP32_AP-Flasher/src/tag_db.cpp b/ESP32_AP-Flasher/src/tag_db.cpp index 9985eadc..da97901a 100644 --- a/ESP32_AP-Flasher/src/tag_db.cpp +++ b/ESP32_AP-Flasher/src/tag_db.cpp @@ -218,7 +218,6 @@ uint8_t getTagCount() { void clearPending(tagRecord* taginfo) { if (taginfo->data != nullptr) { free(taginfo->data); - Serial.println("free taginfo->data"); taginfo->data = nullptr; } taginfo->pending = false;