diff --git a/ESP32_AP-Flasher/data/www/content_cards.json b/ESP32_AP-Flasher/data/www/content_cards.json index f4a7fb6d..8c331864 100644 --- a/ESP32_AP-Flasher/data/www/content_cards.json +++ b/ESP32_AP-Flasher/data/www/content_cards.json @@ -406,5 +406,132 @@ "type": "text" } ] + }, + { + "id": 17, + "name": "Send Command", + "desc": "Send a command to a tag to execute", + "hwtype": [ + 0, + 1, + 2, + 17, + 240 + ], + "param": [ + { + "key": "cmd", + "name": "CMD", + "desc": "Action", + "type": "select", + "options": { + "0": "Reboot", + "1": "Scan Channels", + "2": "Clear settings" + } + } + ] + }, + { + "id": 18, + "name": "Set Tag Config", + "desc": "Sets tag options. The options you see below are the default options. This may or may not match current tag settings", + "hwtype": [ + 0, + 1, + 2, + 17, + 240 + ], + "param": [ + { + "key": "fastboot", + "name": "Boot method", + "desc": "How the tag should boot, fast or normal", + "type": "select", + "options": { + "0": "-Normal boot", + "1": "Fast boot" + } + }, + { + "key": "rfwake", + "name": "RF Wake", + "desc": "If the tag should support RF wake or not. This adds a 0.9µA current draw", + "type": "select", + "options": { + "0": "-Disabled", + "1": "Enabled" + } + }, + { + "key": "tagroaming", + "name": "Tag Roaming", + "desc": "If enabled, the tag will periodically scan for AP's and will switch to a different channel if a stronger signal is found", + "type": "select", + "options": { + "0": "-Disabled", + "1": "Enabled" + } + }, + { + "key": "tagscanontimeout", + "name": "Scan for AP on timeout", + "desc": "If a tag hasn't found an AP for an hour, should it rescan the channels for another AP?", + "type": "select", + "options": { + "1": "-Enabled", + "0": "Disabled" + } + }, + { + "key": "showlowbat", + "name": "Low Battery symbol", + "desc": "Should the tag display the 'low battery' symbol if the battery a voltage threshold has been reached?", + "type": "select", + "options": { + "1": "-Enabled", + "0": "Disabled" + } + }, + { + "key": "shownorf", + "name": "No AP symbol", + "desc": "Should the tag display the 'No-signal/AP' symbol if it hasn't been able to contact an AP?", + "type": "select", + "options": { + "1": "-Enabled", + "0": "Disabled" + } + }, + { + "key": "lowvoltage", + "name": "Low voltage threshold", + "desc": "Below what voltage should the tag display the 'low bat' symbol?", + "type": "select", + "options": { + "2600": "-2.6v", + "2500": "2.5v", + "2400": "2.4v", + "2300": "2.3v", + "2200": "2.2v" + } + }, + { + "key": "fixedchannel", + "name": "Fixed Channel", + "desc": "What channel should the tag initially join?", + "type": "select", + "options": { + "0": "-Auto", + "11": "11", + "15": "15", + "20": "20", + "25": "25", + "26": "26", + "27": "27" + } + } + ] } ] \ 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 79d8c592..2f9e8f27 100644 --- a/ESP32_AP-Flasher/data/www/main.js +++ b/ESP32_AP-Flasher/data/www/main.js @@ -24,13 +24,13 @@ const apstate = [ { state: "requires power cycle", color: "purple" }, { state: "failed", color: "red" }, { state: "coming online", color: "yellow" } -]; +]; const runstate = [ { state: "⏹︎ stopped" }, { state: "⏸pause" }, { state: "" }, // hide running { state: "⏳︎ init" } -]; +]; const imageQueue = []; let isProcessing = false; @@ -48,7 +48,7 @@ window.addEventListener("load", function () { this.document.title = data.alias; } }); - fetch('/content_cards.json') + fetch('/content_cards.json') .then(response => response.json()) .then(data => { cardconfig = data; @@ -307,7 +307,7 @@ $('#taglist').addEventListener("click", (event) => { $('#cfgalias').value = tagdata.alias; $('#cfgmore').style.display = "none"; if (populateSelectTag(tagdata.hwType, tagdata.capabilities)) { - $('#cfgcontent').parentNode.style.display = "flex"; + $('#cfgcontent').parentNode.style.display = "flex"; $('#cfgcontent').value = tagdata.contentMode; $('#cfgcontent').dataset.json = tagdata.modecfgjson; contentselected(); @@ -354,7 +354,7 @@ $('#cfgsave').onclick = function () { formData.append("rotate", $('#cfgrotate').value); formData.append("lut", $('#cfglut').value); - + fetch("/save_cfg", { method: "POST", body: formData @@ -530,6 +530,12 @@ function contentselected() { const optionElement = document.createElement("option"); optionElement.value = key; optionElement.text = element.options[key]; + if (element.options[key].substring(0,1)=="-") { + optionElement.text = element.options[key].substring(1); + optionElement.selected = true; + } else { + optionElement.selected = false; + } input.appendChild(optionElement); } break; diff --git a/ESP32_AP-Flasher/include/commstructs.h b/ESP32_AP-Flasher/include/commstructs.h index 73732d25..6843910e 100644 --- a/ESP32_AP-Flasher/include/commstructs.h +++ b/ESP32_AP-Flasher/include/commstructs.h @@ -107,4 +107,19 @@ struct TagInfo { uint8_t contentMode; } __packed; +struct tagsettings { + uint8_t settingsVer; // the version of the struct as written to the infopage + uint8_t enableFastBoot; // default 0; if set, it will skip splashscreen + uint8_t enableRFWake; // default 0; if set, it will enable RF wake. This will add about ~0.9µA idle power consumption + uint8_t enableTagRoaming; // default 0; if set, the tag will scan for an accesspoint every few check-ins. This will increase power consumption quite a bit + uint8_t enableScanForAPAfterTimeout; // default 1; if a the tag failed to check in, after a few attempts it will try to find a an AP on other channels + uint8_t enableLowBatSymbol; // default 1; tag will show 'low battery' icon on screen if the battery is depleted + uint8_t enableNoRFSymbol; // default 1; tag will show 'no signal' icon on screen if it failed to check in for a longer period of time + uint8_t fastBootCapabilities; // holds the byte with 'capabilities' as detected during a normal tag boot; allows the tag to skip detecting buttons and NFC chip + uint8_t customMode; // default 0; if anything else, tag will bootup in a different 'mode' + uint16_t batLowVoltage; // Low battery threshold voltage (2450 for 2.45v). defaults to BATTERY_VOLTAGE_MINIMUM from powermgt.h + uint16_t minimumCheckInTime; // defaults to BASE_INTERVAL from powermgt.h + uint8_t fixedChannel; // default 0; if set to a valid channel number, the tag will stick to that channel +} __packed; + #pragma pack(pop) \ No newline at end of file diff --git a/ESP32_AP-Flasher/include/contentmanager.h b/ESP32_AP-Flasher/include/contentmanager.h index af98be72..ad1f7883 100644 --- a/ESP32_AP-Flasher/include/contentmanager.h +++ b/ESP32_AP-Flasher/include/contentmanager.h @@ -37,5 +37,6 @@ String windDirectionIcon(int degrees); void getLocation(JsonObject &cfgobj); void prepareNFCReq(uint8_t* dst, const char* url); void prepareLUTreq(uint8_t *dst, String input); +void prepareConfigFile(uint8_t *dst, JsonObject config); void getTemplate(JsonDocument &json, const char *filePath, uint8_t id, uint8_t hwtype); void setU8G2Font(const String &title, U8g2_for_TFT_eSPI &u8f); diff --git a/ESP32_AP-Flasher/include/newproto.h b/ESP32_AP-Flasher/include/newproto.h index 718f1d02..f003a41b 100644 --- a/ESP32_AP-Flasher/include/newproto.h +++ b/ESP32_AP-Flasher/include/newproto.h @@ -12,6 +12,7 @@ extern void processXferComplete(struct espXferComplete* xfc, bool local); extern void processXferTimeout(struct espXferComplete* xfc, bool local); extern void processDataReq(struct espAvailDataReq* adr, bool local); +extern bool sendTagCommand(uint8_t* dst, uint8_t cmd, bool local); extern bool sendAPSegmentedData(uint8_t* dst, String data, uint16_t icons, bool inverted, bool local); extern bool showAPSegmentedInfo(uint8_t* dst, bool local); extern void updateTaginfoitem(struct TagInfo* taginfoitem); diff --git a/ESP32_AP-Flasher/src/contentmanager.cpp b/ESP32_AP-Flasher/src/contentmanager.cpp index b2aa989c..6be2ae98 100644 --- a/ESP32_AP-Flasher/src/contentmanager.cpp +++ b/ESP32_AP-Flasher/src/contentmanager.cpp @@ -286,6 +286,19 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) { taginfo->nextupdate = now + (cfgobj["ttl"].as() < 5 ? 5 : cfgobj["ttl"].as()) * 60; updateTagImage(filename, mac, (cfgobj["ttl"].as() < 5 ? 5 : cfgobj["ttl"].as()), taginfo, imageParams); break; + + case 17: // tag command + sendTagCommand(mac, cfgobj["cmd"].as(), (taginfo->isExternal == false)); + cfgobj["filename"] = ""; + taginfo->nextupdate = 3216153600; + taginfo->contentMode = Image; + break; + case 18: + prepareConfigFile(mac, cfgobj); + cfgobj["filename"] = ""; + taginfo->nextupdate = 3216153600; + taginfo->contentMode = Image; + break; } taginfo->modeConfigJson = doc.as(); @@ -1000,6 +1013,23 @@ void prepareLUTreq(uint8_t *dst, String input) { prepareDataAvail(waveform, waveformLen, DATATYPE_CUSTOM_LUT_OTA, dst); } +void prepareConfigFile(uint8_t *dst, JsonObject config) { + struct tagsettings tagSettings; + tagSettings.settingsVer = 1; + tagSettings.enableFastBoot = config["fastboot"].as(); + tagSettings.enableRFWake = config["rfwake"].as(); + tagSettings.enableTagRoaming = config["tagroaming"].as(); + tagSettings.enableScanForAPAfterTimeout = config["tagscanontimeout"].as(); + tagSettings.enableLowBatSymbol = config["showlowbat"].as(); + tagSettings.enableNoRFSymbol = config["shownorf"].as(); + tagSettings.customMode = 0; + tagSettings.fastBootCapabilities = 0; + tagSettings.minimumCheckInTime = 1; + tagSettings.fixedChannel = config["fixedchannel"].as(); + tagSettings.batLowVoltage = config["lowvoltage"].as(); + prepareDataAvail((uint8_t *)&tagSettings, sizeof(tagSettings), 0xA8, dst); +} + void getTemplate(JsonDocument &json, const char *filePath, uint8_t id, uint8_t hwtype) { File jsonFile = LittleFS.open(filePath, "r"); if (!jsonFile) { diff --git a/ESP32_AP-Flasher/src/newproto.cpp b/ESP32_AP-Flasher/src/newproto.cpp index c3422898..0a092c3c 100644 --- a/ESP32_AP-Flasher/src/newproto.cpp +++ b/ESP32_AP-Flasher/src/newproto.cpp @@ -587,6 +587,26 @@ bool showAPSegmentedInfo(uint8_t* dst, bool local) { } } +bool sendTagCommand(uint8_t* dst, uint8_t cmd, bool local) { + struct pendingData pending = {0}; + memcpy(pending.targetMac, dst, 8); + pending.availdatainfo.dataType = 0xAF; + pending.availdatainfo.dataTypeArgument = cmd; + pending.availdatainfo.nextCheckIn = 0; + pending.attemptsLeft = 120; + char buffer[64]; + sprintf(buffer, ">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]); + Serial.print(buffer); + if (local) { + return sendDataAvail(&pending); + } else { + udpsync.netSendDataAvail(&pending); + return true; + } +} + + + void updateTaginfoitem(struct TagInfo* taginfoitem) { tagRecord* taginfo = nullptr; taginfo = tagRecord::findByMAC(taginfoitem->mac); diff --git a/ESP32_AP-Flasher/src/serialap.cpp b/ESP32_AP-Flasher/src/serialap.cpp index c088171a..3cec6467 100644 --- a/ESP32_AP-Flasher/src/serialap.cpp +++ b/ESP32_AP-Flasher/src/serialap.cpp @@ -606,7 +606,7 @@ bool bringAPOnline() { } void APTask(void* parameter) { - xTaskCreate(rxCmdProcessor, "rxCmdProcessor", 3000, NULL, configMAX_PRIORITIES - 10, NULL); + xTaskCreate(rxCmdProcessor, "rxCmdProcessor", 4000, NULL, configMAX_PRIORITIES - 10, NULL); xTaskCreate(rxSerialTask, "rxSerialTask", 1750, NULL, configMAX_PRIORITIES - 4, NULL); #if (AP_PROCESS_PORT == FLASHER_AP_PORT)