From 8435464c84fd86602f0b38bf77d33d7e065080e9 Mon Sep 17 00:00:00 2001 From: Quency-D Date: Tue, 24 Mar 2026 10:48:15 +0800 Subject: [PATCH 001/214] Add v4 FEM LNA CLI control commands. --- docs/cli_commands.md | 14 +++++++++ examples/companion_radio/DataStore.cpp | 2 ++ examples/companion_radio/MyMesh.cpp | 31 +++++++++++++++++++- examples/companion_radio/NodePrefs.h | 3 +- examples/simple_repeater/MyMesh.cpp | 2 ++ examples/simple_room_server/MyMesh.cpp | 2 ++ examples/simple_sensor/SensorMesh.cpp | 2 ++ src/MeshCore.h | 5 +++- src/helpers/CommonCLI.cpp | 39 ++++++++++++++++++++++++-- src/helpers/CommonCLI.h | 1 + variants/heltec_v4/HeltecV4Board.cpp | 18 ++++++++++++ variants/heltec_v4/HeltecV4Board.h | 3 ++ variants/heltec_v4/LoRaFEMControl.h | 3 +- 13 files changed, 118 insertions(+), 7 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 9769d713..4dbf84e1 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -261,6 +261,20 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### View or change the LoRa FEM receive-path gain state on supported boards +**Usage:** +- `get radio.fem.rxgain` +- `set radio.fem.rxgain ` + +**Parameters:** +- `state`: `on`|`off` + +**Notes:** +- This controls the external LoRa FEM receive-path LNA where the board supports it. +- This is separate from `radio.rxgain`, which controls the radio chip receive gain mode. + +--- + ### System #### View or change this node's name diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index 40f1ceeb..98a7a0dc 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -231,6 +231,7 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no file.read((uint8_t *)&_prefs.autoadd_config, sizeof(_prefs.autoadd_config)); // 87 file.read((uint8_t *)&_prefs.autoadd_max_hops, sizeof(_prefs.autoadd_max_hops)); // 88 file.read((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89 + file.read((uint8_t *)&_prefs.radio_fem_rxgain, sizeof(_prefs.radio_fem_rxgain)); // 90 file.close(); } @@ -269,6 +270,7 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ file.write((uint8_t *)&_prefs.autoadd_config, sizeof(_prefs.autoadd_config)); // 87 file.write((uint8_t *)&_prefs.autoadd_max_hops, sizeof(_prefs.autoadd_max_hops)); // 88 file.write((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89 + file.write((uint8_t *)&_prefs.radio_fem_rxgain, sizeof(_prefs.radio_fem_rxgain)); // 90 file.close(); } diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index b94e4526..d5d0abfe 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -46,7 +46,9 @@ #define CMD_SET_CUSTOM_VAR 41 #define CMD_GET_ADVERT_PATH 42 #define CMD_GET_TUNING_PARAMS 43 -// NOTE: CMD range 44..49 parked, potentially for WiFi operations +#define CMD_GET_RADIO_FEM_RXGAIN 44 +#define CMD_SET_RADIO_FEM_RXGAIN 45 +// NOTE: CMD range 46..49 parked, potentially for WiFi operations #define CMD_SEND_BINARY_REQ 50 #define CMD_FACTORY_RESET 51 #define CMD_SEND_PATH_DISCOVERY_REQ 52 @@ -828,6 +830,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe _prefs.rx_boosted_gain = 1; // enabled by default #endif #endif + _prefs.radio_fem_rxgain = 1; } void MyMesh::begin(bool has_display) { @@ -866,6 +869,7 @@ void MyMesh::begin(bool has_display) { _prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, -9, MAX_LORA_TX_POWER); _prefs.gps_enabled = constrain(_prefs.gps_enabled, 0, 1); // Ensure boolean 0 or 1 _prefs.gps_interval = constrain(_prefs.gps_interval, 0, 86400); // Max 24 hours + _prefs.radio_fem_rxgain = constrain(_prefs.radio_fem_rxgain, 0, 1); #ifdef BLE_PIN_CODE // 123456 by default if (_prefs.ble_pin == 0) { @@ -895,6 +899,7 @@ void MyMesh::begin(bool has_display) { radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); radio_set_tx_power(_prefs.tx_power_dbm); radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); + board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s", radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled"); } @@ -1697,6 +1702,30 @@ void MyMesh::handleCmdFrame(size_t len) { } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } + } else if (cmd_frame[0] == CMD_GET_RADIO_FEM_RXGAIN) { + if (!board.canControlLoRaFemLna()) { + writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); + } else { + out_frame[0] = RESP_CODE_OK; + uint32_t value = board.isLoRaFemLnaEnabled() ? 1 : 0; + memcpy(&out_frame[1], &value, 4); + _serial->writeFrame(out_frame, 5); + } + } else if (cmd_frame[0] == CMD_SET_RADIO_FEM_RXGAIN && len >= 2) { + uint8_t value = cmd_frame[1]; + if (!board.canControlLoRaFemLna()) { + writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); + } else if (value <= 1) { + _prefs.radio_fem_rxgain = value; + if (board.setLoRaFemLnaEnabled(value != 0)) { + savePrefs(); + writeOKFrame(); + } else { + writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); + } + } else { + writeErrFrame(ERR_CODE_ILLEGAL_ARG); + } } else if (cmd_frame[0] == CMD_GET_ADVERT_PATH && len >= PUB_KEY_SIZE+2) { // FUTURE use: uint8_t reserved = cmd_frame[1]; uint8_t *pub_key = &cmd_frame[2]; diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 557be306..7ecfdf7d 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -29,7 +29,8 @@ struct NodePrefs { // persisted to file uint32_t gps_interval; // GPS read interval in seconds uint8_t autoadd_config; // bitmask for auto-add contacts config uint8_t rx_boosted_gain; // SX126x RX boosted gain mode (0=power saving, 1=boosted) + uint8_t radio_fem_rxgain; // LoRa FEM RX gain setting uint8_t client_repeat; uint8_t path_hash_mode; // which path mode to use when sending uint8_t autoadd_max_hops; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64) -}; \ No newline at end of file +}; diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 24e88949..92affc5a 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -896,6 +896,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.rx_boosted_gain = 1; // enabled by default; #endif #endif + _prefs.radio_fem_rxgain = 1; pending_discover_tag = 0; pending_discover_until = 0; @@ -922,6 +923,7 @@ void MyMesh::begin(FILESYSTEM *fs) { radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s", radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled"); + board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); updateAdvertTimer(); updateFloodAdvertTimer(); diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 7b943773..7f61c266 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -631,6 +631,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.gps_enabled = 0; _prefs.gps_interval = 0; _prefs.advert_loc_policy = ADVERT_LOC_PREFS; + _prefs.radio_fem_rxgain = 1; next_post_idx = 0; next_client_idx = 0; @@ -649,6 +650,7 @@ void MyMesh::begin(FILESYSTEM *fs) { radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); radio_set_tx_power(_prefs.tx_power_dbm); + board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); updateAdvertTimer(); updateFloodAdvertTimer(); diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index 57d23a31..58490fd0 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -729,6 +729,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise _prefs.gps_enabled = 0; _prefs.gps_interval = 0; _prefs.advert_loc_policy = ADVERT_LOC_PREFS; + _prefs.radio_fem_rxgain = 1; } void SensorMesh::begin(FILESYSTEM* fs) { @@ -741,6 +742,7 @@ void SensorMesh::begin(FILESYSTEM* fs) { radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); radio_set_tx_power(_prefs.tx_power_dbm); + board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); updateAdvertTimer(); updateFloodAdvertTimer(); diff --git a/src/MeshCore.h b/src/MeshCore.h index 70cd0f06..91b99c80 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -57,6 +57,9 @@ public: virtual uint8_t getStartupReason() const = 0; virtual bool getBootloaderVersion(char* version, size_t max_len) { return false; } virtual bool startOTAUpdate(const char* id, char reply[]) { return false; } // not supported + virtual bool setLoRaFemLnaEnabled(bool enable) { return false; } + virtual bool canControlLoRaFemLna() const { return false; } + virtual bool isLoRaFemLnaEnabled() const { return false; } // Power management interface (boards with power management override these) virtual bool isExternalPowered() { return false; } @@ -100,4 +103,4 @@ public: } }; -} \ No newline at end of file +} diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 8b097c29..1c2afd37 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -88,7 +88,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290 - // next: 291 + file.read((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 291 + // next: 292 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -118,6 +119,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { // sanitise settings _prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean + _prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean file.close(); } @@ -179,7 +181,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290 - // next: 291 + file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 291 + // next: 292 file.close(); } @@ -327,6 +330,12 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch } else if (memcmp(config, "radio.rxgain", 12) == 0) { sprintf(reply, "> %s", _prefs->rx_boosted_gain ? "on" : "off"); #endif + } else if (memcmp(config, "radio.fem.rxgain", 16) == 0) { + if (!_board->canControlLoRaFemLna()) { + strcpy(reply, "Error: unsupported by this board"); + } else { + sprintf(reply, "> %s", _board->isLoRaFemLnaEnabled() ? "on" : "off"); + } } else if (memcmp(config, "radio", 5) == 0) { char freq[16], bw[16]; strcpy(freq, StrHelper::ftoa(_prefs->freq)); @@ -520,13 +529,37 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch _prefs->disable_fwd = memcmp(&config[7], "off", 3) == 0; savePrefs(); strcpy(reply, _prefs->disable_fwd ? "OK - repeat is now OFF" : "OK - repeat is now ON"); -#if defined(USE_SX1262) || defined(USE_SX1268) } else if (memcmp(config, "radio.rxgain ", 13) == 0) { +#if defined(USE_SX1262) || defined(USE_SX1268) _prefs->rx_boosted_gain = memcmp(&config[13], "on", 2) == 0; strcpy(reply, "OK"); savePrefs(); _callbacks->setRxBoostedGain(_prefs->rx_boosted_gain); +#else + strcpy(reply, "Error: unsupported by this board"); #endif + } else if (memcmp(config, "radio.fem.rxgain ", 17) == 0) { + if (!_board->canControlLoRaFemLna()) { + strcpy(reply, "Error: unsupported by this board"); + } else if (memcmp(&config[17], "on", 2) == 0) { + if (_board->setLoRaFemLnaEnabled(true)) { + _prefs->radio_fem_rxgain = 1; + savePrefs(); + strcpy(reply, "OK - LoRa FEM RX gain on"); + } else { + strcpy(reply, "Error: failed to apply LoRa FEM RX gain"); + } + } else if (memcmp(&config[17], "off", 3) == 0) { + if (_board->setLoRaFemLnaEnabled(false)) { + _prefs->radio_fem_rxgain = 0; + savePrefs(); + strcpy(reply, "OK - LoRa FEM RX gain off"); + } else { + strcpy(reply, "Error: failed to apply LoRa FEM RX gain"); + } + } else { + strcpy(reply, "Error: state must be on or off"); + } } else if (memcmp(config, "radio ", 6) == 0) { strcpy(tmp, &config[6]); const char *parts[4]; diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 3a4332d1..82d5fcda 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -58,6 +58,7 @@ struct NodePrefs { // persisted to file float adc_multiplier; char owner_info[120]; uint8_t rx_boosted_gain; // power settings + uint8_t radio_fem_rxgain; // LoRa FEM RX gain setting uint8_t path_hash_mode; // which path mode to use when sending uint8_t loop_detect; }; diff --git a/variants/heltec_v4/HeltecV4Board.cpp b/variants/heltec_v4/HeltecV4Board.cpp index 49580d2e..4c79825a 100644 --- a/variants/heltec_v4/HeltecV4Board.cpp +++ b/variants/heltec_v4/HeltecV4Board.cpp @@ -83,3 +83,21 @@ void HeltecV4Board::begin() { return loRaFEMControl.getFEMType() == KCT8103L_PA ? "Heltec V4.3 OLED" : "Heltec V4 OLED"; #endif } + + bool HeltecV4Board::setLoRaFemLnaEnabled(bool enable) { + if (!loRaFEMControl.isLnaCanControl()) { + return false; + } + + loRaFEMControl.setLNAEnable(enable); + loRaFEMControl.setRxModeEnable(); + return true; + } + + bool HeltecV4Board::canControlLoRaFemLna() const { + return loRaFEMControl.isLnaCanControl(); + } + + bool HeltecV4Board::isLoRaFemLnaEnabled() const { + return loRaFEMControl.isLNAEnabled(); + } diff --git a/variants/heltec_v4/HeltecV4Board.h b/variants/heltec_v4/HeltecV4Board.h index 4d5ee461..fe77caed 100644 --- a/variants/heltec_v4/HeltecV4Board.h +++ b/variants/heltec_v4/HeltecV4Board.h @@ -19,5 +19,8 @@ public: void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; + bool setLoRaFemLnaEnabled(bool enable) override; + bool canControlLoRaFemLna() const override; + bool isLoRaFemLnaEnabled() const override; }; diff --git a/variants/heltec_v4/LoRaFEMControl.h b/variants/heltec_v4/LoRaFEMControl.h index 75452965..d84ebe9c 100644 --- a/variants/heltec_v4/LoRaFEMControl.h +++ b/variants/heltec_v4/LoRaFEMControl.h @@ -18,8 +18,9 @@ class LoRaFEMControl void setRxModeEnable(void); void setRxModeEnableWhenMCUSleep(void); void setLNAEnable(bool enabled); - bool isLnaCanControl(void) { return lna_can_control; } + bool isLnaCanControl(void) const { return lna_can_control; } void setLnaCanControl(bool can_control) { lna_can_control = can_control; } + bool isLNAEnabled(void) const { return lna_enabled; } LoRaFEMType getFEMType(void) const { return fem_type; } private: LoRaFEMType fem_type=OTHER_FEM_TYPES; From 65752fef72191293d8c548d6ba6826663fe947b6 Mon Sep 17 00:00:00 2001 From: Quency-D Date: Tue, 24 Mar 2026 13:57:11 +0800 Subject: [PATCH 002/214] Fix the memory leak issue in the strdup function. --- examples/simple_repeater/UITask.cpp | 3 ++- examples/simple_room_server/UITask.cpp | 3 ++- examples/simple_sensor/UITask.cpp | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/simple_repeater/UITask.cpp b/examples/simple_repeater/UITask.cpp index d096d14b..d1eae208 100644 --- a/examples/simple_repeater/UITask.cpp +++ b/examples/simple_repeater/UITask.cpp @@ -37,7 +37,8 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi } // v1.2.3 (1 Jan 2025) - sprintf(_version_info, "%s (%s)", version, build_date); + snprintf(_version_info, sizeof(_version_info), "%s (%s)", version, build_date); + free(version); } void UITask::renderCurrScreen() { diff --git a/examples/simple_room_server/UITask.cpp b/examples/simple_room_server/UITask.cpp index 46311c5e..a48cc6b3 100644 --- a/examples/simple_room_server/UITask.cpp +++ b/examples/simple_room_server/UITask.cpp @@ -37,7 +37,8 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi } // v1.2.3 (1 Jan 2025) - sprintf(_version_info, "%s (%s)", version, build_date); + snprintf(_version_info, sizeof(_version_info), "%s (%s)", version, build_date); + free(version); } void UITask::renderCurrScreen() { diff --git a/examples/simple_sensor/UITask.cpp b/examples/simple_sensor/UITask.cpp index 0694bc3c..e16c8266 100644 --- a/examples/simple_sensor/UITask.cpp +++ b/examples/simple_sensor/UITask.cpp @@ -37,7 +37,8 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi } // v1.2.3 (1 Jan 2025) - sprintf(_version_info, "%s (%s)", version, build_date); + snprintf(_version_info, sizeof(_version_info), "%s (%s)", version, build_date); + free(version); } void UITask::renderCurrScreen() { From 2442e9a5bd97fe29830164dd57208b98212d442e Mon Sep 17 00:00:00 2001 From: Quency-D Date: Tue, 24 Mar 2026 16:05:28 +0800 Subject: [PATCH 003/214] Adapt LNA CLI control commands for heltec_tracker_v2. --- .../heltec_tracker_v2/HeltecTrackerV2Board.cpp | 18 ++++++++++++++++++ .../heltec_tracker_v2/HeltecTrackerV2Board.h | 3 +++ variants/heltec_tracker_v2/LoRaFEMControl.h | 3 ++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp index aabfed79..f182c905 100644 --- a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp +++ b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp @@ -82,3 +82,21 @@ void HeltecTrackerV2Board::begin() { const char* HeltecTrackerV2Board::getManufacturerName() const { return "Heltec Tracker V2"; } + + bool HeltecTrackerV2Board::setLoRaFemLnaEnabled(bool enable) { + if (!loRaFEMControl.isLnaCanControl()) { + return false; + } + + loRaFEMControl.setLNAEnable(enable); + loRaFEMControl.setRxModeEnable(); + return true; + } + + bool HeltecTrackerV2Board::canControlLoRaFemLna() const { + return loRaFEMControl.isLnaCanControl(); + } + + bool HeltecTrackerV2Board::isLoRaFemLnaEnabled() const { + return loRaFEMControl.isLNAEnabled(); + } diff --git a/variants/heltec_tracker_v2/HeltecTrackerV2Board.h b/variants/heltec_tracker_v2/HeltecTrackerV2Board.h index 33c897bc..ccbecc7a 100644 --- a/variants/heltec_tracker_v2/HeltecTrackerV2Board.h +++ b/variants/heltec_tracker_v2/HeltecTrackerV2Board.h @@ -21,5 +21,8 @@ public: void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; + bool setLoRaFemLnaEnabled(bool enable) override; + bool canControlLoRaFemLna() const override; + bool isLoRaFemLnaEnabled() const override; }; diff --git a/variants/heltec_tracker_v2/LoRaFEMControl.h b/variants/heltec_tracker_v2/LoRaFEMControl.h index 2c50b742..0ce60fff 100644 --- a/variants/heltec_tracker_v2/LoRaFEMControl.h +++ b/variants/heltec_tracker_v2/LoRaFEMControl.h @@ -12,8 +12,9 @@ class LoRaFEMControl void setRxModeEnable(void); void setRxModeEnableWhenMCUSleep(void); void setLNAEnable(bool enabled); - bool isLnaCanControl(void) { return lna_can_control; } + bool isLnaCanControl(void) const { return lna_can_control; } void setLnaCanControl(bool can_control) { lna_can_control = can_control; } + bool isLNAEnabled(void) const { return lna_enabled; } private: bool lna_enabled = false; From 9664305a872e5d7f52cb4f967dd35935a7423312 Mon Sep 17 00:00:00 2001 From: Quency-D Date: Tue, 24 Mar 2026 16:13:13 +0800 Subject: [PATCH 004/214] Adapt LNA CLI control commands for heltec_t096. --- variants/heltec_t096/LoRaFEMControl.h | 3 ++- variants/heltec_t096/T096Board.cpp | 20 +++++++++++++++++++- variants/heltec_t096/T096Board.h | 3 +++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/variants/heltec_t096/LoRaFEMControl.h b/variants/heltec_t096/LoRaFEMControl.h index 2c50b742..0ce60fff 100644 --- a/variants/heltec_t096/LoRaFEMControl.h +++ b/variants/heltec_t096/LoRaFEMControl.h @@ -12,8 +12,9 @@ class LoRaFEMControl void setRxModeEnable(void); void setRxModeEnableWhenMCUSleep(void); void setLNAEnable(bool enabled); - bool isLnaCanControl(void) { return lna_can_control; } + bool isLnaCanControl(void) const { return lna_can_control; } void setLnaCanControl(bool can_control) { lna_can_control = can_control; } + bool isLNAEnabled(void) const { return lna_enabled; } private: bool lna_enabled = false; diff --git a/variants/heltec_t096/T096Board.cpp b/variants/heltec_t096/T096Board.cpp index 55013157..54425145 100644 --- a/variants/heltec_t096/T096Board.cpp +++ b/variants/heltec_t096/T096Board.cpp @@ -123,4 +123,22 @@ void T096Board::powerOff() { const char* T096Board::getManufacturerName() const { return "Heltec T096"; -} \ No newline at end of file +} + +bool T096Board::setLoRaFemLnaEnabled(bool enable) { + if (!loRaFEMControl.isLnaCanControl()) { + return false; + } + + loRaFEMControl.setLNAEnable(enable); + loRaFEMControl.setRxModeEnable(); + return true; +} + +bool T096Board::canControlLoRaFemLna() const { + return loRaFEMControl.isLnaCanControl(); +} + +bool T096Board::isLoRaFemLnaEnabled() const { + return loRaFEMControl.isLNAEnabled(); +} diff --git a/variants/heltec_t096/T096Board.h b/variants/heltec_t096/T096Board.h index d1e3bdfd..15c7e68b 100644 --- a/variants/heltec_t096/T096Board.h +++ b/variants/heltec_t096/T096Board.h @@ -25,4 +25,7 @@ public: uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; void powerOff() override; + bool setLoRaFemLnaEnabled(bool enable) override; + bool canControlLoRaFemLna() const override; + bool isLoRaFemLnaEnabled() const override; }; From 00c14cd88c30d34640b15c47a1a56f9fa1cc827b Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 17 Apr 2026 16:40:00 -0700 Subject: [PATCH 005/214] Add local-only uf2reset CLI command --- docs/cli_commands.md | 10 ++++++++++ src/helpers/CommonCLI.cpp | 29 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 0e785f4e..57ab6c64 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -30,6 +30,16 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +### Enter the UF2 bootloader (nRF52 only) +**Usage:** +- `uf2reset` + +**Serial Only:** Yes + +**Note:** Reboots directly into the UF2 bootloader on supported nRF52 boards. + +--- + ### Reset the clock and reboot **Usage:** - `clkreboot` diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 2f7a0fff..ab035f66 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -4,6 +4,29 @@ #include "AdvertDataHelpers.h" #include +#if defined(NRF52_PLATFORM) +#include +#include + +#ifndef DFU_MAGIC_UF2_RESET +#define DFU_MAGIC_UF2_RESET 0x57 +#endif + +static void resetToUf2Bootloader() { + uint8_t sd_enabled = 0; + sd_softdevice_is_enabled(&sd_enabled); + + if (sd_enabled) { + sd_power_gpregret_clr(0, 0xFF); + sd_power_gpregret_set(0, DFU_MAGIC_UF2_RESET); + } else { + NRF_POWER->GPREGRET = DFU_MAGIC_UF2_RESET; + } + + NVIC_SystemReset(); +} +#endif + #ifndef BRIDGE_MAX_BAUD #define BRIDGE_MAX_BAUD 115200 #endif @@ -210,6 +233,12 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch _board->powerOff(); // doesn't return } else if (memcmp(command, "reboot", 6) == 0) { _board->reboot(); // doesn't return + } else if (sender_timestamp == 0 && memcmp(command, "uf2reset", 8) == 0 && (command[8] == 0 || command[8] == ' ')) { // from serial command line only +#if defined(NRF52_PLATFORM) + resetToUf2Bootloader(); // doesn't return +#else + strcpy(reply, "ERR: unsupported"); +#endif } else if (memcmp(command, "clkreboot", 9) == 0) { // Reset clock getRTCClock()->setCurrentTime(1715770351); // 15 May 2024, 8:50pm From 0ce1a550178d82a569c0f4d998a3c576bf549f92 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 20 Apr 2026 13:40:07 -0700 Subject: [PATCH 006/214] Refactor build.sh menu and build flow --- build.sh | 788 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 627 insertions(+), 161 deletions(-) diff --git a/build.sh b/build.sh index 313c4c47..cd45e8d8 100755 --- a/build.sh +++ b/build.sh @@ -1,9 +1,41 @@ #!/usr/bin/env bash +ALL_PIO_ENVS=() +PIO_CONFIG_JSON="" +MENU_CHOICE="" +SELECTED_TARGET="" + +ENV_VARIANT_SUFFIX_PATTERN='companion_radio_serial|companion_radio_wifi|companion_radio_usb|comp_radio_usb|companion_usb|companion_radio_ble|companion_ble|repeater_bridge_rs232_serial1|repeater_bridge_rs232_serial2|repeater_bridge_rs232|repeater_bridge_espnow|terminal_chat|room_server|room_svr|kiss_modem|sensor|repeatr|repeater' +BOARD_MODIFIER_WITHOUT_DISPLAY="_without_display" +BOARD_MODIFIER_LOGGING="_logging" +BOARD_MODIFIER_TFT="_tft" +BOARD_MODIFIER_EINK="_eink" +BOARD_MODIFIER_EINK_SUFFIX="Eink" +BOARD_LABEL_WITHOUT_DISPLAY="without_display" +BOARD_LABEL_LOGGING="logging" +BOARD_LABEL_TFT="tft" +BOARD_LABEL_EINK="eink" +DEFAULT_VARIANT_LABEL="default" +TAG_PREFIX_ROOM_SERVER="room-server" +TAG_PREFIX_COMPANION="companion" +TAG_PREFIX_REPEATER="repeater" +BULK_BUILD_SUFFIX_REPEATER="_repeater" +BULK_BUILD_SUFFIX_COMPANION_USB="_companion_radio_usb" +BULK_BUILD_SUFFIX_COMPANION_BLE="_companion_radio_ble" +BULK_BUILD_SUFFIX_ROOM_SERVER="_room_server" +SUPPORTED_PLATFORM_PATTERN='ESP32_PLATFORM|NRF52_PLATFORM|STM32_PLATFORM|RP2040_PLATFORM' +OUTPUT_DIR="out" +FALLBACK_VERSION_PREFIX="dev" +FALLBACK_VERSION_DATE_FORMAT='+%Y-%m-%d-%H-%M' + +# External programs invoked by this script: +# bash, cat, cp, date, git, grep, head, mkdir, pio, python3, rm, sed, sort +# Keep this list in sync when adding or removing non-builtin command usage. + global_usage() { cat - < [target] +bash build.sh [target] Commands: help|usage|-h|--help: Shows this message. @@ -17,21 +49,26 @@ Commands: Examples: Build firmware for the "RAK_4631_repeater" device target -$ sh build.sh build-firmware RAK_4631_repeater +$ bash build.sh build-firmware RAK_4631_repeater + +Run without arguments to choose a target from an interactive menu +$ bash build.sh Build all firmwares for device targets containing the string "RAK_4631" -$ sh build.sh build-matching-firmwares +$ bash build.sh build-matching-firmwares Build all companion firmwares -$ sh build.sh build-companion-firmwares +$ bash build.sh build-companion-firmwares Build all repeater firmwares -$ sh build.sh build-repeater-firmwares +$ bash build.sh build-repeater-firmwares Build all chat room server firmwares -$ sh build.sh build-room-server-firmwares +$ bash build.sh build-room-server-firmwares Environment Variables: + FIRMWARE_VERSION=vX.Y.Z: Firmware version to embed in the build output. + If not set, build.sh derives a default from the latest matching git tag and appends "-dev". DISABLE_DEBUG=1: Disables all debug logging flags (MESH_DEBUG, MESH_PACKET_LOGGING, etc.) If not set, debug flags from variant platformio.ini files are used. @@ -39,60 +76,425 @@ Examples: Build without debug logging: $ export FIRMWARE_VERSION=v1.0.0 $ export DISABLE_DEBUG=1 -$ sh build.sh build-firmware RAK_4631_repeater +$ bash build.sh build-firmware RAK_4631_repeater Build with debug logging (default, uses flags from variant files): $ export FIRMWARE_VERSION=v1.0.0 -$ sh build.sh build-firmware RAK_4631_repeater +$ bash build.sh build-firmware RAK_4631_repeater + +Build with the derived default version from git tags: +$ unset FIRMWARE_VERSION +$ bash build.sh EOF } -# get a list of pio env names that start with "env:" -get_pio_envs() { - pio project config | grep 'env:' | sed 's/env://' +init_project_context() { + if [ ${#ALL_PIO_ENVS[@]} -eq 0 ]; then + mapfile -t ALL_PIO_ENVS < <(pio project config | grep 'env:' | sed 's/env://') + fi + + if [ -z "$PIO_CONFIG_JSON" ]; then + PIO_CONFIG_JSON=$(pio project config --json-output) + fi } -# Catch cries for help before doing anything else. -case $1 in - help|usage|-h|--help) - global_usage - exit 1 - ;; - list|-l) - get_pio_envs - exit 0 - ;; -esac +get_pio_envs() { + if [ ${#ALL_PIO_ENVS[@]} -gt 0 ]; then + printf '%s\n' "${ALL_PIO_ENVS[@]}" + else + pio project config | grep 'env:' | sed 's/env://' + fi +} -# cache project config json for use in get_platform_for_env() -PIO_CONFIG_JSON=$(pio project config --json-output) +canonicalize_variant_suffix() { + local variant_suffix=$1 -# $1 should be the string to find (case insensitive) -get_pio_envs_containing_string() { - shopt -s nocasematch - envs=($(get_pio_envs)) - for env in "${envs[@]}"; do - if [[ "$env" == *${1}* ]]; then - echo $env - fi + case "${variant_suffix,,}" in + comp_radio_usb|companion_usb|companion_radio_usb) + echo "companion_radio_usb" + ;; + companion_ble|companion_radio_ble) + echo "companion_radio_ble" + ;; + room_svr|room_server) + echo "room_server" + ;; + repeatr|repeater) + echo "repeater" + ;; + *) + echo "${variant_suffix,,}" + ;; + esac +} + +trim_trailing_underscores() { + local value=$1 + + while [[ "$value" == *_ ]]; do + value=${value%_} + done + + echo "$value" +} + +sort_lines_case_insensitive() { + sort -f +} + +print_numbered_menu() { + local items=("$@") + local i + + for i in "${!items[@]}"; do + printf '%d) %s\n' "$((i + 1))" "${items[$i]}" done } -# $1 should be the string to find (case insensitive) -get_pio_envs_ending_with_string() { +prompt_menu_choice() { + local prompt_label=$1 + local max_choice=$2 + local allow_back=${3:-0} + local choice + + while true; do + if [ "$allow_back" -eq 1 ]; then + read -r -p "${prompt_label} [1-${max_choice}, B=Back, Q=Quit]: " choice + else + read -r -p "${prompt_label} [1-${max_choice}, Q=Quit]: " choice + fi + + case "${choice^^}" in + Q) + MENU_CHOICE="QUIT" + return 0 + ;; + B) + if [ "$allow_back" -eq 1 ]; then + MENU_CHOICE="BACK" + return 0 + fi + echo "Invalid selection." + ;; + *) + if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le "$max_choice" ]; then + MENU_CHOICE="$choice" + return 0 + fi + echo "Invalid selection." + ;; + esac + done +} + +get_env_metadata() { + local env_name=$1 + local trimmed_env_name + local board_part + local variant_part + local board_family + local board_modifier + local variant_label + local tag_prefix + + trimmed_env_name=$(trim_trailing_underscores "$env_name") + board_part=$trimmed_env_name + variant_part="" + shopt -s nocasematch - envs=($(get_pio_envs)) - for env in "${envs[@]}"; do - if [[ "$env" == *${1} ]]; then - echo $env + # Split a raw env name into board and variant pieces using the normalized + # suffix vocabulary defined near the top of the file. + if [[ "$trimmed_env_name" =~ ^(.+)[_-](${ENV_VARIANT_SUFFIX_PATTERN})$ ]]; then + board_part=${BASH_REMATCH[1]} + variant_part=$(canonicalize_variant_suffix "${BASH_REMATCH[2]}") + fi + + # Fold display and form-factor suffixes into the variant label so related + # boards share one first-level menu entry. + case "$board_part" in + *"$BOARD_MODIFIER_WITHOUT_DISPLAY") + board_family=${board_part%"$BOARD_MODIFIER_WITHOUT_DISPLAY"} + board_modifier="$BOARD_LABEL_WITHOUT_DISPLAY" + ;; + *"$BOARD_MODIFIER_LOGGING") + board_family=${board_part%"$BOARD_MODIFIER_LOGGING"} + board_modifier="$BOARD_LABEL_LOGGING" + ;; + *"$BOARD_MODIFIER_TFT") + board_family=${board_part%"$BOARD_MODIFIER_TFT"} + board_modifier="$BOARD_LABEL_TFT" + ;; + *"$BOARD_MODIFIER_EINK") + board_family=${board_part%"$BOARD_MODIFIER_EINK"} + board_modifier="$BOARD_LABEL_EINK" + ;; + *"$BOARD_MODIFIER_EINK_SUFFIX") + board_family=${board_part%"$BOARD_MODIFIER_EINK_SUFFIX"} + board_modifier="$BOARD_LABEL_EINK" + ;; + *) + board_family=$board_part + board_modifier="" + ;; + esac + shopt -u nocasematch + + variant_label="$variant_part" + if [ -n "$board_modifier" ]; then + if [ -n "$variant_label" ]; then + variant_label="${board_modifier}_${variant_label}" + else + variant_label="$board_modifier" + fi + fi + + if [ -z "$variant_label" ]; then + variant_label="$DEFAULT_VARIANT_LABEL" + fi + + case "$variant_part" in + room_server) + tag_prefix="$TAG_PREFIX_ROOM_SERVER" + ;; + companion_radio_*) + tag_prefix="$TAG_PREFIX_COMPANION" + ;; + repeater*) + tag_prefix="$TAG_PREFIX_REPEATER" + ;; + *) + tag_prefix="" + ;; + esac + + printf '%s\t%s\t%s\n' "$board_family" "$variant_label" "$tag_prefix" +} + +get_metadata_field() { + local env_name=$1 + local field_index=$2 + local metadata + + metadata=$(get_env_metadata "$env_name") + case "$field_index" in + 1) + echo "${metadata%%$'\t'*}" + ;; + 2) + metadata=${metadata#*$'\t'} + echo "${metadata%%$'\t'*}" + ;; + 3) + echo "${metadata##*$'\t'}" + ;; + esac +} + +get_board_family_for_env() { + get_metadata_field "$1" 1 +} + +get_variant_name_for_env() { + get_metadata_field "$1" 2 +} + +get_release_tag_prefix_for_env() { + get_metadata_field "$1" 3 +} + +get_variants_for_board() { + local board_family=$1 + local env + + for env in "${ALL_PIO_ENVS[@]}"; do + if [ "$(get_board_family_for_env "$env")" == "$board_family" ]; then + echo "$env" + fi + done | sort_lines_case_insensitive +} + +prompt_for_variant_for_board() { + local board=$1 + local -A seen_variant_labels=() + local variants + local variant_labels + local i + local j + + mapfile -t variants < <(get_variants_for_board "$board") + if [ ${#variants[@]} -eq 0 ]; then + echo "No firmware variants were found for ${board}." + return 1 + fi + + if [ ${#variants[@]} -eq 1 ]; then + SELECTED_TARGET="${variants[0]}" + return 0 + fi + + variant_labels=() + for i in "${!variants[@]}"; do + variant_labels[i]=$(get_variant_name_for_env "${variants[$i]}") + seen_variant_labels["${variant_labels[$i]}"]=$(( ${seen_variant_labels["${variant_labels[$i]}"]:-0} + 1 )) + done + + # Stop early if normalization would present the user with ambiguous labels. + for i in "${!variant_labels[@]}"; do + if [ "${seen_variant_labels["${variant_labels[$i]}"]}" -gt 1 ]; then + echo "Ambiguous firmware variants detected for ${board}: ${variant_labels[$i]}" + echo "The normalized menu labels are not unique for this board family." + for j in "${!variants[@]}"; do + echo " ${variants[$j]}" + done + exit 1 + fi + done + + echo "Select a firmware variant for ${board}:" + while true; do + print_numbered_menu "${variant_labels[@]}" + prompt_menu_choice "Variant selection" "${#variant_labels[@]}" 1 + if [ "$MENU_CHOICE" == "BACK" ]; then + return 1 + fi + if [ "$MENU_CHOICE" == "QUIT" ]; then + echo "Cancelled." + exit 1 + fi + + SELECTED_TARGET="${variants[$((MENU_CHOICE - 1))]}" + return 0 + done +} + +prompt_for_board_target() { + local -A seen_boards=() + local boards=() + local board + local env + + if ! [ -t 0 ]; then + echo "No command provided and no interactive terminal is available." + global_usage + exit 1 + fi + + if [ ${#ALL_PIO_ENVS[@]} -eq 0 ]; then + echo "No PlatformIO environments were found." + exit 1 + fi + + for env in "${ALL_PIO_ENVS[@]}"; do + board=$(get_board_family_for_env "$env") + if [ -z "${seen_boards[$board]}" ]; then + seen_boards["$board"]=1 + boards+=("$board") + fi + done + + mapfile -t boards < <(printf '%s\n' "${boards[@]}" | sort_lines_case_insensitive) + + echo "No command provided. Select a board family:" + while true; do + print_numbered_menu "${boards[@]}" + prompt_menu_choice "Board selection" "${#boards[@]}" + if [ "$MENU_CHOICE" == "QUIT" ]; then + echo "Cancelled." + exit 1 + fi + + board=${boards[$((MENU_CHOICE - 1))]} + if prompt_for_variant_for_board "$board"; then + echo "Building firmware for ${SELECTED_TARGET}" + return 0 fi done } -# get platform flag for a given environment -# $1 should be the environment name +get_latest_version_from_tags() { + local env_name=$1 + local tag_prefix + local latest_tag + local fallback_version + + fallback_version="${FALLBACK_VERSION_PREFIX}-$(date "${FALLBACK_VERSION_DATE_FORMAT}")" + tag_prefix=$(get_release_tag_prefix_for_env "$env_name") + if [ -z "$tag_prefix" ]; then + echo "$fallback_version" + return 0 + fi + + latest_tag=$(git tag --list "${tag_prefix}-v*" --sort=-version:refname | head -n 1) + if [ -z "$latest_tag" ]; then + echo "$fallback_version" + return 0 + fi + + echo "${latest_tag#"${tag_prefix}"-}" +} + +derive_default_firmware_version() { + local env_name=$1 + local base_version + + base_version=$(get_latest_version_from_tags "$env_name") + case "$base_version" in + *-dev|dev-*) + echo "$base_version" + ;; + *) + echo "${base_version}-dev" + ;; + esac +} + +prompt_for_firmware_version() { + local env_name=$1 + local suggested_version + local entered_version + + suggested_version=$(derive_default_firmware_version "$env_name") + + if ! [ -t 0 ]; then + FIRMWARE_VERSION="$suggested_version" + echo "FIRMWARE_VERSION not set, using derived default: ${FIRMWARE_VERSION}" + return 0 + fi + + echo "Suggested firmware version for ${env_name}: ${suggested_version}" + read -r -e -i "${suggested_version}" -p "Firmware version: " entered_version + FIRMWARE_VERSION="${entered_version:-$suggested_version}" +} + +get_pio_envs_containing_string() { + local env + + shopt -s nocasematch + for env in "${ALL_PIO_ENVS[@]}"; do + if [[ "$env" == *${1}* ]]; then + echo "$env" + fi + done + shopt -u nocasematch +} + +get_pio_envs_ending_with_string() { + local env + + shopt -s nocasematch + for env in "${ALL_PIO_ENVS[@]}"; do + if [[ "$env" == *${1} ]]; then + echo "$env" + fi + done + shopt -u nocasematch +} + get_platform_for_env() { local env_name=$1 + + # PlatformIO exposes project config as JSON; scan the selected env's + # build_flags to recover the platform token used for artifact collection. echo "$PIO_CONFIG_JSON" | python3 -c " import sys, json, re data = json.load(sys.stdin) @@ -101,142 +503,154 @@ for section, options in data: for key, value in options: if key == 'build_flags': for flag in value: - match = re.search(r'(ESP32_PLATFORM|NRF52_PLATFORM|STM32_PLATFORM|RP2040_PLATFORM)', flag) + match = re.search(r'($SUPPORTED_PLATFORM_PATTERN)', flag) if match: print(match.group(1)) sys.exit(0) " } -# disable all debug logging flags if DISABLE_DEBUG=1 is set +is_supported_platform() { + local env_platform=$1 + + [[ "$env_platform" =~ ^(${SUPPORTED_PLATFORM_PATTERN})$ ]] +} + disable_debug_flags() { if [ "$DISABLE_DEBUG" == "1" ]; then export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -UMESH_DEBUG -UBLE_DEBUG_LOGGING -UWIFI_DEBUG_LOGGING -UBRIDGE_DEBUG -UGPS_NMEA_DEBUG -UCORE_DEBUG_LEVEL -UESPNOW_DEBUG_LOGGING -UDEBUG_RP2040_WIRE -UDEBUG_RP2040_SPI -UDEBUG_RP2040_CORE -UDEBUG_RP2040_PORT -URADIOLIB_DEBUG_SPI -UCFG_DEBUG -URADIOLIB_DEBUG_BASIC -URADIOLIB_DEBUG_PROTOCOL" fi } -# build firmware for the provided pio env in $1 +copy_build_output() { + local source_path=$1 + local output_path=$2 + + if [ -f "$source_path" ]; then + cp -- "$source_path" "$output_path" + fi +} + +collect_esp32_artifacts() { + local env_name=$1 + local firmware_filename=$2 + + pio run -t mergebin -e "$env_name" + copy_build_output ".pio/build/${env_name}/firmware.bin" "out/${firmware_filename}.bin" + copy_build_output ".pio/build/${env_name}/firmware-merged.bin" "out/${firmware_filename}-merged.bin" +} + +collect_nrf52_artifacts() { + local env_name=$1 + local firmware_filename=$2 + + python3 bin/uf2conv/uf2conv.py ".pio/build/${env_name}/firmware.hex" -c -o ".pio/build/${env_name}/firmware.uf2" -f 0xADA52840 + copy_build_output ".pio/build/${env_name}/firmware.uf2" "out/${firmware_filename}.uf2" + copy_build_output ".pio/build/${env_name}/firmware.zip" "out/${firmware_filename}.zip" +} + +collect_stm32_artifacts() { + local env_name=$1 + local firmware_filename=$2 + + copy_build_output ".pio/build/${env_name}/firmware.bin" "out/${firmware_filename}.bin" + copy_build_output ".pio/build/${env_name}/firmware.hex" "out/${firmware_filename}.hex" +} + +collect_rp2040_artifacts() { + local env_name=$1 + local firmware_filename=$2 + + copy_build_output ".pio/build/${env_name}/firmware.bin" "out/${firmware_filename}.bin" + copy_build_output ".pio/build/${env_name}/firmware.uf2" "out/${firmware_filename}.uf2" +} + +collect_build_artifacts() { + local env_name=$1 + local env_platform=$2 + local firmware_filename=$3 + + # Post-build outputs differ by platform, so dispatch to the matching + # collector after the main firmware build succeeds. + case "$env_platform" in + ESP32_PLATFORM) + collect_esp32_artifacts "$env_name" "$firmware_filename" + ;; + NRF52_PLATFORM) + collect_nrf52_artifacts "$env_name" "$firmware_filename" + ;; + STM32_PLATFORM) + collect_stm32_artifacts "$env_name" "$firmware_filename" + ;; + RP2040_PLATFORM) + collect_rp2040_artifacts "$env_name" "$firmware_filename" + ;; + esac +} + build_firmware() { - # get env platform for post build actions - ENV_PLATFORM=($(get_platform_for_env $1)) + local env_name=$1 + local env_platform + local commit_hash + local firmware_build_date + local firmware_version_string + local firmware_filename - # get git commit sha - COMMIT_HASH=$(git rev-parse --short HEAD) - - # set firmware build date - FIRMWARE_BUILD_DATE=$(date '+%d-%b-%Y') - - # get FIRMWARE_VERSION, which should be provided by the environment - if [ -z "$FIRMWARE_VERSION" ]; then - echo "FIRMWARE_VERSION must be set in environment" + env_platform=$(get_platform_for_env "$env_name") + if ! is_supported_platform "$env_platform"; then + echo "Unsupported or unknown platform for env: $env_name" exit 1 fi - # set firmware version string - # e.g: v1.0.0-abcdef - FIRMWARE_VERSION_STRING="${FIRMWARE_VERSION}-${COMMIT_HASH}" + commit_hash=$(git rev-parse --short HEAD) + firmware_build_date=$(date '+%d-%b-%Y') - # craft filename - # e.g: RAK_4631_Repeater-v1.0.0-SHA - FIRMWARE_FILENAME="$1-${FIRMWARE_VERSION_STRING}" + if [ -z "$FIRMWARE_VERSION" ]; then + prompt_for_firmware_version "$env_name" + echo "Using firmware version: ${FIRMWARE_VERSION}" + fi - # add firmware version info to end of existing platformio build flags in environment vars - export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DFIRMWARE_BUILD_DATE='\"${FIRMWARE_BUILD_DATE}\"' -DFIRMWARE_VERSION='\"${FIRMWARE_VERSION_STRING}\"'" + firmware_version_string="${FIRMWARE_VERSION}-${commit_hash}" + firmware_filename="${env_name}-${firmware_version_string}" - # disable debug flags if requested + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DFIRMWARE_BUILD_DATE='\"${firmware_build_date}\"' -DFIRMWARE_VERSION='\"${firmware_version_string}\"'" disable_debug_flags - # build firmware target - pio run -e $1 - - # build merge-bin for esp32 fresh install, copy .bins to out folder (e.g: Heltec_v3_room_server-v1.0.0-SHA.bin) - if [ "$ENV_PLATFORM" == "ESP32_PLATFORM" ]; then - pio run -t mergebin -e $1 - cp .pio/build/$1/firmware.bin out/${FIRMWARE_FILENAME}.bin 2>/dev/null || true - cp .pio/build/$1/firmware-merged.bin out/${FIRMWARE_FILENAME}-merged.bin 2>/dev/null || true - fi - - # build .uf2 for nrf52 boards, copy .uf2 and .zip to out folder (e.g: RAK_4631_Repeater-v1.0.0-SHA.uf2) - if [ "$ENV_PLATFORM" == "NRF52_PLATFORM" ]; then - python3 bin/uf2conv/uf2conv.py .pio/build/$1/firmware.hex -c -o .pio/build/$1/firmware.uf2 -f 0xADA52840 - cp .pio/build/$1/firmware.uf2 out/${FIRMWARE_FILENAME}.uf2 2>/dev/null || true - cp .pio/build/$1/firmware.zip out/${FIRMWARE_FILENAME}.zip 2>/dev/null || true - fi - - # for stm32, copy .bin and .hex to out folder - if [ "$ENV_PLATFORM" == "STM32_PLATFORM" ]; then - cp .pio/build/$1/firmware.bin out/${FIRMWARE_FILENAME}.bin 2>/dev/null || true - cp .pio/build/$1/firmware.hex out/${FIRMWARE_FILENAME}.hex 2>/dev/null || true - fi - - # for rp2040, copy .bin and .uf2 to out folder - if [ "$ENV_PLATFORM" == "RP2040_PLATFORM" ]; then - cp .pio/build/$1/firmware.bin out/${FIRMWARE_FILENAME}.bin 2>/dev/null || true - cp .pio/build/$1/firmware.uf2 out/${FIRMWARE_FILENAME}.uf2 2>/dev/null || true - fi - + pio run -e "$env_name" + collect_build_artifacts "$env_name" "$env_platform" "$firmware_filename" } -# firmwares containing $1 will be built build_all_firmwares_matching() { - envs=($(get_pio_envs_containing_string "$1")) + local envs + local env + + mapfile -t envs < <(get_pio_envs_containing_string "$1") for env in "${envs[@]}"; do - build_firmware $env + build_firmware "$env" done } -# firmwares ending with $1 will be built build_all_firmwares_by_suffix() { - envs=($(get_pio_envs_ending_with_string "$1")) + local envs + local env + + mapfile -t envs < <(get_pio_envs_ending_with_string "$1") for env in "${envs[@]}"; do - build_firmware $env + build_firmware "$env" done } build_repeater_firmwares() { - -# # build specific repeater firmwares -# build_firmware "Heltec_v2_repeater" -# build_firmware "Heltec_v3_repeater" -# build_firmware "Xiao_C3_Repeater_sx1262" -# build_firmware "Xiao_S3_WIO_Repeater" -# build_firmware "LilyGo_T3S3_sx1262_Repeater" -# build_firmware "RAK_4631_Repeater" - - # build all repeater firmwares - build_all_firmwares_by_suffix "_repeater" - + build_all_firmwares_by_suffix "$BULK_BUILD_SUFFIX_REPEATER" } build_companion_firmwares() { - -# # build specific companion firmwares -# build_firmware "Heltec_v2_companion_radio_usb" -# build_firmware "Heltec_v2_companion_radio_ble" -# build_firmware "Heltec_v3_companion_radio_usb" -# build_firmware "Heltec_v3_companion_radio_ble" -# build_firmware "Xiao_S3_WIO_companion_radio_ble" -# build_firmware "LilyGo_T3S3_sx1262_companion_radio_usb" -# build_firmware "LilyGo_T3S3_sx1262_companion_radio_ble" -# build_firmware "RAK_4631_companion_radio_usb" -# build_firmware "RAK_4631_companion_radio_ble" -# build_firmware "t1000e_companion_radio_ble" - - # build all companion firmwares - build_all_firmwares_by_suffix "_companion_radio_usb" - build_all_firmwares_by_suffix "_companion_radio_ble" - + build_all_firmwares_by_suffix "$BULK_BUILD_SUFFIX_COMPANION_USB" + build_all_firmwares_by_suffix "$BULK_BUILD_SUFFIX_COMPANION_BLE" } build_room_server_firmwares() { - -# # build specific room server firmwares -# build_firmware "Heltec_v3_room_server" -# build_firmware "RAK_4631_room_server" - - # build all room server firmwares - build_all_firmwares_by_suffix "_room_server" - + build_all_firmwares_by_suffix "$BULK_BUILD_SUFFIX_ROOM_SERVER" } build_firmwares() { @@ -245,34 +659,86 @@ build_firmwares() { build_room_server_firmwares } -# clean build dir -rm -rf out -mkdir -p out +prepare_output_dir() { + local output_dir="$OUTPUT_DIR" -# handle script args -if [[ $1 == "build-firmware" ]]; then - TARGETS=${@:2} - if [ "$TARGETS" ]; then - for env in $TARGETS; do - build_firmware $env - done - else + if [ -z "$output_dir" ] || [ "$output_dir" == "/" ] || [ "$output_dir" == "." ]; then + echo "Refusing to clean unsafe output directory: $output_dir" + exit 1 + fi + + rm -rf -- "$output_dir" + mkdir -p -- "$output_dir" +} + +run_build_firmware_command() { + local targets=("${@:2}") + local env + + if [ ${#targets[@]} -eq 0 ]; then echo "usage: $0 build-firmware " exit 1 fi -elif [[ $1 == "build-matching-firmwares" ]]; then - if [ "$2" ]; then - build_all_firmwares_matching $2 - else - echo "usage: $0 build-matching-firmwares " - exit 1 + + for env in "${targets[@]}"; do + build_firmware "$env" + done +} + +run_command() { + case "$1" in + build-firmware) + run_build_firmware_command "$@" + ;; + build-matching-firmwares) + if [ -n "$2" ]; then + build_all_firmwares_matching "$2" + else + echo "usage: $0 build-matching-firmwares " + exit 1 + fi + ;; + build-firmwares) + build_firmwares + ;; + build-companion-firmwares) + build_companion_firmwares + ;; + build-repeater-firmwares) + build_repeater_firmwares + ;; + build-room-server-firmwares) + build_room_server_firmwares + ;; + *) + global_usage + exit 1 + ;; + esac +} + +main() { + case "${1:-}" in + help|usage|-h|--help) + global_usage + exit 0 + ;; + list|-l) + init_project_context + get_pio_envs + exit 0 + ;; + esac + + init_project_context + + if [ $# -eq 0 ]; then + prompt_for_board_target + set -- build-firmware "$SELECTED_TARGET" fi -elif [[ $1 == "build-firmwares" ]]; then - build_firmwares -elif [[ $1 == "build-companion-firmwares" ]]; then - build_companion_firmwares -elif [[ $1 == "build-repeater-firmwares" ]]; then - build_repeater_firmwares -elif [[ $1 == "build-room-server-firmwares" ]]; then - build_room_server_firmwares -fi + + prepare_output_dir + run_command "$@" +} + +main "$@" From 166f804da169bc3d0301fd3cb5ff5bca291c1ebe Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 20 Apr 2026 13:47:01 -0700 Subject: [PATCH 007/214] Remove unrelated CLI changes from PR branch --- docs/cli_commands.md | 10 ---------- src/helpers/CommonCLI.cpp | 29 ----------------------------- 2 files changed, 39 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 57ab6c64..0e785f4e 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -30,16 +30,6 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- -### Enter the UF2 bootloader (nRF52 only) -**Usage:** -- `uf2reset` - -**Serial Only:** Yes - -**Note:** Reboots directly into the UF2 bootloader on supported nRF52 boards. - ---- - ### Reset the clock and reboot **Usage:** - `clkreboot` diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index ab035f66..2f7a0fff 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -4,29 +4,6 @@ #include "AdvertDataHelpers.h" #include -#if defined(NRF52_PLATFORM) -#include -#include - -#ifndef DFU_MAGIC_UF2_RESET -#define DFU_MAGIC_UF2_RESET 0x57 -#endif - -static void resetToUf2Bootloader() { - uint8_t sd_enabled = 0; - sd_softdevice_is_enabled(&sd_enabled); - - if (sd_enabled) { - sd_power_gpregret_clr(0, 0xFF); - sd_power_gpregret_set(0, DFU_MAGIC_UF2_RESET); - } else { - NRF_POWER->GPREGRET = DFU_MAGIC_UF2_RESET; - } - - NVIC_SystemReset(); -} -#endif - #ifndef BRIDGE_MAX_BAUD #define BRIDGE_MAX_BAUD 115200 #endif @@ -233,12 +210,6 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch _board->powerOff(); // doesn't return } else if (memcmp(command, "reboot", 6) == 0) { _board->reboot(); // doesn't return - } else if (sender_timestamp == 0 && memcmp(command, "uf2reset", 8) == 0 && (command[8] == 0 || command[8] == ' ')) { // from serial command line only -#if defined(NRF52_PLATFORM) - resetToUf2Bootloader(); // doesn't return -#else - strcpy(reply, "ERR: unsupported"); -#endif } else if (memcmp(command, "clkreboot", 9) == 0) { // Reset clock getRTCClock()->setCurrentTime(1715770351); // 15 May 2024, 8:50pm From 4c393348dfd9a55e48e2f6bcd77ce70e4e6aa5e9 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 20 Apr 2026 13:53:38 -0700 Subject: [PATCH 008/214] Add new line at the end of the file. --- build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/build.sh b/build.sh index cd45e8d8..4b47220d 100755 --- a/build.sh +++ b/build.sh @@ -742,3 +742,4 @@ main() { } main "$@" + From d8552e33448391d90a0275a013db3b23d8da8b39 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 20 Apr 2026 14:06:39 -0700 Subject: [PATCH 009/214] Better json arg passing in build.sh --- build.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build.sh b/build.sh index 4b47220d..30b73b5f 100755 --- a/build.sh +++ b/build.sh @@ -495,7 +495,8 @@ get_platform_for_env() { # PlatformIO exposes project config as JSON; scan the selected env's # build_flags to recover the platform token used for artifact collection. - echo "$PIO_CONFIG_JSON" | python3 -c " + # Feed the cached JSON via stdin to avoid shell echo quirks and argv/env size limits. + python3 -c " import sys, json, re data = json.load(sys.stdin) for section, options in data: @@ -507,7 +508,7 @@ for section, options in data: if match: print(match.group(1)) sys.exit(0) -" +" <<<"$PIO_CONFIG_JSON" } is_supported_platform() { From 8525b4e980a9eef86cf4cf475f7936b3cd69a101 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 21 Apr 2026 16:24:00 -0700 Subject: [PATCH 010/214] For packets with a path set; auto try again if no echo was heard --- docs/cli_commands.md | 28 +++ examples/simple_repeater/MyMesh.cpp | 71 ++++++++ examples/simple_repeater/MyMesh.h | 7 + src/Dispatcher.cpp | 4 +- src/Dispatcher.h | 2 + src/Mesh.cpp | 271 +++++++++++++++++++++++++++- src/Mesh.h | 41 +++++ src/helpers/CommonCLI.cpp | 51 +++++- src/helpers/CommonCLI.h | 4 +- src/helpers/SimpleMeshTables.h | 174 +++++++++++++++--- 10 files changed, 620 insertions(+), 33 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 0e785f4e..a5b17af9 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -467,6 +467,34 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### View or change whether direct retries can fall back to the recently-heard repeater list +**Usage:** +- `get direct.retry.heard` +- `set direct.retry.heard ` + +**Parameters:** +- `state`: `on`|`off` + +**Default:** `off` + +**Note:** When enabled, a repeater can use recently-heard non-duplicate repeater prefixes as a fallback for direct retry eligibility when no suitable neighbor entry is available. + +--- + +#### View or change the SNR margin used for direct retry eligibility +**Usage:** +- `get direct.retry.margin` +- `set direct.retry.margin ` + +**Parameters:** +- `value`: Margin in dB above the SF-specific receive floor (minimum `0`, default `5`) + +**Default:** `5` + +**Note:** The retry gate uses the active SF floor of `SF5=-2.5`, `SF6=-5`, `SF7=-7.5`, `SF8=-10`, `SF9=-12.5`, `SF10=-15`, `SF11=-17.5`, `SF12=-20`, then adds this margin. + +--- + #### [Experimental] View or change the processing delay for received traffic **Usage:** - `get rxdelay` diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 24e88949..33c350e3 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -40,6 +40,9 @@ #ifndef TXT_ACK_DELAY #define TXT_ACK_DELAY 200 #endif +#ifndef HALO_DIRECT_RETRY_DELAY_MIN + #define HALO_DIRECT_RETRY_DELAY_MIN 200 +#endif #define FIRMWARE_VER_LEVEL 2 @@ -60,6 +63,20 @@ #define LAZY_CONTACTS_WRITE_DELAY 5000 +const NeighbourInfo* MyMesh::findNeighbourByHash(const uint8_t* hash, uint8_t hash_len) const { +#if MAX_NEIGHBOURS + for (int i = 0; i < MAX_NEIGHBOURS; i++) { + if (neighbours[i].heard_timestamp > 0 && neighbours[i].id.isHashMatch(hash, hash_len)) { + return &neighbours[i]; + } + } +#else + (void)hash; + (void)hash_len; +#endif + return NULL; +} + void MyMesh::putNeighbour(const mesh::Identity &id, uint32_t timestamp, float snr) { #if MAX_NEIGHBOURS // check if neighbours enabled // find existing neighbour, else use least recently updated @@ -399,6 +416,8 @@ File MyMesh::openAppend(const char *fname) { static uint8_t max_loop_minimal[] = { 0, /* 1-byte */ 4, /* 2-byte */ 2, /* 3-byte */ 1 }; static uint8_t max_loop_moderate[] = { 0, /* 1-byte */ 2, /* 2-byte */ 1, /* 3-byte */ 1 }; static uint8_t max_loop_strict[] = { 0, /* 1-byte */ 1, /* 2-byte */ 1, /* 3-byte */ 1 }; +// SF5..SF12 receive floors, scaled by 4 so we can keep the retry gate in int8_t quarter-dB units. +static const int8_t direct_retry_floor_x4[] = { -10, -20, -30, -40, -50, -60, -70, -80 }; bool MyMesh::isLooped(const mesh::Packet* packet, const uint8_t max_counters[]) { uint8_t hash_size = packet->getPathHashSize(); @@ -531,6 +550,44 @@ uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) { uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * _prefs.direct_tx_delay_factor); return getRNG()->nextInt(0, 5*t + 1); } +int8_t MyMesh::getDirectRetryMinSNRX4() const { + // Use the live SF so `tempradio` changes immediately affect the retry threshold. + uint8_t sf = constrain(active_sf, (uint8_t)5, (uint8_t)12); + int16_t threshold = direct_retry_floor_x4[sf - 5] + ((int16_t)_prefs.direct_retry_snr_margin_db * 4); + return (int8_t)constrain(threshold, -128, 127); +} +bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const { + if (_prefs.disable_fwd) { + return false; + } + + int8_t min_snr_x4 = getDirectRetryMinSNRX4(); + const NeighbourInfo* neighbour = findNeighbourByHash(next_hop_hash, next_hop_hash_len); + // Prefer the explicit neighbor table first; it is the strongest signal that this hop is still reachable. + if (neighbour != NULL && neighbour->snr >= min_snr_x4) { + return true; + } + + if (!_prefs.direct_retry_recent_enabled) { + return false; + } + + // If no neighbor entry exists, fall back to the recent-heard repeater cache keyed by the same path prefix. + const auto* recent = ((const SimpleMeshTables *)getTables())->findRecentRepeaterByHash(next_hop_hash, next_hop_hash_len); + return recent != NULL && recent->snr_x4 >= min_snr_x4; +} +uint32_t MyMesh::getDirectRetryEchoDelay(const mesh::Packet* packet) const { + // Approximate LoRa line rate in kilobits/sec from the live radio params the repeater is using now. + float kbps = (((float) active_sf) * active_bw * ((float) active_cr)) / ((float) (1UL << active_sf)); + if (kbps <= 0.0f) { + return HALO_DIRECT_RETRY_DELAY_MIN; + } + + // Wait roughly long enough for our transmission, the next hop's receive/forward window, and its echo back. + uint32_t bits = ((uint32_t) packet->getRawLength()) * 8; + uint32_t scaled_wait_millis = (uint32_t) ((((float) bits) * 4.0f) / kbps); + return max((uint32_t) HALO_DIRECT_RETRY_DELAY_MIN, scaled_wait_millis); +} bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) { // just try to determine region for packet (apply later in allowPacketForward()) @@ -859,6 +916,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.rx_delay_base = 0.0f; // turn off by default, was 10.0; _prefs.tx_delay_factor = 0.5f; // was 0.25f _prefs.direct_tx_delay_factor = 0.3f; // was 0.2 + _prefs.direct_retry_recent_enabled = 0; + _prefs.direct_retry_snr_margin_db = 5; StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); _prefs.node_lat = ADVERT_LAT; _prefs.node_lon = ADVERT_LON; @@ -899,6 +958,9 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc pending_discover_tag = 0; pending_discover_until = 0; + active_bw = _prefs.bw; + active_sf = _prefs.sf; + active_cr = _prefs.cr; } void MyMesh::begin(FILESYSTEM *fs) { @@ -917,6 +979,9 @@ void MyMesh::begin(FILESYSTEM *fs) { #endif radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); + active_bw = _prefs.bw; + active_sf = _prefs.sf; + active_cr = _prefs.cr; radio_set_tx_power(_prefs.tx_power_dbm); radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); @@ -1314,12 +1379,18 @@ void MyMesh::loop() { if (set_radio_at && millisHasNowPassed(set_radio_at)) { // apply pending (temporary) radio params set_radio_at = 0; // clear timer radio_set_params(pending_freq, pending_bw, pending_sf, pending_cr); + active_bw = pending_bw; + active_sf = pending_sf; + active_cr = pending_cr; MESH_DEBUG_PRINTLN("Temp radio params"); } if (revert_radio_at && millisHasNowPassed(revert_radio_at)) { // revert radio params to orig revert_radio_at = 0; // clear timer radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); + active_bw = _prefs.bw; + active_sf = _prefs.sf; + active_cr = _prefs.cr; MESH_DEBUG_PRINTLN("Radio params restored"); } diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 92958448..2d7d8dc1 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -109,8 +109,11 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { unsigned long set_radio_at, revert_radio_at; float pending_freq; float pending_bw; + float active_bw; // live BW, including temporary radio overrides uint8_t pending_sf; + uint8_t active_sf; // live SF, including temporary radio overrides uint8_t pending_cr; + uint8_t active_cr; // live CR, including temporary radio overrides int matching_peer_indexes[MAX_CLIENTS]; #if defined(WITH_RS232_BRIDGE) RS232Bridge bridge; @@ -118,6 +121,8 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { ESPNowBridge bridge; #endif + const NeighbourInfo* findNeighbourByHash(const uint8_t* hash, uint8_t hash_len) const; + int8_t getDirectRetryMinSNRX4() const; void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr); void sendNodeDiscoverReq(); uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood); @@ -146,6 +151,8 @@ protected: uint32_t getRetransmitDelay(const mesh::Packet* packet) override; uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override; + bool allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const override; + uint32_t getDirectRetryEchoDelay(const mesh::Packet* packet) const override; int getInterferenceThreshold() const override { return _prefs.interference_threshold; diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index 9d7a1113..cccbd36c 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -106,6 +106,7 @@ void Dispatcher::loop() { _radio->onSendFinished(); logTx(outbound, 2 + outbound->getPathByteLen() + outbound->payload_len); + onSendComplete(outbound); if (outbound->isRouteFlood()) { n_sent_flood++; } else { @@ -118,6 +119,7 @@ void Dispatcher::loop() { _radio->onSendFinished(); logTxFail(outbound, 2 + outbound->getPathByteLen() + outbound->payload_len); + onSendFail(outbound); releasePacket(outbound); // return to pool outbound = NULL; @@ -386,4 +388,4 @@ unsigned long Dispatcher::futureMillis(int millis_from_now) const { return _ms->getMillis() + millis_from_now; } -} \ No newline at end of file +} diff --git a/src/Dispatcher.h b/src/Dispatcher.h index 2a99d068..163c6196 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -159,6 +159,8 @@ protected: virtual void logRx(Packet* packet, int len, float score) { } // hooks for custom logging virtual void logTx(Packet* packet, int len) { } virtual void logTxFail(Packet* packet, int len) { } + virtual void onSendComplete(Packet* packet) { } + virtual void onSendFail(Packet* packet) { } virtual const char* getLogDateTime() { return ""; } virtual float getAirtimeBudgetFactor() const; diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 57fee140..b9b39c95 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -4,11 +4,32 @@ namespace mesh { void Mesh::begin() { + for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { + _direct_retries[i].packet = NULL; + _direct_retries[i].trigger_packet = NULL; + _direct_retries[i].retry_at = 0; + _direct_retries[i].retry_delay = 0; + _direct_retries[i].priority = 0; + _direct_retries[i].progress_marker = 0; + _direct_retries[i].expect_path_growth = false; + _direct_retries[i].queued = false; + _direct_retries[i].active = false; + } Dispatcher::begin(); } void Mesh::loop() { Dispatcher::loop(); + + for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { + if (!_direct_retries[i].active || !_direct_retries[i].queued || !millisHasNowPassed(_direct_retries[i].retry_at)) { + continue; + } + + if (!isDirectRetryQueued(_direct_retries[i].packet)) { + clearDirectRetrySlot(i); + } + } } bool Mesh::allowPacketForward(const mesh::Packet* packet) { @@ -22,10 +43,25 @@ uint32_t Mesh::getRetransmitDelay(const mesh::Packet* packet) { uint32_t Mesh::getDirectRetransmitDelay(const Packet* packet) { return 0; // by default, no delay } +bool Mesh::allowDirectRetry(const Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const { + return false; +} +uint32_t Mesh::getDirectRetryEchoDelay(const Packet* packet) const { + // Keep the base fallback aligned with the repeater's minimum retry wait. + return 200; +} uint8_t Mesh::getExtraAckTransmitCount() const { return 0; } +void Mesh::onSendComplete(Packet* packet) { + armDirectRetryOnSendComplete(packet); +} + +void Mesh::onSendFail(Packet* packet) { + clearPendingDirectRetryOnSendFail(packet); +} + uint32_t Mesh::getCADFailRetryDelay() const { return _rng->nextInt(1, 4)*120; } @@ -39,6 +75,10 @@ int Mesh::searchChannelsByHash(const uint8_t* hash, GroupChannel channels[], int } DispatcherAction Mesh::onRecvPacket(Packet* pkt) { + if (pkt->isRouteDirect()) { + cancelDirectRetryOnEcho(pkt); + } + if (pkt->isRouteDirect() && pkt->getPayloadType() == PAYLOAD_TYPE_TRACE) { if (pkt->path_len < MAX_PATH_SIZE) { uint8_t i = 0; @@ -58,6 +98,7 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { pkt->path[pkt->path_len++] = (int8_t) (pkt->getSNR()*4); uint32_t d = getDirectRetransmitDelay(pkt); + maybeScheduleDirectRetry(pkt, 5); return ACTION_RETRANSMIT_DELAYED(5, d); // schedule with priority 5 (for now), maybe make configurable? } } @@ -98,6 +139,7 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { removeSelfFromPath(pkt); uint32_t d = getDirectRetransmitDelay(pkt); + maybeScheduleDirectRetry(pkt, 0); return ACTION_RETRANSMIT_DELAYED(0, d); // Routed traffic is HIGHEST priority } } @@ -372,6 +414,7 @@ void Mesh::routeDirectRecvAcks(Packet* packet, uint32_t delay_millis) { a1->path_len = Packet::copyPath(a1->path, packet->path, packet->path_len); a1->header &= ~PH_ROUTE_MASK; a1->header |= ROUTE_TYPE_DIRECT; + maybeScheduleDirectRetry(a1, 0); sendPacket(a1, 0, delay_millis); } extra--; @@ -382,11 +425,225 @@ void Mesh::routeDirectRecvAcks(Packet* packet, uint32_t delay_millis) { a2->path_len = Packet::copyPath(a2->path, packet->path, packet->path_len); a2->header &= ~PH_ROUTE_MASK; a2->header |= ROUTE_TYPE_DIRECT; + maybeScheduleDirectRetry(a2, 0); sendPacket(a2, 0, delay_millis); } } } +void Mesh::clearDirectRetrySlot(int idx) { + _direct_retries[idx].packet = NULL; + _direct_retries[idx].trigger_packet = NULL; + _direct_retries[idx].retry_at = 0; + _direct_retries[idx].retry_delay = 0; + _direct_retries[idx].priority = 0; + _direct_retries[idx].progress_marker = 0; + _direct_retries[idx].expect_path_growth = false; + _direct_retries[idx].queued = false; + _direct_retries[idx].active = false; +} + +bool Mesh::isDirectRetryQueued(const Packet* packet) const { + for (int i = 0; i < _mgr->getOutboundTotal(); i++) { + if (_mgr->getOutboundByIdx(i) == packet) { + return true; + } + } + return false; +} + +void Mesh::calculateDirectRetryKey(const Packet* packet, uint8_t* dest_key) const { + uint8_t type = packet->getPayloadType(); + Utils::sha256(dest_key, MAX_HASH_SIZE, &type, 1, packet->payload, packet->payload_len); +} + +bool Mesh::cancelDirectRetryOnEcho(const Packet* packet) { + uint8_t recv_key[MAX_HASH_SIZE]; + calculateDirectRetryKey(packet, recv_key); + + bool cleared = false; + for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { + if (!_direct_retries[i].active || memcmp(recv_key, _direct_retries[i].retry_key, MAX_HASH_SIZE) != 0) { + continue; + } + + bool is_echo = _direct_retries[i].expect_path_growth + ? packet->path_len > _direct_retries[i].progress_marker + : packet->getPathHashCount() < _direct_retries[i].progress_marker; + if (!is_echo) { + continue; + } + + if (_direct_retries[i].queued) { + for (int j = 0; j < _mgr->getOutboundTotal(); j++) { + if (_mgr->getOutboundByIdx(j) == _direct_retries[i].packet) { + Packet* pending = _mgr->removeOutboundByIdx(j); + if (pending) { + releasePacket(pending); + } + break; + } + } + clearDirectRetrySlot(i); + } else { + clearDirectRetrySlot(i); + } + cleared = true; + } + + return cleared; +} + +void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { + for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { + if (!_direct_retries[i].active) { + continue; + } + + if (_direct_retries[i].queued) { + if (_direct_retries[i].packet == packet) { + // The retry packet itself just finished transmitting; Dispatcher will release it after this hook. + clearDirectRetrySlot(i); + } + continue; + } + + if (_direct_retries[i].trigger_packet != packet) { + continue; + } + + // Allocate the retry packet only after TX-complete so busy repeaters do not reserve pool slots early. + Packet* retry = obtainNewPacket(); + if (retry == NULL) { + clearDirectRetrySlot(i); + continue; + } + + *retry = *packet; + + // Start the echo wait only after the initial direct transmission actually completed. + sendPacket(retry, _direct_retries[i].priority, _direct_retries[i].retry_delay); + if (isDirectRetryQueued(retry)) { + _direct_retries[i].packet = retry; + _direct_retries[i].trigger_packet = NULL; + _direct_retries[i].queued = true; + _direct_retries[i].retry_at = futureMillis(_direct_retries[i].retry_delay); + } else { + clearDirectRetrySlot(i); + } + } +} + +void Mesh::clearPendingDirectRetryOnSendFail(const Packet* packet) { + for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { + if (!_direct_retries[i].active) { + continue; + } + + if (_direct_retries[i].queued) { + if (_direct_retries[i].packet == packet) { + // The queued retry itself failed; Dispatcher will release it after this hook. + clearDirectRetrySlot(i); + } + continue; + } + + if (_direct_retries[i].trigger_packet == packet) { + clearDirectRetrySlot(i); + } + } +} + +bool Mesh::getDirectRetryTarget(const Packet* packet, const uint8_t*& next_hop_hash, uint8_t& next_hop_hash_len, + uint8_t& progress_marker, bool& expect_path_growth) const { + switch (packet->getPayloadType()) { + case PAYLOAD_TYPE_ACK: + case PAYLOAD_TYPE_PATH: + case PAYLOAD_TYPE_REQ: + case PAYLOAD_TYPE_RESPONSE: + case PAYLOAD_TYPE_TXT_MSG: + case PAYLOAD_TYPE_ANON_REQ: + if (packet->getPathHashCount() <= 1) { + return false; + } + next_hop_hash = packet->path; + next_hop_hash_len = packet->getPathHashSize(); + progress_marker = packet->getPathHashCount(); + expect_path_growth = false; + return true; + + case PAYLOAD_TYPE_MULTIPART: + if (packet->payload_len < 1 || (packet->payload[0] & 0x0F) != PAYLOAD_TYPE_ACK || packet->getPathHashCount() <= 1) { + return false; + } + next_hop_hash = packet->path; + next_hop_hash_len = packet->getPathHashSize(); + progress_marker = packet->getPathHashCount(); + expect_path_growth = false; + return true; + + case PAYLOAD_TYPE_TRACE: { + if (packet->payload_len < 9) { + return false; + } + + uint8_t hash_size = 1 << (packet->payload[8] & 0x03); + uint8_t route_bytes = packet->payload_len - 9; + uint8_t offset = packet->path_len * hash_size; + if (offset + hash_size > route_bytes) { + return false; + } + if (offset + (2 * hash_size) > route_bytes) { + return false; // no downstream repeater means there will be no forward echo to overhear. + } + + next_hop_hash = &packet->payload[9 + offset]; + next_hop_hash_len = hash_size; + progress_marker = packet->path_len; + expect_path_growth = true; + return true; + } + + default: + return false; + } +} + +void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { + const uint8_t* next_hop_hash; + uint8_t next_hop_hash_len; + uint8_t progress_marker; + bool expect_path_growth; + if (!getDirectRetryTarget(packet, next_hop_hash, next_hop_hash_len, progress_marker, expect_path_growth) + || !allowDirectRetry(packet, next_hop_hash, next_hop_hash_len)) { + return; + } + + int slot_idx = -1; + for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { + if (!_direct_retries[i].active) { + slot_idx = i; + break; + } + } + if (slot_idx < 0) { + return; + } + + // Only store retry metadata here; allocate the retry packet after the initial TX really completes. + uint32_t retry_delay = getDirectRetryEchoDelay(packet); + calculateDirectRetryKey(packet, _direct_retries[slot_idx].retry_key); + _direct_retries[slot_idx].packet = NULL; + _direct_retries[slot_idx].trigger_packet = const_cast(packet); + _direct_retries[slot_idx].retry_at = 0; + _direct_retries[slot_idx].retry_delay = retry_delay; + _direct_retries[slot_idx].priority = priority; + _direct_retries[slot_idx].progress_marker = progress_marker; + _direct_retries[slot_idx].expect_path_growth = expect_path_growth; + _direct_retries[slot_idx].queued = false; + _direct_retries[slot_idx].active = true; +} + Packet* Mesh::createAdvert(const LocalIdentity& id, const uint8_t* app_data, size_t app_data_len) { if (app_data_len > MAX_ADVERT_DATA_SIZE) return NULL; @@ -634,7 +891,7 @@ void Mesh::sendFlood(Packet* packet, uint32_t delay_millis, uint8_t path_hash_si packet->header |= ROUTE_TYPE_FLOOD; packet->setPathHashSizeAndCount(path_hash_size, 0); - _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us + _tables->markSent(packet); // mark this packet as already sent in case it is rebroadcast back to us uint8_t pri; if (packet->getPayloadType() == PAYLOAD_TYPE_PATH) { @@ -663,7 +920,7 @@ void Mesh::sendFlood(Packet* packet, uint16_t* transport_codes, uint32_t delay_m packet->transport_codes[1] = transport_codes[1]; packet->setPathHashSizeAndCount(path_hash_size, 0); - _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us + _tables->markSent(packet); // mark this packet as already sent in case it is rebroadcast back to us uint8_t pri; if (packet->getPayloadType() == PAYLOAD_TYPE_PATH) { @@ -696,7 +953,8 @@ void Mesh::sendDirect(Packet* packet, const uint8_t* path, uint8_t path_len, uin pri = 0; } } - _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us + _tables->markSent(packet); // mark this packet as already sent in case it is rebroadcast back to us + maybeScheduleDirectRetry(packet, pri); sendPacket(packet, pri, delay_millis); } @@ -706,7 +964,7 @@ void Mesh::sendZeroHop(Packet* packet, uint32_t delay_millis) { packet->path_len = 0; // path_len of zero means Zero Hop - _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us + _tables->markSent(packet); // mark this packet as already sent in case it is rebroadcast back to us sendPacket(packet, 0, delay_millis); } @@ -719,9 +977,10 @@ void Mesh::sendZeroHop(Packet* packet, uint16_t* transport_codes, uint32_t delay packet->path_len = 0; // path_len of zero means Zero Hop - _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us + _tables->markSent(packet); // mark this packet as already sent in case it is rebroadcast back to us sendPacket(packet, 0, delay_millis); } -} \ No newline at end of file +} + diff --git a/src/Mesh.h b/src/Mesh.h index f9f87863..94bfec9f 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -4,6 +4,10 @@ namespace mesh { +#ifndef MAX_DIRECT_RETRY_SLOTS + #define MAX_DIRECT_RETRY_SLOTS 6 +#endif + class GroupChannel { public: uint8_t hash[PATH_HASH_SIZE]; @@ -16,6 +20,7 @@ public: class MeshTables { public: virtual bool hasSeen(const Packet* packet) = 0; + virtual void markSent(const Packet* packet) = 0; virtual void clear(const Packet* packet) = 0; // remove this packet hash from table }; @@ -24,17 +29,42 @@ public: * and provides virtual methods for sub-classes on handling incoming, and also preparing outbound Packets. */ class Mesh : public Dispatcher { + struct DirectRetryEntry { + Packet* packet; + Packet* trigger_packet; + unsigned long retry_at; + uint32_t retry_delay; + uint8_t retry_key[MAX_HASH_SIZE]; + uint8_t priority; + uint8_t progress_marker; + bool expect_path_growth; + bool queued; + bool active; + }; + RTCClock* _rtc; RNG* _rng; MeshTables* _tables; + DirectRetryEntry _direct_retries[MAX_DIRECT_RETRY_SLOTS]; void removeSelfFromPath(Packet* packet); void routeDirectRecvAcks(Packet* packet, uint32_t delay_millis); + void clearDirectRetrySlot(int idx); + bool isDirectRetryQueued(const Packet* packet) const; + void calculateDirectRetryKey(const Packet* packet, uint8_t* dest_key) const; + bool cancelDirectRetryOnEcho(const Packet* packet); + void armDirectRetryOnSendComplete(const Packet* packet); + void clearPendingDirectRetryOnSendFail(const Packet* packet); + bool getDirectRetryTarget(const Packet* packet, const uint8_t*& next_hop_hash, uint8_t& next_hop_hash_len, + uint8_t& progress_marker, bool& expect_path_growth) const; + void maybeScheduleDirectRetry(const Packet* packet, uint8_t priority); //void routeRecvAcks(Packet* packet, uint32_t delay_millis); DispatcherAction forwardMultipartDirect(Packet* pkt); protected: DispatcherAction onRecvPacket(Packet* pkt) override; + void onSendComplete(Packet* packet) override; + void onSendFail(Packet* packet) override; virtual uint32_t getCADFailRetryDelay() const override; @@ -65,6 +95,17 @@ protected: */ virtual uint32_t getDirectRetransmitDelay(const Packet* packet); + /** + * \brief Decide whether a DIRECT packet should get one delayed retry if the next hop echo is not overheard. + * Sub-classes can use neighbour tables or other link-quality data to opt in selectively. + */ + virtual bool allowDirectRetry(const Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const; + + /** + * \returns milliseconds to wait for the next-hop echo before queueing one retry of the DIRECT packet. + */ + virtual uint32_t getDirectRetryEchoDelay(const Packet* packet) const; + /** * \returns number of extra (Direct) ACK transmissions wanted. */ diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 2f7a0fff..c7095b26 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -8,6 +8,13 @@ #define BRIDGE_MAX_BAUD 115200 #endif +// These bytes used to be reserved/unused in persisted prefs, so keep a marker before trusting them. +#define DIRECT_RETRY_PREFS_MAGIC_0 0xD4 +#define DIRECT_RETRY_PREFS_MAGIC_1 0x52 +#define DIRECT_RETRY_RECENT_DEFAULT 0 +#define DIRECT_RETRY_SNR_MARGIN_DB_DEFAULT 5 +#define DIRECT_RETRY_SNR_MARGIN_DB_MAX 40 + // Believe it or not, this std C function is busted on some platforms! static uint32_t _atoi(const char* sp) { uint32_t n = 0; @@ -60,7 +67,9 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->tx_delay_factor, sizeof(_prefs->tx_delay_factor)); // 84 file.read((uint8_t *)&_prefs->guest_password[0], sizeof(_prefs->guest_password)); // 88 file.read((uint8_t *)&_prefs->direct_tx_delay_factor, sizeof(_prefs->direct_tx_delay_factor)); // 104 - file.read(pad, 4); // 108 : 4 bytes unused + file.read((uint8_t *)&_prefs->direct_retry_recent_enabled, sizeof(_prefs->direct_retry_recent_enabled)); // 108 + file.read((uint8_t *)&_prefs->direct_retry_snr_margin_db, sizeof(_prefs->direct_retry_snr_margin_db)); // 109 + file.read((uint8_t *)&_prefs->direct_retry_prefs_magic[0], sizeof(_prefs->direct_retry_prefs_magic)); // 110 file.read((uint8_t *)&_prefs->sf, sizeof(_prefs->sf)); // 112 file.read((uint8_t *)&_prefs->cr, sizeof(_prefs->cr)); // 113 file.read((uint8_t *)&_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114 @@ -102,6 +111,15 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->multi_acks = constrain(_prefs->multi_acks, 0, 1); _prefs->adc_multiplier = constrain(_prefs->adc_multiplier, 0.0f, 10.0f); _prefs->path_hash_mode = constrain(_prefs->path_hash_mode, 0, 2); // NOTE: mode 3 reserved for future + // Old firmware left offset 108..111 undefined, so require the marker before using the new retry prefs. + if (_prefs->direct_retry_prefs_magic[0] != DIRECT_RETRY_PREFS_MAGIC_0 + || _prefs->direct_retry_prefs_magic[1] != DIRECT_RETRY_PREFS_MAGIC_1) { + _prefs->direct_retry_recent_enabled = DIRECT_RETRY_RECENT_DEFAULT; + _prefs->direct_retry_snr_margin_db = DIRECT_RETRY_SNR_MARGIN_DB_DEFAULT; + } else { + _prefs->direct_retry_recent_enabled = constrain(_prefs->direct_retry_recent_enabled, 0, 1); + _prefs->direct_retry_snr_margin_db = constrain(_prefs->direct_retry_snr_margin_db, 0, DIRECT_RETRY_SNR_MARGIN_DB_MAX); + } // sanitise bad bridge pref values _prefs->bridge_enabled = constrain(_prefs->bridge_enabled, 0, 1); @@ -150,7 +168,11 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->tx_delay_factor, sizeof(_prefs->tx_delay_factor)); // 84 file.write((uint8_t *)&_prefs->guest_password[0], sizeof(_prefs->guest_password)); // 88 file.write((uint8_t *)&_prefs->direct_tx_delay_factor, sizeof(_prefs->direct_tx_delay_factor)); // 104 - file.write(pad, 4); // 108 : 4 byte unused + file.write((uint8_t *)&_prefs->direct_retry_recent_enabled, sizeof(_prefs->direct_retry_recent_enabled)); // 108 + file.write((uint8_t *)&_prefs->direct_retry_snr_margin_db, sizeof(_prefs->direct_retry_snr_margin_db)); // 109 + // Persist a marker so later loads can distinguish real values from legacy garbage in this reserved slot. + uint8_t retry_magic[2] = { DIRECT_RETRY_PREFS_MAGIC_0, DIRECT_RETRY_PREFS_MAGIC_1 }; + file.write(retry_magic, sizeof(retry_magic)); // 110 file.write((uint8_t *)&_prefs->sf, sizeof(_prefs->sf)); // 112 file.write((uint8_t *)&_prefs->cr, sizeof(_prefs->cr)); // 113 file.write((uint8_t *)&_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114 @@ -338,6 +360,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch sprintf(reply, "> %d", (uint32_t)_prefs->flood_max); } else if (memcmp(config, "direct.txdelay", 14) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor)); + } else if (memcmp(config, "direct.retry.heard", 18) == 0) { + sprintf(reply, "> %s", _prefs->direct_retry_recent_enabled ? "on" : "off"); + } else if (memcmp(config, "direct.retry.margin", 19) == 0) { + sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_snr_margin_db); } else if (memcmp(config, "owner.info", 10) == 0) { *reply++ = '>'; *reply++ = ' '; @@ -587,6 +613,27 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch } else { strcpy(reply, "Error, cannot be negative"); } + } else if (memcmp(config, "direct.retry.heard ", 19) == 0) { + if (memcmp(&config[19], "on", 2) == 0) { + _prefs->direct_retry_recent_enabled = 1; + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(&config[19], "off", 3) == 0) { + _prefs->direct_retry_recent_enabled = 0; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be on or off"); + } + } else if (memcmp(config, "direct.retry.margin ", 20) == 0) { + int db = atoi(&config[20]); + if (db >= 0 && db <= DIRECT_RETRY_SNR_MARGIN_DB_MAX) { + _prefs->direct_retry_snr_margin_db = (uint8_t)db; + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error, min 0 and max %d", DIRECT_RETRY_SNR_MARGIN_DB_MAX); + } } else if (memcmp(config, "owner.info ", 11) == 0) { config += 11; char *dp = _prefs->owner_info; diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 3a4332d1..c1e0c5e9 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -32,7 +32,9 @@ struct NodePrefs { // persisted to file float tx_delay_factor; char guest_password[16]; float direct_tx_delay_factor; - uint32_t guard; + uint8_t direct_retry_recent_enabled; + uint8_t direct_retry_snr_margin_db; + uint8_t direct_retry_prefs_magic[2]; uint8_t sf; uint8_t cr; uint8_t allow_read_only; diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index 2f8af52a..217fd5a0 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -8,13 +8,103 @@ #define MAX_PACKET_HASHES 128 #define MAX_PACKET_ACKS 64 +#define MAX_RECENT_REPEATERS 64 +#define MAX_ROUTE_HASH_BYTES 3 class SimpleMeshTables : public mesh::MeshTables { +public: + struct RecentRepeaterInfo { + // Just enough identity to match a next-hop path prefix plus the SNR that heard it. + uint8_t prefix[MAX_ROUTE_HASH_BYTES]; + uint8_t prefix_len; + int8_t snr_x4; + }; + +private: uint8_t _hashes[MAX_PACKET_HASHES*MAX_HASH_SIZE]; int _next_idx; uint32_t _acks[MAX_PACKET_ACKS]; int _next_ack_idx; uint32_t _direct_dups, _flood_dups; + RecentRepeaterInfo _recent_repeaters[MAX_RECENT_REPEATERS]; + int _next_recent_repeater_idx; + + bool hasSeenAck(uint32_t ack) const { + for (int i = 0; i < MAX_PACKET_ACKS; i++) { + if (ack == _acks[i]) { + return true; + } + } + return false; + } + + void storeAck(uint32_t ack) { + _acks[_next_ack_idx] = ack; + _next_ack_idx = (_next_ack_idx + 1) % MAX_PACKET_ACKS; + } + + bool hasSeenHash(const uint8_t* hash) const { + const uint8_t* sp = _hashes; + for (int i = 0; i < MAX_PACKET_HASHES; i++, sp += MAX_HASH_SIZE) { + if (memcmp(hash, sp, MAX_HASH_SIZE) == 0) { + return true; + } + } + return false; + } + + void storeHash(const uint8_t* hash) { + memcpy(&_hashes[_next_idx*MAX_HASH_SIZE], hash, MAX_HASH_SIZE); + _next_idx = (_next_idx + 1) % MAX_PACKET_HASHES; + } + + bool extractRecentRepeater(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const { + // Learn repeater prefixes only from packet shapes that expose a trustworthy repeater ID. + if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->payload_len >= PUB_KEY_SIZE) { + memcpy(prefix, packet->payload, MAX_ROUTE_HASH_BYTES); + prefix_len = MAX_ROUTE_HASH_BYTES; + return true; + } + + if (packet->getPayloadType() == PAYLOAD_TYPE_CONTROL + && packet->isRouteDirect() + && packet->getPathHashCount() == 0 + && packet->payload_len >= 6 + MAX_ROUTE_HASH_BYTES + && (packet->payload[0] & 0xF0) == 0x90) { + memcpy(prefix, &packet->payload[6], MAX_ROUTE_HASH_BYTES); + prefix_len = MAX_ROUTE_HASH_BYTES; + return true; + } + + if (packet->isRouteFlood() && packet->getPathHashCount() > 0) { + prefix_len = packet->getPathHashSize(); + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + + const uint8_t* last_hop = &packet->path[(packet->getPathHashCount() - 1) * packet->getPathHashSize()]; + memcpy(prefix, last_hop, prefix_len); + return true; + } + + return false; + } + + void recordRecentRepeater(const mesh::Packet* packet) { + uint8_t prefix[MAX_ROUTE_HASH_BYTES] = {0}; + uint8_t prefix_len = 0; + if (!extractRecentRepeater(packet, prefix, prefix_len) || prefix_len == 0) { + return; + } + + // Ring buffer is enough here; retry fallback only needs a recent prefix->SNR observation. + RecentRepeaterInfo& slot = _recent_repeaters[_next_recent_repeater_idx]; + memset(slot.prefix, 0, sizeof(slot.prefix)); + memcpy(slot.prefix, prefix, prefix_len); + slot.prefix_len = prefix_len; + slot.snr_x4 = packet->_snr; + _next_recent_repeater_idx = (_next_recent_repeater_idx + 1) % MAX_RECENT_REPEATERS; + } public: SimpleMeshTables() { @@ -23,6 +113,8 @@ public: memset(_acks, 0, sizeof(_acks)); _next_ack_idx = 0; _direct_dups = _flood_dups = 0; + memset(_recent_repeaters, 0, sizeof(_recent_repeaters)); + _next_recent_repeater_idx = 0; } #ifdef ESP32 @@ -31,12 +123,16 @@ public: f.read((uint8_t *) &_next_idx, sizeof(_next_idx)); f.read((uint8_t *) &_acks[0], sizeof(_acks)); f.read((uint8_t *) &_next_ack_idx, sizeof(_next_ack_idx)); + f.read((uint8_t *) &_recent_repeaters[0], sizeof(_recent_repeaters)); + f.read((uint8_t *) &_next_recent_repeater_idx, sizeof(_next_recent_repeater_idx)); } void saveTo(File f) { f.write(_hashes, sizeof(_hashes)); f.write((const uint8_t *) &_next_idx, sizeof(_next_idx)); f.write((const uint8_t *) &_acks[0], sizeof(_acks)); f.write((const uint8_t *) &_next_ack_idx, sizeof(_next_ack_idx)); + f.write((const uint8_t *) &_recent_repeaters[0], sizeof(_recent_repeaters)); + f.write((const uint8_t *) &_next_recent_repeater_idx, sizeof(_next_recent_repeater_idx)); } #endif @@ -44,28 +140,8 @@ public: if (packet->getPayloadType() == PAYLOAD_TYPE_ACK) { uint32_t ack; memcpy(&ack, packet->payload, 4); - for (int i = 0; i < MAX_PACKET_ACKS; i++) { - if (ack == _acks[i]) { - if (packet->isRouteDirect()) { - _direct_dups++; // keep some stats - } else { - _flood_dups++; - } - return true; - } - } - - _acks[_next_ack_idx] = ack; - _next_ack_idx = (_next_ack_idx + 1) % MAX_PACKET_ACKS; // cyclic table - return false; - } - uint8_t hash[MAX_HASH_SIZE]; - packet->calculatePacketHash(hash); - - const uint8_t* sp = _hashes; - for (int i = 0; i < MAX_PACKET_HASHES; i++, sp += MAX_HASH_SIZE) { - if (memcmp(hash, sp, MAX_HASH_SIZE) == 0) { + if (hasSeenAck(ack)) { if (packet->isRouteDirect()) { _direct_dups++; // keep some stats } else { @@ -73,13 +149,46 @@ public: } return true; } + + storeAck(ack); + return false; } - memcpy(&_hashes[_next_idx*MAX_HASH_SIZE], hash, MAX_HASH_SIZE); - _next_idx = (_next_idx + 1) % MAX_PACKET_HASHES; // cyclic table + uint8_t hash[MAX_HASH_SIZE]; + packet->calculatePacketHash(hash); + + if (hasSeenHash(hash)) { + if (packet->isRouteDirect()) { + _direct_dups++; // keep some stats + } else { + _flood_dups++; + } + return true; + } + + storeHash(hash); + recordRecentRepeater(packet); return false; } + void markSent(const mesh::Packet* packet) override { + // Outbound packets must be marked as already-sent without teaching the recent-heard cache about ourselves. + if (packet->getPayloadType() == PAYLOAD_TYPE_ACK) { + uint32_t ack; + memcpy(&ack, packet->payload, 4); + if (!hasSeenAck(ack)) { + storeAck(ack); + } + return; + } + + uint8_t hash[MAX_HASH_SIZE]; + packet->calculatePacketHash(hash); + if (!hasSeenHash(hash)) { + storeHash(hash); + } + } + void clear(const mesh::Packet* packet) override { if (packet->getPayloadType() == PAYLOAD_TYPE_ACK) { uint32_t ack; @@ -107,5 +216,24 @@ public: uint32_t getNumDirectDups() const { return _direct_dups; } uint32_t getNumFloodDups() const { return _flood_dups; } + const RecentRepeaterInfo* findRecentRepeaterByHash(const uint8_t* hash, uint8_t hash_len) const { + if (hash == NULL || hash_len == 0) { + return NULL; + } + + // Search newest-to-oldest so the retry gate prefers the freshest SNR sample for a prefix. + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + int idx = (_next_recent_repeater_idx - 1 - i + MAX_RECENT_REPEATERS) % MAX_RECENT_REPEATERS; + const RecentRepeaterInfo* info = &_recent_repeaters[idx]; + if (info->prefix_len < hash_len || info->prefix_len == 0) { + continue; + } + if (memcmp(info->prefix, hash, hash_len) == 0) { + return info; + } + } + return NULL; + } + void resetStats() { _direct_dups = _flood_dups = 0; } }; From 577433ce476745945ae7568c6708716b338303da Mon Sep 17 00:00:00 2001 From: mikecarper Date: Thu, 23 Apr 2026 16:25:51 -0700 Subject: [PATCH 011/214] Retry 3 times with a 200ms,300ms,400ms backoff. --- docs/cli_commands.md | 13 +++++ examples/simple_repeater/MyMesh.cpp | 85 ++++++++++++++++++++++++++++ examples/simple_repeater/MyMesh.h | 2 + src/Dispatcher.h | 1 + src/Mesh.cpp | 56 ++++++++++++++++++- src/Mesh.h | 6 ++ src/helpers/SimpleMeshTables.h | 86 +++++++++++++++++++++++++---- 7 files changed, 235 insertions(+), 14 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 3e79019f..774066b0 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -115,6 +115,19 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +### Get or set recent repeater fallback prefix/SNR +**Usage:** +- `recent.repeater` +- `recent.repeater ` + +**Parameters:** +- `prefix_hex`: 1-3 bytes of next-hop prefix (hex) +- `snr_db`: SNR in dB (supports decimals; stored at x4 precision) + +**Note:** `set` is rejected when the prefix already exists in neighbors. + +--- + ## Statistics ### Clear Stats diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 7966404d..71b532ed 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -77,6 +77,15 @@ const NeighbourInfo* MyMesh::findNeighbourByHash(const uint8_t* hash, uint8_t ha return NULL; } +bool MyMesh::allowRecentRepeaterPrefixStore(const uint8_t* prefix, uint8_t prefix_len, void* ctx) { + if (ctx == NULL || prefix == NULL || prefix_len == 0) { + return true; + } + + const MyMesh* self = (const MyMesh*) ctx; + return self->findNeighbourByHash(prefix, prefix_len) == NULL; +} + void MyMesh::putNeighbour(const mesh::Identity &id, uint32_t timestamp, float snr) { #if MAX_NEIGHBOURS // check if neighbours enabled // find existing neighbour, else use least recently updated @@ -550,6 +559,34 @@ void MyMesh::logTxFail(mesh::Packet *pkt, int len) { } } +void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis) { + if (packet == NULL) { + return; + } + + MESH_DEBUG_PRINTLN("%s direct retry %s (type=%d, route=%s, payload_len=%d, delay=%lu)", + getLogDateTime(), + event, + (uint32_t)packet->getPayloadType(), + packet->isRouteDirect() ? "D" : "F", + (uint32_t)packet->payload_len, + (unsigned long)delay_millis); + + if (_logging) { + File f = openAppend(PACKET_LOG_FILE); + if (f) { + f.print(getLogDateTime()); + f.printf(": DIRECT RETRY %s (type=%d, route=%s, payload_len=%d, delay=%lu)\n", + event, + (uint32_t)packet->getPayloadType(), + packet->isRouteDirect() ? "D" : "F", + (uint32_t)packet->payload_len, + (unsigned long)delay_millis); + f.close(); + } + } +} + int MyMesh::calcRxDelay(float score, uint32_t air_time) const { if (_prefs.rx_delay_base <= 0.0f) return 0; return (int)((pow(_prefs.rx_delay_base, 0.85f - score) - 1.0) * air_time); @@ -976,6 +1013,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc active_bw = _prefs.bw; active_sf = _prefs.sf; active_cr = _prefs.cr; + + ((SimpleMeshTables *)getTables())->setRecentRepeaterAllowFilter(&MyMesh::allowRecentRepeaterPrefixStore, this); } void MyMesh::begin(FILESYSTEM *fs) { @@ -1017,6 +1056,7 @@ void MyMesh::begin(FILESYSTEM *fs) { active_bw = _prefs.bw; active_sf = _prefs.sf; active_cr = _prefs.cr; + ((SimpleMeshTables *)getTables())->setRecentRepeaterMinSNRX4(getDirectRetryMinSNRX4()); radio_set_tx_power(_prefs.tx_power_dbm); radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); @@ -1305,6 +1345,48 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply Serial.printf("\n"); } reply[0] = 0; + } else if (memcmp(command, "recent.repeater", 15) == 0) { + const char* sub = command + 15; + while (*sub == ' ') sub++; + auto* tables = (SimpleMeshTables*)getTables(); + if (*sub == 0) { + const auto* info = tables->getLatestRecentRepeater(); + if (info == NULL) { + strcpy(reply, "> none"); + } else { + char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; + mesh::Utils::toHex(hex, info->prefix, info->prefix_len); + sprintf(reply, "> %s,%s", hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); + } + } else { + char* params = (char*) sub; + char* arg_snr = strchr(params, ' '); + if (arg_snr == NULL) { + strcpy(reply, "Err - usage: recent.repeater "); + } else { + *arg_snr++ = 0; + while (*arg_snr == ' ') arg_snr++; + if (*arg_snr == 0) { + strcpy(reply, "Err - usage: recent.repeater "); + } else { + int hex_len = strlen(params); + int prefix_len = hex_len / 2; + uint8_t prefix[MAX_ROUTE_HASH_BYTES] = {0}; + if ((hex_len % 2) != 0 || prefix_len <= 0 || prefix_len > MAX_ROUTE_HASH_BYTES || !mesh::Utils::fromHex(prefix, prefix_len, params)) { + strcpy(reply, "Err - prefix must be 1-3 bytes hex"); + } else { + float snr_db = strtof(arg_snr, nullptr); + int snr_x4 = (int)(snr_db * 4.0f + (snr_db >= 0.0f ? 0.5f : -0.5f)); + snr_x4 = constrain(snr_x4, -128, 127); + if (tables->setRecentRepeater(prefix, (uint8_t)prefix_len, (int8_t)snr_x4)) { + strcpy(reply, "OK"); + } else { + strcpy(reply, "Err - prefix is already in neighbors"); + } + } + } + } + } } else if (memcmp(command, "discover.neighbors", 18) == 0) { const char* sub = command + 18; while (*sub == ' ') sub++; @@ -1358,6 +1440,9 @@ void MyMesh::loop() { MESH_DEBUG_PRINTLN("Radio params restored"); } + // Keep recent-prefix learning aligned with the live retry SNR gate. + ((SimpleMeshTables *)getTables())->setRecentRepeaterMinSNRX4(getDirectRetryMinSNRX4()); + // is pending dirty contacts write needed? if (dirty_contacts_expiry && millisHasNowPassed(dirty_contacts_expiry)) { acl.save(_fs); diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index d2c84b1a..16566dca 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -123,6 +123,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { #endif const NeighbourInfo* findNeighbourByHash(const uint8_t* hash, uint8_t hash_len) const; + static bool allowRecentRepeaterPrefixStore(const uint8_t* prefix, uint8_t prefix_len, void* ctx); int8_t getDirectRetryMinSNRX4() const; void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr); uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood); @@ -153,6 +154,7 @@ protected: uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override; bool allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const override; uint32_t getDirectRetryEchoDelay(const mesh::Packet* packet) const override; + void onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis) override; int getInterferenceThreshold() const override { return _prefs.interference_threshold; diff --git a/src/Dispatcher.h b/src/Dispatcher.h index 163c6196..90ee5cdb 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -170,6 +170,7 @@ protected: virtual int getInterferenceThreshold() const { return 0; } // disabled by default virtual int getAGCResetInterval() const { return 0; } // disabled by default virtual unsigned long getDutyCycleWindowMs() const { return 3600000; } + const Packet* getOutboundInFlight() const { return outbound; } public: void begin(); diff --git a/src/Mesh.cpp b/src/Mesh.cpp index b9b39c95..b9892eed 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -3,12 +3,16 @@ namespace mesh { +static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS = 3; +static const uint32_t DIRECT_RETRY_BACKOFF_MS[DIRECT_RETRY_MAX_ATTEMPTS] = { 200, 300, 400 }; + void Mesh::begin() { for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { _direct_retries[i].packet = NULL; _direct_retries[i].trigger_packet = NULL; _direct_retries[i].retry_at = 0; _direct_retries[i].retry_delay = 0; + _direct_retries[i].retry_attempts_sent = 0; _direct_retries[i].priority = 0; _direct_retries[i].progress_marker = 0; _direct_retries[i].expect_path_growth = false; @@ -27,6 +31,9 @@ void Mesh::loop() { } if (!isDirectRetryQueued(_direct_retries[i].packet)) { + if (_direct_retries[i].packet == getOutboundInFlight()) { + continue; // currently transmitting; keep slot until onSendComplete/onSendFail emits event + } clearDirectRetrySlot(i); } } @@ -436,6 +443,7 @@ void Mesh::clearDirectRetrySlot(int idx) { _direct_retries[idx].trigger_packet = NULL; _direct_retries[idx].retry_at = 0; _direct_retries[idx].retry_delay = 0; + _direct_retries[idx].retry_attempts_sent = 0; _direct_retries[idx].priority = 0; _direct_retries[idx].progress_marker = 0; _direct_retries[idx].expect_path_growth = false; @@ -484,8 +492,12 @@ bool Mesh::cancelDirectRetryOnEcho(const Packet* packet) { break; } } + onDirectRetryEvent("canceled_echo", _direct_retries[i].packet, 0); + onDirectRetryEvent("good", _direct_retries[i].packet, 0); clearDirectRetrySlot(i); } else { + onDirectRetryEvent("canceled_echo", _direct_retries[i].trigger_packet, 0); + onDirectRetryEvent("good", _direct_retries[i].trigger_packet, 0); clearDirectRetrySlot(i); } cleared = true; @@ -503,7 +515,35 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { if (_direct_retries[i].queued) { if (_direct_retries[i].packet == packet) { // The retry packet itself just finished transmitting; Dispatcher will release it after this hook. - clearDirectRetrySlot(i); + onDirectRetryEvent("resent", packet, 0); + _direct_retries[i].retry_attempts_sent++; + if (_direct_retries[i].retry_attempts_sent >= DIRECT_RETRY_MAX_ATTEMPTS) { + onDirectRetryEvent("failure", packet, 0); + clearDirectRetrySlot(i); + continue; + } + + Packet* retry = obtainNewPacket(); + if (retry == NULL) { + onDirectRetryEvent("dropped_no_packet", packet, 0); + onDirectRetryEvent("failure", packet, 0); + clearDirectRetrySlot(i); + continue; + } + + *retry = *packet; + uint32_t retry_delay = DIRECT_RETRY_BACKOFF_MS[_direct_retries[i].retry_attempts_sent]; + sendPacket(retry, _direct_retries[i].priority, retry_delay); + if (isDirectRetryQueued(retry)) { + _direct_retries[i].packet = retry; + _direct_retries[i].retry_delay = retry_delay; + _direct_retries[i].retry_at = futureMillis(retry_delay); + onDirectRetryEvent("queued", retry, retry_delay); + } else { + onDirectRetryEvent("dropped_queue_full", retry, retry_delay); + onDirectRetryEvent("failure", retry, 0); + clearDirectRetrySlot(i); + } } continue; } @@ -515,6 +555,8 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { // Allocate the retry packet only after TX-complete so busy repeaters do not reserve pool slots early. Packet* retry = obtainNewPacket(); if (retry == NULL) { + onDirectRetryEvent("dropped_no_packet", packet, _direct_retries[i].retry_delay); + onDirectRetryEvent("failure", packet, 0); clearDirectRetrySlot(i); continue; } @@ -528,7 +570,10 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { _direct_retries[i].trigger_packet = NULL; _direct_retries[i].queued = true; _direct_retries[i].retry_at = futureMillis(_direct_retries[i].retry_delay); + onDirectRetryEvent("queued", retry, _direct_retries[i].retry_delay); } else { + onDirectRetryEvent("dropped_queue_full", retry, _direct_retries[i].retry_delay); + onDirectRetryEvent("failure", retry, 0); clearDirectRetrySlot(i); } } @@ -543,12 +588,16 @@ void Mesh::clearPendingDirectRetryOnSendFail(const Packet* packet) { if (_direct_retries[i].queued) { if (_direct_retries[i].packet == packet) { // The queued retry itself failed; Dispatcher will release it after this hook. + onDirectRetryEvent("dropped_send_fail", packet, 0); + onDirectRetryEvent("failure", packet, 0); clearDirectRetrySlot(i); } continue; } if (_direct_retries[i].trigger_packet == packet) { + onDirectRetryEvent("dropped_send_fail", packet, 0); + onDirectRetryEvent("failure", packet, 0); clearDirectRetrySlot(i); } } @@ -631,17 +680,19 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { } // Only store retry metadata here; allocate the retry packet after the initial TX really completes. - uint32_t retry_delay = getDirectRetryEchoDelay(packet); + uint32_t retry_delay = DIRECT_RETRY_BACKOFF_MS[0]; calculateDirectRetryKey(packet, _direct_retries[slot_idx].retry_key); _direct_retries[slot_idx].packet = NULL; _direct_retries[slot_idx].trigger_packet = const_cast(packet); _direct_retries[slot_idx].retry_at = 0; _direct_retries[slot_idx].retry_delay = retry_delay; + _direct_retries[slot_idx].retry_attempts_sent = 0; _direct_retries[slot_idx].priority = priority; _direct_retries[slot_idx].progress_marker = progress_marker; _direct_retries[slot_idx].expect_path_growth = expect_path_growth; _direct_retries[slot_idx].queued = false; _direct_retries[slot_idx].active = true; + onDirectRetryEvent("armed", packet, retry_delay); } Packet* Mesh::createAdvert(const LocalIdentity& id, const uint8_t* app_data, size_t app_data_len) { @@ -983,4 +1034,3 @@ void Mesh::sendZeroHop(Packet* packet, uint16_t* transport_codes, uint32_t delay } } - diff --git a/src/Mesh.h b/src/Mesh.h index 94bfec9f..4441514b 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -34,6 +34,7 @@ class Mesh : public Dispatcher { Packet* trigger_packet; unsigned long retry_at; uint32_t retry_delay; + uint8_t retry_attempts_sent; uint8_t retry_key[MAX_HASH_SIZE]; uint8_t priority; uint8_t progress_marker; @@ -111,6 +112,11 @@ protected: */ virtual uint8_t getExtraAckTransmitCount() const; + /** + * \brief Optional hook for logging direct-retry lifecycle events. + */ + virtual void onDirectRetryEvent(const char* event, const Packet* packet, uint32_t delay_millis) { } + /** * \brief Perform search of local DB of peers/contacts. * \returns Number of peers with matching hash diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index 217fd5a0..f5da272b 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -13,6 +13,8 @@ class SimpleMeshTables : public mesh::MeshTables { public: + typedef bool (*RecentRepeaterAllowFn)(const uint8_t* prefix, uint8_t prefix_len, void* ctx); + struct RecentRepeaterInfo { // Just enough identity to match a next-hop path prefix plus the SNR that heard it. uint8_t prefix[MAX_ROUTE_HASH_BYTES]; @@ -28,6 +30,9 @@ private: uint32_t _direct_dups, _flood_dups; RecentRepeaterInfo _recent_repeaters[MAX_RECENT_REPEATERS]; int _next_recent_repeater_idx; + int8_t _recent_repeater_min_snr_x4; + RecentRepeaterAllowFn _recent_repeater_allow_fn; + void* _recent_repeater_allow_ctx; bool hasSeenAck(uint32_t ack) const { for (int i = 0; i < MAX_PACKET_ACKS; i++) { @@ -58,6 +63,11 @@ private: _next_idx = (_next_idx + 1) % MAX_PACKET_HASHES; } + bool prefixesOverlap(const uint8_t* a, uint8_t a_len, const uint8_t* b, uint8_t b_len) const { + uint8_t n = a_len < b_len ? a_len : b_len; + return n > 0 && memcmp(a, b, n) == 0; + } + bool extractRecentRepeater(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const { // Learn repeater prefixes only from packet shapes that expose a trustworthy repeater ID. if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->payload_len >= PUB_KEY_SIZE) { @@ -96,14 +106,10 @@ private: if (!extractRecentRepeater(packet, prefix, prefix_len) || prefix_len == 0) { return; } - - // Ring buffer is enough here; retry fallback only needs a recent prefix->SNR observation. - RecentRepeaterInfo& slot = _recent_repeaters[_next_recent_repeater_idx]; - memset(slot.prefix, 0, sizeof(slot.prefix)); - memcpy(slot.prefix, prefix, prefix_len); - slot.prefix_len = prefix_len; - slot.snr_x4 = packet->_snr; - _next_recent_repeater_idx = (_next_recent_repeater_idx + 1) % MAX_RECENT_REPEATERS; + if (packet->_snr < _recent_repeater_min_snr_x4) { + return; + } + setRecentRepeater(prefix, prefix_len, packet->_snr); } public: @@ -115,6 +121,9 @@ public: _direct_dups = _flood_dups = 0; memset(_recent_repeaters, 0, sizeof(_recent_repeaters)); _next_recent_repeater_idx = 0; + _recent_repeater_min_snr_x4 = -128; + _recent_repeater_allow_fn = NULL; + _recent_repeater_allow_ctx = NULL; } #ifdef ESP32 @@ -216,19 +225,74 @@ public: uint32_t getNumDirectDups() const { return _direct_dups; } uint32_t getNumFloodDups() const { return _flood_dups; } + void setRecentRepeaterMinSNRX4(int8_t min_snr_x4) { + _recent_repeater_min_snr_x4 = min_snr_x4; + } + void setRecentRepeaterAllowFilter(RecentRepeaterAllowFn fn, void* ctx) { + _recent_repeater_allow_fn = fn; + _recent_repeater_allow_ctx = ctx; + } + bool setRecentRepeater(const uint8_t* prefix, uint8_t prefix_len, int8_t snr_x4) { + if (prefix == NULL || prefix_len == 0) { + return false; + } + + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + + if (_recent_repeater_allow_fn != NULL && !_recent_repeater_allow_fn(prefix, prefix_len, _recent_repeater_allow_ctx)) { + return false; + } + + // Keep one slot for overlapping prefixes so 1/2/3-byte paths share the same entry. + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + RecentRepeaterInfo& existing = _recent_repeaters[i]; + if (existing.prefix_len == 0 || !prefixesOverlap(existing.prefix, existing.prefix_len, prefix, prefix_len)) { + continue; + } + if (prefix_len > existing.prefix_len) { + memset(existing.prefix, 0, sizeof(existing.prefix)); + memcpy(existing.prefix, prefix, prefix_len); + existing.prefix_len = prefix_len; + } + existing.snr_x4 = snr_x4; + return true; + } + + // Ring buffer is enough here; retry fallback only needs a recent prefix->SNR observation. + RecentRepeaterInfo& slot = _recent_repeaters[_next_recent_repeater_idx]; + memset(slot.prefix, 0, sizeof(slot.prefix)); + memcpy(slot.prefix, prefix, prefix_len); + slot.prefix_len = prefix_len; + slot.snr_x4 = snr_x4; + _next_recent_repeater_idx = (_next_recent_repeater_idx + 1) % MAX_RECENT_REPEATERS; + return true; + } + const RecentRepeaterInfo* getLatestRecentRepeater() const { + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + int idx = (_next_recent_repeater_idx - 1 - i + MAX_RECENT_REPEATERS) % MAX_RECENT_REPEATERS; + const RecentRepeaterInfo* info = &_recent_repeaters[idx]; + if (info->prefix_len > 0) { + return info; + } + } + return NULL; + } + const RecentRepeaterInfo* findRecentRepeaterByHash(const uint8_t* hash, uint8_t hash_len) const { if (hash == NULL || hash_len == 0) { return NULL; } - // Search newest-to-oldest so the retry gate prefers the freshest SNR sample for a prefix. + // Search newest-to-oldest and allow 1/2/3-byte prefixes to overlap-match. for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { int idx = (_next_recent_repeater_idx - 1 - i + MAX_RECENT_REPEATERS) % MAX_RECENT_REPEATERS; const RecentRepeaterInfo* info = &_recent_repeaters[idx]; - if (info->prefix_len < hash_len || info->prefix_len == 0) { + if (info->prefix_len == 0) { continue; } - if (memcmp(info->prefix, hash, hash_len) == 0) { + if (prefixesOverlap(info->prefix, info->prefix_len, hash, hash_len)) { return info; } } From 9c2ac5aa1c7b8448f9189813120879d2b00a63fc Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 24 Apr 2026 00:08:10 -0700 Subject: [PATCH 012/214] Max retries is now a var that can be set between 1 to 15 --- docs/cli_commands.md | 39 +++++++- examples/simple_repeater/MyMesh.cpp | 136 ++++++++++++++++++++++++---- examples/simple_repeater/MyMesh.h | 1 + src/Mesh.cpp | 30 ++++-- src/Mesh.h | 10 ++ src/helpers/CommonCLI.cpp | 49 +++++++++- src/helpers/CommonCLI.h | 3 + src/helpers/SimpleMeshTables.h | 45 +++++++++ 8 files changed, 284 insertions(+), 29 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 774066b0..e798cf0e 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -117,14 +117,21 @@ This document provides an overview of CLI commands that can be sent to MeshCore ### Get or set recent repeater fallback prefix/SNR **Usage:** -- `recent.repeater` -- `recent.repeater ` +- `get recent.repeater` +- `get recent.repeater all` +- `get recent.repeater first ` +- `get recent.repeater last ` +- `set recent.repeater ` **Parameters:** - `prefix_hex`: 1-3 bytes of next-hop prefix (hex) - `snr_db`: SNR in dB (supports decimals; stored at x4 precision) +- `count`: number of entries to print -**Note:** `set` is rejected when the prefix already exists in neighbors. +**Notes:** +- `set` is rejected when the prefix already exists in neighbors. +- `all` prints oldest to newest; `first` prints the oldest N; `last` prints the newest N. +- Remote CLI replies include rows too, but may truncate when the packet payload limit is reached. --- @@ -545,6 +552,32 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### View or change the number of direct retry attempts +**Usage:** +- `get direct.retry.count` +- `set direct.retry.count ` + +**Parameters:** +- `value`: Retry attempts after initial TX (`1`-`15`) + +**Default:** `3` + +--- + +#### View or change the base direct retry wait (milliseconds) +**Usage:** +- `get direct.retry.base` +- `set direct.retry.base ` + +**Parameters:** +- `value`: Base wait in milliseconds (`10`-`5000`) + +**Default:** `200` + +**Note:** The actual first retry wait is `base + computed_echo_wait_from_live_phy`. + +--- + #### [Experimental] View or change the processing delay for received traffic **Usage:** - `get rxdelay` diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 71b532ed..220f9704 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -627,16 +627,21 @@ bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_ho return recent != NULL && recent->snr_x4 >= min_snr_x4; } uint32_t MyMesh::getDirectRetryEchoDelay(const mesh::Packet* packet) const { + uint32_t base_wait_millis = constrain((uint32_t)_prefs.direct_retry_base_ms, (uint32_t)10, (uint32_t)5000); // Approximate LoRa line rate in kilobits/sec from the live radio params the repeater is using now. float kbps = (((float) active_sf) * active_bw * ((float) active_cr)) / ((float) (1UL << active_sf)); if (kbps <= 0.0f) { - return HALO_DIRECT_RETRY_DELAY_MIN; + return base_wait_millis; } // Wait roughly long enough for our transmission, the next hop's receive/forward window, and its echo back. uint32_t bits = ((uint32_t) packet->getRawLength()) * 8; uint32_t scaled_wait_millis = (uint32_t) ((((float) bits) * 4.0f) / kbps); - return max((uint32_t) HALO_DIRECT_RETRY_DELAY_MIN, scaled_wait_millis); + return base_wait_millis + scaled_wait_millis; +} +uint8_t MyMesh::getDirectRetryMaxAttempts(const mesh::Packet* packet) const { + (void)packet; + return constrain(_prefs.direct_retry_attempts, (uint8_t)1, (uint8_t)15); } bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) { @@ -970,6 +975,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.direct_tx_delay_factor = 0.3f; // was 0.2 _prefs.direct_retry_recent_enabled = 0; _prefs.direct_retry_snr_margin_db = 5; + _prefs.direct_retry_attempts = 3; + _prefs.direct_retry_base_ms = 200; StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); _prefs.node_lat = ADVERT_LAT; _prefs.node_lon = ADVERT_LON; @@ -1345,29 +1352,36 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply Serial.printf("\n"); } reply[0] = 0; - } else if (memcmp(command, "recent.repeater", 15) == 0) { - const char* sub = command + 15; - while (*sub == ' ') sub++; - auto* tables = (SimpleMeshTables*)getTables(); - if (*sub == 0) { - const auto* info = tables->getLatestRecentRepeater(); - if (info == NULL) { - strcpy(reply, "> none"); - } else { - char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; - mesh::Utils::toHex(hex, info->prefix, info->prefix_len); - sprintf(reply, "> %s,%s", hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); - } + } else if (strncmp(command, "get recent.repeater", 19) == 0 + || strncmp(command, "set recent.repeater", 19) == 0 + || strncmp(command, "recent.repeater", 15) == 0) { + bool is_get = false; + bool is_set = false; + const char* sub = command; + + if (strncmp(command, "get recent.repeater", 19) == 0) { + is_get = true; + sub = command + 19; + } else if (strncmp(command, "set recent.repeater", 19) == 0) { + is_set = true; + sub = command + 19; } else { + sub = command + 15; // legacy command format + } + while (*sub == ' ') sub++; + + auto* tables = (SimpleMeshTables*)getTables(); + + if (is_set || (!is_get && *sub != 0 && strcmp(sub, "all") != 0 && strncmp(sub, "first ", 6) != 0 && strncmp(sub, "last ", 5) != 0)) { char* params = (char*) sub; char* arg_snr = strchr(params, ' '); if (arg_snr == NULL) { - strcpy(reply, "Err - usage: recent.repeater "); + strcpy(reply, "Err - usage: set recent.repeater "); } else { *arg_snr++ = 0; while (*arg_snr == ' ') arg_snr++; if (*arg_snr == 0) { - strcpy(reply, "Err - usage: recent.repeater "); + strcpy(reply, "Err - usage: set recent.repeater "); } else { int hex_len = strlen(params); int prefix_len = hex_len / 2; @@ -1386,6 +1400,94 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } } } + } else if (*sub == 0) { + const auto* info = tables->getLatestRecentRepeater(); + if (info == NULL) { + strcpy(reply, "> none"); + } else { + char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; + mesh::Utils::toHex(hex, info->prefix, info->prefix_len); + sprintf(reply, "> %s,%s", hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); + } + } else if (strcmp(sub, "all") == 0 || strncmp(sub, "first ", 6) == 0 || strncmp(sub, "last ", 5) == 0) { + int total = tables->getRecentRepeaterCount(); + if (total <= 0) { + strcpy(reply, "> none"); + } else { + bool newest_first = false; + int limit = total; + const char* mode = "all"; + if (strncmp(sub, "first ", 6) == 0 || strncmp(sub, "last ", 5) == 0) { + const char* nstr = sub + (sub[0] == 'f' ? 6 : 5); + while (*nstr == ' ') nstr++; + if (*nstr == 0) { + strcpy(reply, "Err - usage: get recent.repeater first|last "); + return; + } + char* end_ptr = NULL; + long parsed = strtol(nstr, &end_ptr, 10); + while (end_ptr != NULL && *end_ptr == ' ') end_ptr++; + if (end_ptr == NULL || *end_ptr != 0 || parsed <= 0) { + strcpy(reply, "Err - count must be > 0"); + return; + } + limit = (int)parsed; + if (sub[0] == 'l') { + newest_first = true; + mode = "last"; + } else { + mode = "first"; + } + } + if (limit > total) { + limit = total; + } + + if (sender_timestamp == 0) { + Serial.printf("Recent repeater table (%s %d/%d):\n", mode, limit, total); + for (int i = 0; i < limit; i++) { + const auto* info = newest_first ? tables->getRecentRepeaterNewestByIdx(i) : tables->getRecentRepeaterOldestByIdx(i); + if (info == NULL) { + continue; + } + char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; + mesh::Utils::toHex(hex, info->prefix, info->prefix_len); + Serial.printf("%02d: %s,%s\n", i + 1, hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); + } + sprintf(reply, "> showing %d/%d (%s)", limit, total, mode); + } else { + // Remote CLI replies are packet-bound, so include as many rows as fit. + int written = snprintf(reply, 160, "> showing %d/%d (%s)", limit, total, mode); + bool truncated = false; + if (written < 0) { + reply[0] = 0; + written = 0; + } + for (int i = 0; i < limit; i++) { + const auto* info = newest_first ? tables->getRecentRepeaterNewestByIdx(i) : tables->getRecentRepeaterOldestByIdx(i); + if (info == NULL) { + continue; + } + if (written >= 154) { + truncated = true; + break; + } + char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; + mesh::Utils::toHex(hex, info->prefix, info->prefix_len); + int n = snprintf(reply + written, 160 - written, "\n%02d:%s,%s", i + 1, hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); + if (n < 0 || n >= (160 - written)) { + truncated = true; + break; + } + written += n; + } + if (truncated && written < 156) { + snprintf(reply + written, 160 - written, "\n..."); + } + } + } + } else { + strcpy(reply, "Err - usage: get recent.repeater [all|first |last ]"); } } else if (memcmp(command, "discover.neighbors", 18) == 0) { const char* sub = command + 18; diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 16566dca..00a8a31b 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -154,6 +154,7 @@ protected: uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override; bool allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const override; uint32_t getDirectRetryEchoDelay(const mesh::Packet* packet) const override; + uint8_t getDirectRetryMaxAttempts(const mesh::Packet* packet) const override; void onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis) override; int getInterferenceThreshold() const override { diff --git a/src/Mesh.cpp b/src/Mesh.cpp index b9892eed..47fc6e8d 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -3,8 +3,8 @@ namespace mesh { -static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS = 3; -static const uint32_t DIRECT_RETRY_BACKOFF_MS[DIRECT_RETRY_MAX_ATTEMPTS] = { 200, 300, 400 }; +static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT = 3; +static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX = 15; void Mesh::begin() { for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { @@ -57,6 +57,14 @@ uint32_t Mesh::getDirectRetryEchoDelay(const Packet* packet) const { // Keep the base fallback aligned with the repeater's minimum retry wait. return 200; } +uint8_t Mesh::getDirectRetryMaxAttempts(const Packet* packet) const { + return DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT; +} +uint32_t Mesh::getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx) const { + uint32_t base = getDirectRetryEchoDelay(packet); + // Keep the historical linear spacing while allowing the base wait to vary by platform/profile. + return base + ((uint32_t)attempt_idx * 100UL); +} uint8_t Mesh::getExtraAckTransmitCount() const { return 0; } @@ -517,7 +525,13 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { // The retry packet itself just finished transmitting; Dispatcher will release it after this hook. onDirectRetryEvent("resent", packet, 0); _direct_retries[i].retry_attempts_sent++; - if (_direct_retries[i].retry_attempts_sent >= DIRECT_RETRY_MAX_ATTEMPTS) { + uint8_t max_attempts = getDirectRetryMaxAttempts(packet); + if (max_attempts < 1) { + max_attempts = 1; + } else if (max_attempts > DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX) { + max_attempts = DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX; + } + if (_direct_retries[i].retry_attempts_sent >= max_attempts) { onDirectRetryEvent("failure", packet, 0); clearDirectRetrySlot(i); continue; @@ -532,7 +546,7 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { } *retry = *packet; - uint32_t retry_delay = DIRECT_RETRY_BACKOFF_MS[_direct_retries[i].retry_attempts_sent]; + uint32_t retry_delay = getDirectRetryAttemptDelay(packet, _direct_retries[i].retry_attempts_sent); sendPacket(retry, _direct_retries[i].priority, retry_delay); if (isDirectRetryQueued(retry)) { _direct_retries[i].packet = retry; @@ -612,7 +626,9 @@ bool Mesh::getDirectRetryTarget(const Packet* packet, const uint8_t*& next_hop_h case PAYLOAD_TYPE_RESPONSE: case PAYLOAD_TYPE_TXT_MSG: case PAYLOAD_TYPE_ANON_REQ: - if (packet->getPathHashCount() <= 1) { + // Allow retries even when only one downstream hop remains so fixed direct paths + // (e.g. remote admin/login over 2-hop chains) use the same retry policy. + if (packet->getPathHashCount() == 0) { return false; } next_hop_hash = packet->path; @@ -622,7 +638,7 @@ bool Mesh::getDirectRetryTarget(const Packet* packet, const uint8_t*& next_hop_h return true; case PAYLOAD_TYPE_MULTIPART: - if (packet->payload_len < 1 || (packet->payload[0] & 0x0F) != PAYLOAD_TYPE_ACK || packet->getPathHashCount() <= 1) { + if (packet->payload_len < 1 || (packet->payload[0] & 0x0F) != PAYLOAD_TYPE_ACK || packet->getPathHashCount() == 0) { return false; } next_hop_hash = packet->path; @@ -680,7 +696,7 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { } // Only store retry metadata here; allocate the retry packet after the initial TX really completes. - uint32_t retry_delay = DIRECT_RETRY_BACKOFF_MS[0]; + uint32_t retry_delay = getDirectRetryAttemptDelay(packet, 0); calculateDirectRetryKey(packet, _direct_retries[slot_idx].retry_key); _direct_retries[slot_idx].packet = NULL; _direct_retries[slot_idx].trigger_packet = const_cast(packet); diff --git a/src/Mesh.h b/src/Mesh.h index 4441514b..ad4d8a2f 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -107,6 +107,16 @@ protected: */ virtual uint32_t getDirectRetryEchoDelay(const Packet* packet) const; + /** + * \returns maximum number of retry transmissions after the initial direct TX. + */ + virtual uint8_t getDirectRetryMaxAttempts(const Packet* packet) const; + + /** + * \returns delay before a specific retry attempt, where attempt_idx=0 is the first retry. + */ + virtual uint32_t getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx) const; + /** * \returns number of extra (Direct) ACK transmissions wanted. */ diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 38d4536a..02d27830 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -14,6 +14,14 @@ #define DIRECT_RETRY_RECENT_DEFAULT 0 #define DIRECT_RETRY_SNR_MARGIN_DB_DEFAULT 5 #define DIRECT_RETRY_SNR_MARGIN_DB_MAX 40 +#define DIRECT_RETRY_TIMING_MAGIC_0 0xD5 +#define DIRECT_RETRY_TIMING_MAGIC_1 0x54 +#define DIRECT_RETRY_COUNT_DEFAULT 3 +#define DIRECT_RETRY_COUNT_MIN 1 +#define DIRECT_RETRY_COUNT_MAX 15 +#define DIRECT_RETRY_BASE_MS_DEFAULT 200 +#define DIRECT_RETRY_BASE_MS_MIN 10 +#define DIRECT_RETRY_BASE_MS_MAX 5000 // Believe it or not, this std C function is busted on some platforms! static uint32_t _atoi(const char* sp) { @@ -97,7 +105,10 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290 - // next: 291 + file.read((uint8_t *)&_prefs->direct_retry_attempts, sizeof(_prefs->direct_retry_attempts)); // 291 + file.read((uint8_t *)&_prefs->direct_retry_base_ms, sizeof(_prefs->direct_retry_base_ms)); // 292 + file.read((uint8_t *)&_prefs->direct_retry_timing_magic[0], sizeof(_prefs->direct_retry_timing_magic)); // 294 + // next: 296 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -121,6 +132,14 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->direct_retry_recent_enabled = constrain(_prefs->direct_retry_recent_enabled, 0, 1); _prefs->direct_retry_snr_margin_db = constrain(_prefs->direct_retry_snr_margin_db, 0, DIRECT_RETRY_SNR_MARGIN_DB_MAX); } + if (_prefs->direct_retry_timing_magic[0] != DIRECT_RETRY_TIMING_MAGIC_0 + || _prefs->direct_retry_timing_magic[1] != DIRECT_RETRY_TIMING_MAGIC_1) { + _prefs->direct_retry_attempts = DIRECT_RETRY_COUNT_DEFAULT; + _prefs->direct_retry_base_ms = DIRECT_RETRY_BASE_MS_DEFAULT; + } else { + _prefs->direct_retry_attempts = constrain(_prefs->direct_retry_attempts, DIRECT_RETRY_COUNT_MIN, DIRECT_RETRY_COUNT_MAX); + _prefs->direct_retry_base_ms = constrain(_prefs->direct_retry_base_ms, DIRECT_RETRY_BASE_MS_MIN, DIRECT_RETRY_BASE_MS_MAX); + } // sanitise bad bridge pref values _prefs->bridge_enabled = constrain(_prefs->bridge_enabled, 0, 1); @@ -201,7 +220,11 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290 - // next: 291 + file.write((uint8_t *)&_prefs->direct_retry_attempts, sizeof(_prefs->direct_retry_attempts)); // 291 + file.write((uint8_t *)&_prefs->direct_retry_base_ms, sizeof(_prefs->direct_retry_base_ms)); // 292 + uint8_t retry_timing_magic[2] = { DIRECT_RETRY_TIMING_MAGIC_0, DIRECT_RETRY_TIMING_MAGIC_1 }; + file.write(retry_timing_magic, sizeof(retry_timing_magic)); // 294 + // next: 296 file.close(); } @@ -363,6 +386,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re sprintf(reply, "> %s", _prefs->direct_retry_recent_enabled ? "on" : "off"); } else if (memcmp(config, "direct.retry.margin", 19) == 0) { sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_snr_margin_db); + } else if (memcmp(config, "direct.retry.count", 18) == 0) { + sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_attempts); + } else if (memcmp(config, "direct.retry.base", 17) == 0) { + sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_base_ms); } else if (memcmp(config, "owner.info", 10) == 0) { *reply++ = '>'; *reply++ = ' '; @@ -633,6 +660,24 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else { sprintf(reply, "Error, min 0 and max %d", DIRECT_RETRY_SNR_MARGIN_DB_MAX); } + } else if (memcmp(config, "direct.retry.count ", 19) == 0) { + int count = atoi(&config[19]); + if (count >= DIRECT_RETRY_COUNT_MIN && count <= DIRECT_RETRY_COUNT_MAX) { + _prefs->direct_retry_attempts = (uint8_t)count; + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error, min %d and max %d", DIRECT_RETRY_COUNT_MIN, DIRECT_RETRY_COUNT_MAX); + } + } else if (memcmp(config, "direct.retry.base ", 18) == 0) { + int delay_ms = atoi(&config[18]); + if (delay_ms >= DIRECT_RETRY_BASE_MS_MIN && delay_ms <= DIRECT_RETRY_BASE_MS_MAX) { + _prefs->direct_retry_base_ms = (uint16_t)delay_ms; + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error, min %d and max %d", DIRECT_RETRY_BASE_MS_MIN, DIRECT_RETRY_BASE_MS_MAX); + } } else if (memcmp(config, "owner.info ", 11) == 0) { config += 11; char *dp = _prefs->owner_info; diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 85962638..03b1fb64 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -63,6 +63,9 @@ struct NodePrefs { // persisted to file uint8_t rx_boosted_gain; // power settings uint8_t path_hash_mode; // which path mode to use when sending uint8_t loop_detect; + uint8_t direct_retry_attempts; + uint16_t direct_retry_base_ms; + uint8_t direct_retry_timing_magic[2]; }; class CommonCLICallbacks { diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index f5da272b..705869ad 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -279,6 +279,51 @@ public: } return NULL; } + int getRecentRepeaterCount() const { + int count = 0; + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + if (_recent_repeaters[i].prefix_len > 0) { + count++; + } + } + return count; + } + const RecentRepeaterInfo* getRecentRepeaterNewestByIdx(int idx_wanted) const { + if (idx_wanted < 0) { + return NULL; + } + int idx_seen = 0; + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + int idx = (_next_recent_repeater_idx - 1 - i + MAX_RECENT_REPEATERS) % MAX_RECENT_REPEATERS; + const RecentRepeaterInfo* info = &_recent_repeaters[idx]; + if (info->prefix_len == 0) { + continue; + } + if (idx_seen == idx_wanted) { + return info; + } + idx_seen++; + } + return NULL; + } + const RecentRepeaterInfo* getRecentRepeaterOldestByIdx(int idx_wanted) const { + if (idx_wanted < 0) { + return NULL; + } + int idx_seen = 0; + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + int idx = (_next_recent_repeater_idx + i) % MAX_RECENT_REPEATERS; + const RecentRepeaterInfo* info = &_recent_repeaters[idx]; + if (info->prefix_len == 0) { + continue; + } + if (idx_seen == idx_wanted) { + return info; + } + idx_seen++; + } + return NULL; + } const RecentRepeaterInfo* findRecentRepeaterByHash(const uint8_t* hash, uint8_t hash_len) const { if (hash == NULL || hash_len == 0) { From c5d8ada27d4e9a2146442fe5ce4326047d011832 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Fri, 24 Apr 2026 23:02:11 +0700 Subject: [PATCH 013/214] Added features from PowerSaving 14.1.1 --- build-iotthinks.sh | 96 +++ build.sh | 7 +- examples/companion_radio/MyMesh.cpp | 39 +- examples/companion_radio/MyMesh.h | 5 +- examples/companion_radio/NodePrefs.h | 1 + examples/companion_radio/main.cpp | 44 +- examples/simple_repeater/MyMesh.cpp | 2 + examples/simple_repeater/main.cpp | 20 +- examples/simple_room_server/MyMesh.cpp | 10 + examples/simple_room_server/MyMesh.h | 3 + examples/simple_room_server/main.cpp | 13 + src/MeshCore.h | 4 + src/helpers/CommonCLI.cpp | 557 ++++++++++-------- src/helpers/CommonCLI.h | 1 + src/helpers/ESP32Board.h | 146 ++++- src/helpers/SensorManager.h | 2 + src/helpers/esp32/TBeamBoard.h | 14 +- .../sensors/EnvironmentSensorManager.cpp | 133 +++-- .../sensors/EnvironmentSensorManager.h | 1 + variants/gat562_30s_mesh_kit/platformio.ini | 2 +- .../gat562_mesh_tracker_pro/platformio.ini | 2 +- variants/heltec_t114/platformio.ini | 2 +- .../HeltecTrackerV2Board.cpp | 18 + .../heltec_tracker_v2/HeltecTrackerV2Board.h | 3 + variants/heltec_tracker_v2/LoRaFEMControl.h | 3 +- variants/heltec_v2/HeltecV2Board.h | 16 +- variants/heltec_v2/platformio.ini | 31 +- variants/heltec_v2/target.cpp | 4 +- variants/heltec_v3/platformio.ini | 26 + variants/heltec_v4/HeltecV4Board.cpp | 24 +- variants/heltec_v4/HeltecV4Board.h | 3 + variants/heltec_v4/LoRaFEMControl.h | 3 +- variants/heltec_v4/platformio.ini | 76 ++- .../LilygoT3S3SX1276Board.h | 10 + variants/lilygo_t3s3_sx1276/target.cpp | 2 +- variants/lilygo_t3s3_sx1276/target.h | 4 +- variants/lilygo_tbeam_SX1262/platformio.ini | 7 + .../LilygoTBeamSX1276Board.h | 10 + variants/lilygo_tbeam_SX1276/platformio.ini | 7 + variants/lilygo_tbeam_SX1276/target.cpp | 2 +- variants/lilygo_tbeam_SX1276/target.h | 4 +- .../platformio.ini | 7 + variants/lilygo_tlora_v2_1/LilyGoTLoraBoard.h | 4 + variants/rak3401/platformio.ini | 2 +- variants/rak4631/platformio.ini | 2 +- variants/sensecap_solar/platformio.ini | 2 +- variants/xiao_c3/platformio.ini | 22 + variants/xiao_nrf52/platformio.ini | 2 +- variants/xiao_s3/XiaoS3Board.h | 13 + variants/xiao_s3/platformio.ini | 180 ++++++ variants/xiao_s3/target.cpp | 56 ++ variants/xiao_s3/target.h | 30 + variants/xiao_s3_wio/platformio.ini | 26 + 53 files changed, 1321 insertions(+), 382 deletions(-) create mode 100644 build-iotthinks.sh create mode 100644 variants/lilygo_t3s3_sx1276/LilygoT3S3SX1276Board.h create mode 100644 variants/lilygo_tbeam_SX1276/LilygoTBeamSX1276Board.h create mode 100644 variants/xiao_s3/XiaoS3Board.h create mode 100644 variants/xiao_s3/platformio.ini create mode 100644 variants/xiao_s3/target.cpp create mode 100644 variants/xiao_s3/target.h diff --git a/build-iotthinks.sh b/build-iotthinks.sh new file mode 100644 index 00000000..7c654482 --- /dev/null +++ b/build-iotthinks.sh @@ -0,0 +1,96 @@ +# sh ./build-repeaters-iotthinks.sh +export FIRMWARE_VERSION="PowerSaving15" + +############# Repeaters ############# +# Commonly-used boards +## ESP32 - 12 boards +sh build.sh build-firmware \ +Heltec_v3_repeater \ +Heltec_WSL3_repeater \ +heltec_v4_repeater \ +Station_G2_repeater \ +T_Beam_S3_Supreme_SX1262_repeater \ +Tbeam_SX1262_repeater \ +LilyGo_T3S3_sx1262_repeater \ +Xiao_S3_WIO_repeater \ +Xiao_C3_repeater \ +Xiao_C6_repeater_ \ +Heltec_E290_repeater \ +Heltec_Wireless_Tracker_repeater + +## NRF52 - 13 boards +sh build.sh build-firmware \ +RAK_4631_repeater \ +Heltec_t114_repeater \ +Xiao_nrf52_repeater \ +Heltec_mesh_solar_repeater \ +ProMicro_repeater \ +SenseCap_Solar_repeater \ +t1000e_repeater \ +LilyGo_T-Echo_repeater \ +WioTrackerL1_repeater \ +RAK_3401_repeater \ +RAK_WisMesh_Tag_repeater \ +GAT562_30S_Mesh_Kit_repeater \ +GAT562_Mesh_Tracker_Pro_repeater + +## ESP32, SX1276 - 3 boards +sh build.sh build-firmware \ +Heltec_v2_repeater \ +LilyGo_TLora_V2_1_1_6_repeater \ +Tbeam_SX1276_repeater + +## Ikoka - 3 boards +sh build.sh build-firmware \ +ikoka_nano_nrf_22dbm_repeater \ +ikoka_nano_nrf_30dbm_repeater \ +ikoka_nano_nrf_33dbm_repeater + +############# Room Server ############# +# ESP32 +sh build.sh build-firmware \ +Heltec_v3_room_server \ +heltec_v4_room_server + +# NRF52 +sh build.sh build-firmware \ +RAK_4631_room_server \ +Heltec_t114_room_server \ +Xiao_nrf52_room_server \ +t1000e_room_server \ +WioTrackerL1_room_server \ +RAK_3401_room_server + +############# Companions BLE ############# +# ESP32 +sh build.sh build-firmware \ +Heltec_v3_companion_radio_ble_ps \ +heltec_v4_companion_radio_ble_ps \ +heltec_v4_companion_radio_ble_ps_femoff \ +Xiao_S3_WIO_companion_radio_ble \ +Heltec_Wireless_Paper_companion_radio_ble + +# NRF52 +sh build.sh build-firmware \ +RAK_4631_companion_radio_ble \ +Heltec_t114_companion_radio_ble \ +Xiao_nrf52_companion_radio_ble \ +t1000e_companion_radio_ble \ +LilyGo_T-Echo_companion_radio_ble \ +WioTrackerL1_companion_radio_ble \ +RAK_3401_companion_radio_ble \ +RAK_WisMesh_Tag_companion_radio_ble + +############# Companions USB ############# +sh build.sh build-firmware \ +Heltec_v3_companion_radio_usb + +############# Companions BLE PS ############# +sh build.sh build-firmware \ +Heltec_v3_companion_radio_ble_ps \ +heltec_v4_companion_radio_ble_ps \ +heltec_v4_3_companion_radio_ble_ps_femoff \ +Xiao_C3_companion_radio_ble_ps \ +Xiao_S3_companion_radio_ble_ps \ +Xiao_S3_WIO_companion_radio_ble_ps \ +Heltec_v2_companion_radio_ble_ps diff --git a/build.sh b/build.sh index 313c4c47..006eae96 100755 --- a/build.sh +++ b/build.sh @@ -134,7 +134,8 @@ build_firmware() { # set firmware version string # e.g: v1.0.0-abcdef - FIRMWARE_VERSION_STRING="${FIRMWARE_VERSION}-${COMMIT_HASH}" + # FIRMWARE_VERSION_STRING="${FIRMWARE_VERSION}-${COMMIT_HASH}" + FIRMWARE_VERSION_STRING="${FIRMWARE_VERSION}" # craft filename # e.g: RAK_4631_Repeater-v1.0.0-SHA @@ -152,8 +153,8 @@ build_firmware() { # build merge-bin for esp32 fresh install, copy .bins to out folder (e.g: Heltec_v3_room_server-v1.0.0-SHA.bin) if [ "$ENV_PLATFORM" == "ESP32_PLATFORM" ]; then pio run -t mergebin -e $1 - cp .pio/build/$1/firmware.bin out/${FIRMWARE_FILENAME}.bin 2>/dev/null || true - cp .pio/build/$1/firmware-merged.bin out/${FIRMWARE_FILENAME}-merged.bin 2>/dev/null || true + cp .pio/build/$1/firmware.bin out/${FIRMWARE_FILENAME}-upgrade.bin 2>/dev/null || true + cp .pio/build/$1/firmware-merged.bin out/${FIRMWARE_FILENAME}-freshInstall-merged.bin 2>/dev/null || true fi # build .uf2 for nrf52 boards, copy .uf2 and .zip to out folder (e.g: RAK_4631_Repeater-v1.0.0-SHA.uf2) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index e8c1914b..67ff0a81 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -46,7 +46,9 @@ #define CMD_SET_CUSTOM_VAR 41 #define CMD_GET_ADVERT_PATH 42 #define CMD_GET_TUNING_PARAMS 43 -// NOTE: CMD range 44..49 parked, potentially for WiFi operations +#define CMD_GET_RADIO_FEM_RXGAIN 44 +#define CMD_SET_RADIO_FEM_RXGAIN 45 +// NOTE: CMD range 46..49 parked, potentially for WiFi operations #define CMD_SEND_BINARY_REQ 50 #define CMD_FACTORY_RESET 51 #define CMD_SEND_PATH_DISCOVERY_REQ 52 @@ -876,6 +878,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe _prefs.rx_boosted_gain = 1; // enabled by default #endif #endif + _prefs.radio_fem_rxgain = 1; } void MyMesh::begin(bool has_display) { @@ -925,6 +928,7 @@ void MyMesh::begin(bool has_display) { _prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, -9, MAX_LORA_TX_POWER); _prefs.gps_enabled = constrain(_prefs.gps_enabled, 0, 1); // Ensure boolean 0 or 1 _prefs.gps_interval = constrain(_prefs.gps_interval, 0, 86400); // Max 24 hours + _prefs.radio_fem_rxgain = constrain(_prefs.radio_fem_rxgain, 0, 1); #ifdef BLE_PIN_CODE // 123456 by default if (_prefs.ble_pin == 0) { @@ -954,6 +958,7 @@ void MyMesh::begin(bool has_display) { radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); radio_set_tx_power(_prefs.tx_power_dbm); radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); + board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s", radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled"); } @@ -1798,6 +1803,30 @@ void MyMesh::handleCmdFrame(size_t len) { } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } + } else if (cmd_frame[0] == CMD_GET_RADIO_FEM_RXGAIN) { + if (!board.canControlLoRaFemLna()) { + writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); + } else { + out_frame[0] = RESP_CODE_OK; + uint32_t value = board.isLoRaFemLnaEnabled() ? 1 : 0; + memcpy(&out_frame[1], &value, 4); + _serial->writeFrame(out_frame, 5); + } + } else if (cmd_frame[0] == CMD_SET_RADIO_FEM_RXGAIN && len >= 2) { + uint8_t value = cmd_frame[1]; + if (!board.canControlLoRaFemLna()) { + writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); + } else if (value <= 1) { + _prefs.radio_fem_rxgain = value; + if (board.setLoRaFemLnaEnabled(value != 0)) { + savePrefs(); + writeOKFrame(); + } else { + writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); + } + } else { + writeErrFrame(ERR_CODE_ILLEGAL_ARG); + } } else if (cmd_frame[0] == CMD_GET_ADVERT_PATH && len >= PUB_KEY_SIZE+2) { // FUTURE use: uint8_t reserved = cmd_frame[1]; uint8_t *pub_key = &cmd_frame[2]; @@ -2191,3 +2220,11 @@ bool MyMesh::advert() { return false; } } + +// To check if there is pending work +bool MyMesh::hasPendingWork() const { +#if defined(WITH_BRIDGE) + if (bridge.isRunning()) return true; // bridge needs WiFi radio, can't sleep +#endif + return _mgr->getOutboundTotal() > 0; +} diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index aeff591c..ad0909bc 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -165,7 +165,7 @@ protected: public: void savePrefs() { _store->savePrefs(_prefs, sensors.node_lat, sensors.node_lon); } - + #if ENV_INCLUDE_GPS == 1 void applyGpsPrefs() { sensors.setSettingValue("gps", _prefs.gps_enabled ? "1" : "0"); @@ -177,6 +177,9 @@ public: } #endif + // To check if there is pending work + bool hasPendingWork() const; + private: void writeOKFrame(); void writeErrFrame(uint8_t err_code); diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 48c381ce..6598a69c 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -29,6 +29,7 @@ struct NodePrefs { // persisted to file uint32_t gps_interval; // GPS read interval in seconds uint8_t autoadd_config; // bitmask for auto-add contacts config uint8_t rx_boosted_gain; // SX126x RX boosted gain mode (0=power saving, 1=boosted) + uint8_t radio_fem_rxgain; // LoRa FEM RX gain setting uint8_t client_repeat; uint8_t path_hash_mode; // which path mode to use when sending uint8_t autoadd_max_hops; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 876dc9c3..90903097 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -2,6 +2,11 @@ #include #include "MyMesh.h" +#ifdef ESP32_PLATFORM +#include "esp_pm.h" +#include "esp_bt.h" +#endif + // Believe it or not, this std C function is busted on some platforms! static uint32_t _atoi(const char* sp) { uint32_t n = 0; @@ -42,8 +47,8 @@ static uint32_t _atoi(const char* sp) { #define TCP_PORT 5000 #endif #elif defined(BLE_PIN_CODE) - #include - SerialBLEInterface serial_interface; + #include + SerialBLEInterface serial_interface; #elif defined(SERIAL_RX) #include ArduinoSerialInterface serial_interface; @@ -220,6 +225,33 @@ void setup() { #ifdef DISPLAY_CLASS ui_task.begin(disp, &sensors, the_mesh.getNodePrefs()); // still want to pass this in as dependency, as prefs might be moved #endif + +#ifdef ESP32_PLATFORM + // Enable BLE sleep + esp_err_t errBLESleep = esp_bt_sleep_enable(); + if (errBLESleep == ESP_OK) { + Serial.println("Bluetooth sleep enabled successfully"); + } else { + Serial.printf("Bluetooth sleep enable failed: %s\n", esp_err_to_name(errBLESleep)); + } + +#if CONFIG_IDF_TARGET_ESP32C3 + esp_pm_config_esp32c3_t pm_config; +#elif CONFIG_IDF_TARGET_ESP32S3 + esp_pm_config_esp32s3_t pm_config; +#elif CONFIG_IDF_TARGET_ESP32 + esp_pm_config_esp32_t pm_config; +#endif + + // Configure Power Management + pm_config = { .max_freq_mhz = 80, .min_freq_mhz = 40, .light_sleep_enable = true }; + esp_err_t errPM = esp_pm_configure(&pm_config); + if (errPM == ESP_OK) { + Serial.println("Power Management configured successfully"); + } else { + Serial.printf("Power Management failed to configure: %d\r\n", errPM); + } +#endif } void loop() { @@ -229,4 +261,12 @@ void loop() { ui_task.loop(); #endif rtc_clock.tick(); + + if (!the_mesh.hasPendingWork()) { +#if defined(NRF52_PLATFORM) + board.sleep(0); // nrf ignores seconds param, sleeps whenever possible +#else if defined(ESP32_PLATFORM) + vTaskDelay(pdMS_TO_TICKS(50)); // attempt to sleep +#endif + } } diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 666f79fc..1e9cb91f 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -911,6 +911,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.rx_boosted_gain = 1; // enabled by default; #endif #endif + _prefs.radio_fem_rxgain = 1; pending_discover_tag = 0; pending_discover_until = 0; @@ -959,6 +960,7 @@ void MyMesh::begin(FILESYSTEM *fs) { radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s", radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled"); + board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); updateAdvertTimer(); updateFloodAdvertTimer(); diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index e37078ce..b3fbbe5b 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -20,8 +20,7 @@ void halt() { static char command[160]; // For power saving -unsigned long lastActive = 0; // mark last active time -unsigned long nextSleepinSecs = 120; // next sleep in seconds. The first sleep (if enabled) is after 2 minutes from boot +unsigned long POWERSAVING_FIRSTSLEEP_SECS = 120; // The first sleep (if enabled) from boot #if defined(PIN_USER_BTN) && defined(_SEEED_SENSECAP_SOLAR_H_) static unsigned long userBtnDownAt = 0; @@ -40,9 +39,6 @@ void setup() { delay(5000); #endif - // For power saving - lastActive = millis(); // mark last active time since boot - #ifdef DISPLAY_CLASS if (display.begin()) { display.startFrame(); @@ -155,16 +151,12 @@ void loop() { rtc_clock.tick(); if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) { - #if defined(NRF52_PLATFORM) +#if defined(NRF52_PLATFORM) board.sleep(1800); // nrf ignores seconds param, sleeps whenever possible - #else - if (the_mesh.millisHasNowPassed(lastActive + nextSleepinSecs * 1000)) { // To check if it is time to sleep - board.sleep(1800); // To sleep. Wake up after 30 minutes or when receiving a LoRa packet - lastActive = millis(); - nextSleepinSecs = 5; // Default: To work for 5s and sleep again - } else { - nextSleepinSecs += 5; // When there is pending work, to work another 5s +#else + if (the_mesh.millisHasNowPassed(POWERSAVING_FIRSTSLEEP_SECS * 1000)) { // To check if it is time to sleep + board.sleep(1800); // Sleep. Wake up after 30 minutes or when receiving a LoRa packet } - #endif +#endif } } diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 145fb0fd..bdda8194 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -652,6 +652,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.gps_enabled = 0; _prefs.gps_interval = 0; _prefs.advert_loc_policy = ADVERT_LOC_PREFS; + _prefs.radio_fem_rxgain = 1; next_post_idx = 0; next_client_idx = 0; @@ -693,6 +694,7 @@ void MyMesh::begin(FILESYSTEM *fs) { radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); radio_set_tx_power(_prefs.tx_power_dbm); + board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); updateAdvertTimer(); updateFloodAdvertTimer(); @@ -1022,3 +1024,11 @@ void MyMesh::loop() { uptime_millis += now - last_millis; last_millis = now; } + +// To check if there is pending work +bool MyMesh::hasPendingWork() const { +#if defined(WITH_BRIDGE) + if (bridge.isRunning()) return true; // bridge needs WiFi radio, can't sleep +#endif + return _mgr->getOutboundTotal() > 0; +} diff --git a/examples/simple_room_server/MyMesh.h b/examples/simple_room_server/MyMesh.h index 1b35ae95..410ffef0 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -222,4 +222,7 @@ public: void clearStats() override; void handleCommand(uint32_t sender_timestamp, char* command, char* reply); void loop(); + + // To check if there is pending work + bool hasPendingWork() const; }; diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 825fb007..55179809 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -18,6 +18,9 @@ void halt() { static char command[MAX_POST_TEXT_LEN+1]; +// For power saving +unsigned long POWERSAVING_FIRSTSLEEP_SECS = 120; // The first sleep (if enabled) from boot + void setup() { Serial.begin(115200); delay(1000); @@ -113,4 +116,14 @@ void loop() { ui_task.loop(); #endif rtc_clock.tick(); + + if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) { +#if defined(NRF52_PLATFORM) + board.sleep(1800); // nrf ignores seconds param, sleeps whenever possible +#else + if (the_mesh.millisHasNowPassed(POWERSAVING_FIRSTSLEEP_SECS * 1000)) { // To check if it is time to sleep + board.sleep(1800); // Sleep. Wake up after 30 minutes or when receiving a LoRa packet + } +#endif + } } diff --git a/src/MeshCore.h b/src/MeshCore.h index 2db1d4c3..a79bd8b0 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -52,12 +52,16 @@ public: virtual void onAfterTransmit() { } virtual void reboot() = 0; virtual void powerOff() { /* no op */ } + virtual uint32_t getIRQGpio() { return -1; } // not supported. Returns DIO1 (SX1262) and DIO0 (SX127x) virtual void sleep(uint32_t secs) { /* no op */ } virtual uint32_t getGpio() { return 0; } virtual void setGpio(uint32_t values) {} virtual uint8_t getStartupReason() const = 0; virtual bool getBootloaderVersion(char* version, size_t max_len) { return false; } virtual bool startOTAUpdate(const char* id, char reply[]) { return false; } // not supported + virtual bool setLoRaFemLnaEnabled(bool enable) { return false; } + virtual bool canControlLoRaFemLna() const { return false; } + virtual bool isLoRaFemLnaEnabled() const { return false; } // Power management interface (boards with power management override these) virtual bool isExternalPowered() { return false; } diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index d495aada..1aa4265c 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -3,6 +3,8 @@ #include "TxtDataHelpers.h" #include "AdvertDataHelpers.h" #include +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) #ifndef BRIDGE_MAX_BAUD #define BRIDGE_MAX_BAUD 115200 @@ -118,6 +120,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { // sanitise settings _prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean + _prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean file.close(); } @@ -180,6 +183,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290 // next: 291 + file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 291 + // next: 292 file.close(); } @@ -426,19 +431,59 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } #endif } else if (memcmp(command, "powersaving on", 14) == 0) { +#if defined(NRF52_PLATFORM) _prefs->powersaving_enabled = 1; savePrefs(); - strcpy(reply, "ok"); // TODO: to return Not supported if required + strcpy(reply, "On - Immediate effect"); +#elif defined(ESP32) && !defined(WITH_BRIDGE) + _prefs->powersaving_enabled = 1; + savePrefs(); + strcpy(reply, "On - After 2 minutes"); +#elif defined(WITH_BRIDGE) + strcpy(reply, "Bridge not supported"); +#else + strcpy(reply, "Board not supported"); +#endif } else if (memcmp(command, "powersaving off", 15) == 0) { _prefs->powersaving_enabled = 0; savePrefs(); - strcpy(reply, "ok"); + strcpy(reply, "Off"); } else if (memcmp(command, "powersaving", 11) == 0) { if (_prefs->powersaving_enabled) { - strcpy(reply, "on"); + strcpy(reply, "On"); } else { - strcpy(reply, "off"); + strcpy(reply, "Off"); } + } else if (memcmp(command, "sensor", 6) == 0) { + // I2C +#if defined(ENV_PIN_SDA) && defined(ENV_PIN_SCL) + sprintf(reply, "I2C Wire1: SDA=%s,SCL=%s\r\n", STR(ENV_PIN_SDA), STR(ENV_PIN_SCL)); +#elif defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL) + sprintf(reply, "I2C Wire: SDA=%s, SCL=%s\r\n", STR(PIN_BOARD_SDA), STR(PIN_BOARD_SCL)); +#elif defined(PIN_WIRE_SDA) && defined(PIN_WIRE_SCL) + sprintf(reply, "I2C Wire: SDA=%s, SCL=%s\r\n", STR(PIN_WIRE_SDA), STR(PIN_WIRE_SCL)); +#else + sprintf(reply, "I2C GPIOs not defined\r\n"); +#endif + + // GPS +#if defined(PIN_GPS_RX) && defined(PIN_GPS_TX) + sprintf(reply + strlen(reply), "GPS Serial: RX=%s, TX=%s", STR(PIN_GPS_RX), STR(PIN_GPS_TX)); +#ifdef ENV_INCLUDE_GPS> 0 + sprintf(reply + strlen(reply), ". Configured"); +#else + sprintf(reply + strlen(reply), ". Not configured"); +#endif +#else + sprintf(reply + strlen(reply), "GPS Serial not defined"); +#endif + } else if (memcmp(command, "powerlog", 8) == 0) { + sprintf(reply, "Last reset reason: %s", _board->getResetReasonString(_board->getResetReason())); +#if defined(NRF52_PLATFORM) + sprintf(reply + strlen(reply), "\r\nLast shutdown reason: %s", + _board->getShutdownReasonString(_board->getShutdownReason())); + sprintf(reply + strlen(reply), "\r\nLast boot voltage: %u mV", _board->getBootVoltage()); +#endif } else if (memcmp(command, "log start", 9) == 0) { _callbacks->setLoggingOn(true); strcpy(reply, " logging on"); @@ -477,257 +522,279 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "OK - %d.%d%%", a_int, a_frac); } } else if (memcmp(config, "af ", 3) == 0) { - _prefs->airtime_factor = atof(&config[3]); - savePrefs(); - strcpy(reply, "OK"); - } else if (memcmp(config, "int.thresh ", 11) == 0) { - _prefs->interference_threshold = atoi(&config[11]); - savePrefs(); - strcpy(reply, "OK"); - } else if (memcmp(config, "agc.reset.interval ", 19) == 0) { - _prefs->agc_reset_interval = atoi(&config[19]) / 4; - savePrefs(); - sprintf(reply, "OK - interval rounded to %d", ((uint32_t) _prefs->agc_reset_interval) * 4); - } else if (memcmp(config, "multi.acks ", 11) == 0) { - _prefs->multi_acks = atoi(&config[11]); - savePrefs(); - strcpy(reply, "OK"); - } else if (memcmp(config, "allow.read.only ", 16) == 0) { - _prefs->allow_read_only = memcmp(&config[16], "on", 2) == 0; - savePrefs(); - strcpy(reply, "OK"); - } else if (memcmp(config, "flood.advert.interval ", 22) == 0) { - int hours = _atoi(&config[22]); - if ((hours > 0 && hours < 3) || (hours > 168)) { - strcpy(reply, "Error: interval range is 3-168 hours"); - } else { - _prefs->flood_advert_interval = (uint8_t)(hours); - _callbacks->updateFloodAdvertTimer(); - savePrefs(); - strcpy(reply, "OK"); - } - } else if (memcmp(config, "advert.interval ", 16) == 0) { - int mins = _atoi(&config[16]); - if ((mins > 0 && mins < MIN_LOCAL_ADVERT_INTERVAL) || (mins > 240)) { - sprintf(reply, "Error: interval range is %d-240 minutes", MIN_LOCAL_ADVERT_INTERVAL); - } else { - _prefs->advert_interval = (uint8_t)(mins / 2); - _callbacks->updateAdvertTimer(); - savePrefs(); - strcpy(reply, "OK"); - } - } else if (memcmp(config, "guest.password ", 15) == 0) { - StrHelper::strncpy(_prefs->guest_password, &config[15], sizeof(_prefs->guest_password)); - savePrefs(); - strcpy(reply, "OK"); - } else if (memcmp(config, "prv.key ", 8) == 0) { - uint8_t prv_key[PRV_KEY_SIZE]; - bool success = mesh::Utils::fromHex(prv_key, PRV_KEY_SIZE, &config[8]); - // only allow rekey if key is valid - if (success && mesh::LocalIdentity::validatePrivateKey(prv_key)) { - mesh::LocalIdentity new_id; - new_id.readFrom(prv_key, PRV_KEY_SIZE); - _callbacks->saveIdentity(new_id); - strcpy(reply, "OK, reboot to apply! New pubkey: "); - mesh::Utils::toHex(&reply[33], new_id.pub_key, PUB_KEY_SIZE); - } else { - strcpy(reply, "Error, bad key"); - } - } else if (memcmp(config, "name ", 5) == 0) { - if (isValidName(&config[5])) { - StrHelper::strncpy(_prefs->node_name, &config[5], sizeof(_prefs->node_name)); - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error, bad chars"); - } - } else if (memcmp(config, "repeat ", 7) == 0) { - _prefs->disable_fwd = memcmp(&config[7], "off", 3) == 0; - savePrefs(); - strcpy(reply, _prefs->disable_fwd ? "OK - repeat is now OFF" : "OK - repeat is now ON"); + _prefs->airtime_factor = atof(&config[3]); + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "int.thresh ", 11) == 0) { + _prefs->interference_threshold = atoi(&config[11]); + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "agc.reset.interval ", 19) == 0) { + _prefs->agc_reset_interval = atoi(&config[19]) / 4; + savePrefs(); + sprintf(reply, "OK - interval rounded to %d", ((uint32_t) _prefs->agc_reset_interval) * 4); + } else if (memcmp(config, "multi.acks ", 11) == 0) { + _prefs->multi_acks = atoi(&config[11]); + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "allow.read.only ", 16) == 0) { + _prefs->allow_read_only = memcmp(&config[16], "on", 2) == 0; + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "flood.advert.interval ", 22) == 0) { + int hours = _atoi(&config[22]); + if ((hours > 0 && hours < 3) || (hours > 168)) { + strcpy(reply, "Error: interval range is 3-168 hours"); + } else { + _prefs->flood_advert_interval = (uint8_t)(hours); + _callbacks->updateFloodAdvertTimer(); + savePrefs(); + strcpy(reply, "OK"); + } + } else if (memcmp(config, "advert.interval ", 16) == 0) { + int mins = _atoi(&config[16]); + if ((mins > 0 && mins < MIN_LOCAL_ADVERT_INTERVAL) || (mins > 240)) { + sprintf(reply, "Error: interval range is %d-240 minutes", MIN_LOCAL_ADVERT_INTERVAL); + } else { + _prefs->advert_interval = (uint8_t)(mins / 2); + _callbacks->updateAdvertTimer(); + savePrefs(); + strcpy(reply, "OK"); + } + } else if (memcmp(config, "guest.password ", 15) == 0) { + StrHelper::strncpy(_prefs->guest_password, &config[15], sizeof(_prefs->guest_password)); + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "prv.key ", 8) == 0) { + uint8_t prv_key[PRV_KEY_SIZE]; + bool success = mesh::Utils::fromHex(prv_key, PRV_KEY_SIZE, &config[8]); + // only allow rekey if key is valid + if (success && mesh::LocalIdentity::validatePrivateKey(prv_key)) { + mesh::LocalIdentity new_id; + new_id.readFrom(prv_key, PRV_KEY_SIZE); + _callbacks->saveIdentity(new_id); + strcpy(reply, "OK, reboot to apply! New pubkey: "); + mesh::Utils::toHex(&reply[33], new_id.pub_key, PUB_KEY_SIZE); + } else { + strcpy(reply, "Error, bad key"); + } + } else if (memcmp(config, "name ", 5) == 0) { + if (isValidName(&config[5])) { + StrHelper::strncpy(_prefs->node_name, &config[5], sizeof(_prefs->node_name)); + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, bad chars"); + } + } else if (memcmp(config, "repeat ", 7) == 0) { + _prefs->disable_fwd = memcmp(&config[7], "off", 3) == 0; + savePrefs(); + strcpy(reply, _prefs->disable_fwd ? "OK - repeat is now OFF" : "OK - repeat is now ON"); #if defined(USE_SX1262) || defined(USE_SX1268) } else if (memcmp(config, "radio.rxgain ", 13) == 0) { - _prefs->rx_boosted_gain = memcmp(&config[13], "on", 2) == 0; - strcpy(reply, "OK"); - savePrefs(); - _callbacks->setRxBoostedGain(_prefs->rx_boosted_gain); + _prefs->rx_boosted_gain = memcmp(&config[13], "on", 2) == 0; + strcpy(reply, "OK"); + savePrefs(); + _callbacks->setRxBoostedGain(_prefs->rx_boosted_gain); #endif - } else if (memcmp(config, "radio ", 6) == 0) { - strcpy(tmp, &config[6]); - const char *parts[4]; - int num = mesh::Utils::parseTextParts(tmp, parts, 4); - float freq = num > 0 ? strtof(parts[0], nullptr) : 0.0f; - float bw = num > 1 ? strtof(parts[1], nullptr) : 0.0f; - uint8_t sf = num > 2 ? atoi(parts[2]) : 0; - uint8_t cr = num > 3 ? atoi(parts[3]) : 0; + } else if (memcmp(config, "radio.fem.rxgain ", 17) == 0) { + if (!_board->canControlLoRaFemLna()) { + strcpy(reply, "Error: unsupported by this board"); + } else if (memcmp(&config[17], "on", 2) == 0) { + if (_board->setLoRaFemLnaEnabled(true)) { + _prefs->radio_fem_rxgain = 1; + savePrefs(); + strcpy(reply, "OK - LoRa FEM RX gain on"); + } else { + strcpy(reply, "Error: failed to apply LoRa FEM RX gain"); + } + } else if (memcmp(&config[17], "off", 3) == 0) { + if (_board->setLoRaFemLnaEnabled(false)) { + _prefs->radio_fem_rxgain = 0; + savePrefs(); + strcpy(reply, "OK - LoRa FEM RX gain off"); + } else { + strcpy(reply, "Error: failed to apply LoRa FEM RX gain"); + } + } else { + strcpy(reply, "Error: state must be on or off"); + } + } else if (memcmp(config, "radio ", 6) == 0) { + strcpy(tmp, &config[6]); + const char *parts[4]; + int num = mesh::Utils::parseTextParts(tmp, parts, 4); + float freq = num > 0 ? strtof(parts[0], nullptr) : 0.0f; + float bw = num > 1 ? strtof(parts[1], nullptr) : 0.0f; + uint8_t sf = num > 2 ? atoi(parts[2]) : 0; + uint8_t cr = num > 3 ? atoi(parts[3]) : 0; if (freq >= 150.0f && freq <= 2500.0f && sf >= 5 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7.0f && bw <= 500.0f) { - _prefs->sf = sf; - _prefs->cr = cr; - _prefs->freq = freq; - _prefs->bw = bw; - _callbacks->savePrefs(); - strcpy(reply, "OK - reboot to apply"); - } else { - strcpy(reply, "Error, invalid radio params"); - } - } else if (memcmp(config, "lat ", 4) == 0) { - _prefs->node_lat = atof(&config[4]); - savePrefs(); - strcpy(reply, "OK"); - } else if (memcmp(config, "lon ", 4) == 0) { - _prefs->node_lon = atof(&config[4]); - savePrefs(); - strcpy(reply, "OK"); - } else if (memcmp(config, "rxdelay ", 8) == 0) { - float db = atof(&config[8]); - if (db >= 0) { - _prefs->rx_delay_base = db; - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error, cannot be negative"); - } - } else if (memcmp(config, "txdelay ", 8) == 0) { - float f = atof(&config[8]); - if (f >= 0) { - _prefs->tx_delay_factor = f; - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error, cannot be negative"); - } - } else if (memcmp(config, "flood.max ", 10) == 0) { - uint8_t m = atoi(&config[10]); - if (m <= 64) { - _prefs->flood_max = m; - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error, max 64"); - } - } else if (memcmp(config, "direct.txdelay ", 15) == 0) { - float f = atof(&config[15]); - if (f >= 0) { - _prefs->direct_tx_delay_factor = f; - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error, cannot be negative"); - } - } else if (memcmp(config, "owner.info ", 11) == 0) { - config += 11; - char *dp = _prefs->owner_info; - while (*config && dp - _prefs->owner_info < sizeof(_prefs->owner_info)-1) { - *dp++ = (*config == '|') ? '\n' : *config; // translate '|' to newline chars - config++; - } - *dp = 0; - savePrefs(); - strcpy(reply, "OK"); - } else if (memcmp(config, "path.hash.mode ", 15) == 0) { - config += 15; - uint8_t mode = atoi(config); - if (mode < 3) { - _prefs->path_hash_mode = mode; - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error, must be 0,1, or 2"); - } - } else if (memcmp(config, "loop.detect ", 12) == 0) { - config += 12; - uint8_t mode; - if (memcmp(config, "off", 3) == 0) { - mode = LOOP_DETECT_OFF; - } else if (memcmp(config, "minimal", 7) == 0) { - mode = LOOP_DETECT_MINIMAL; - } else if (memcmp(config, "moderate", 8) == 0) { - mode = LOOP_DETECT_MODERATE; - } else if (memcmp(config, "strict", 6) == 0) { - mode = LOOP_DETECT_STRICT; - } else { - mode = 0xFF; - strcpy(reply, "Error, must be: off, minimal, moderate, or strict"); - } - if (mode != 0xFF) { - _prefs->loop_detect = mode; - savePrefs(); - strcpy(reply, "OK"); - } - } else if (memcmp(config, "tx ", 3) == 0) { - _prefs->tx_power_dbm = atoi(&config[3]); - savePrefs(); - _callbacks->setTxPower(_prefs->tx_power_dbm); - strcpy(reply, "OK"); - } else if (sender_timestamp == 0 && memcmp(config, "freq ", 5) == 0) { - _prefs->freq = atof(&config[5]); - savePrefs(); - strcpy(reply, "OK - reboot to apply"); + _prefs->sf = sf; + _prefs->cr = cr; + _prefs->freq = freq; + _prefs->bw = bw; + _callbacks->savePrefs(); + strcpy(reply, "OK - reboot to apply"); + } else { + strcpy(reply, "Error, invalid radio params"); + } + } else if (memcmp(config, "lat ", 4) == 0) { + _prefs->node_lat = atof(&config[4]); + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "lon ", 4) == 0) { + _prefs->node_lon = atof(&config[4]); + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "rxdelay ", 8) == 0) { + float db = atof(&config[8]); + if (db >= 0) { + _prefs->rx_delay_base = db; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, cannot be negative"); + } + } else if (memcmp(config, "txdelay ", 8) == 0) { + float f = atof(&config[8]); + if (f >= 0) { + _prefs->tx_delay_factor = f; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, cannot be negative"); + } + } else if (memcmp(config, "flood.max ", 10) == 0) { + uint8_t m = atoi(&config[10]); + if (m <= 64) { + _prefs->flood_max = m; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, max 64"); + } + } else if (memcmp(config, "direct.txdelay ", 15) == 0) { + float f = atof(&config[15]); + if (f >= 0) { + _prefs->direct_tx_delay_factor = f; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, cannot be negative"); + } + } else if (memcmp(config, "owner.info ", 11) == 0) { + config += 11; + char *dp = _prefs->owner_info; + while (*config && dp - _prefs->owner_info < sizeof(_prefs->owner_info)-1) { + *dp++ = (*config == '|') ? '\n' : *config; // translate '|' to newline chars + config++; + } + *dp = 0; + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "path.hash.mode ", 15) == 0) { + config += 15; + uint8_t mode = atoi(config); + if (mode < 3) { + _prefs->path_hash_mode = mode; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be 0,1, or 2"); + } + } else if (memcmp(config, "loop.detect ", 12) == 0) { + config += 12; + uint8_t mode; + if (memcmp(config, "off", 3) == 0) { + mode = LOOP_DETECT_OFF; + } else if (memcmp(config, "minimal", 7) == 0) { + mode = LOOP_DETECT_MINIMAL; + } else if (memcmp(config, "moderate", 8) == 0) { + mode = LOOP_DETECT_MODERATE; + } else if (memcmp(config, "strict", 6) == 0) { + mode = LOOP_DETECT_STRICT; + } else { + mode = 0xFF; + strcpy(reply, "Error, must be: off, minimal, moderate, or strict"); + } + if (mode != 0xFF) { + _prefs->loop_detect = mode; + savePrefs(); + strcpy(reply, "OK"); + } + } else if (memcmp(config, "tx ", 3) == 0) { + _prefs->tx_power_dbm = atoi(&config[3]); + savePrefs(); + _callbacks->setTxPower(_prefs->tx_power_dbm); + strcpy(reply, "OK"); + } else if (sender_timestamp == 0 && memcmp(config, "freq ", 5) == 0) { + _prefs->freq = atof(&config[5]); + savePrefs(); + strcpy(reply, "OK - reboot to apply"); #ifdef WITH_BRIDGE - } else if (memcmp(config, "bridge.enabled ", 15) == 0) { - _prefs->bridge_enabled = memcmp(&config[15], "on", 2) == 0; - _callbacks->setBridgeState(_prefs->bridge_enabled); - savePrefs(); - strcpy(reply, "OK"); - } else if (memcmp(config, "bridge.delay ", 13) == 0) { - int delay = _atoi(&config[13]); - if (delay >= 0 && delay <= 10000) { - _prefs->bridge_delay = (uint16_t)delay; - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error: delay must be between 0-10000 ms"); - } - } else if (memcmp(config, "bridge.source ", 14) == 0) { - _prefs->bridge_pkt_src = memcmp(&config[14], "rx", 2) == 0; - savePrefs(); - strcpy(reply, "OK"); + } else if (memcmp(config, "bridge.enabled ", 15) == 0) { + _prefs->bridge_enabled = memcmp(&config[15], "on", 2) == 0; + _callbacks->setBridgeState(_prefs->bridge_enabled); + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "bridge.delay ", 13) == 0) { + int delay = _atoi(&config[13]); + if (delay >= 0 && delay <= 10000) { + _prefs->bridge_delay = (uint16_t)delay; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error: delay must be between 0-10000 ms"); + } + } else if (memcmp(config, "bridge.source ", 14) == 0) { + _prefs->bridge_pkt_src = memcmp(&config[14], "rx", 2) == 0; + savePrefs(); + strcpy(reply, "OK"); #endif #ifdef WITH_RS232_BRIDGE - } else if (memcmp(config, "bridge.baud ", 12) == 0) { - uint32_t baud = atoi(&config[12]); - if (baud >= 9600 && baud <= BRIDGE_MAX_BAUD) { - _prefs->bridge_baud = (uint32_t)baud; - _callbacks->restartBridge(); - savePrefs(); - strcpy(reply, "OK"); - } else { - sprintf(reply, "Error: baud rate must be between 9600-%d",BRIDGE_MAX_BAUD); - } + } else if (memcmp(config, "bridge.baud ", 12) == 0) { + uint32_t baud = atoi(&config[12]); + if (baud >= 9600 && baud <= BRIDGE_MAX_BAUD) { + _prefs->bridge_baud = (uint32_t)baud; + _callbacks->restartBridge(); + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error: baud rate must be between 9600-%d",BRIDGE_MAX_BAUD); + } #endif #ifdef WITH_ESPNOW_BRIDGE - } else if (memcmp(config, "bridge.channel ", 15) == 0) { - int ch = atoi(&config[15]); - if (ch > 0 && ch < 15) { - _prefs->bridge_channel = (uint8_t)ch; - _callbacks->restartBridge(); - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error: channel must be between 1-14"); - } - } else if (memcmp(config, "bridge.secret ", 14) == 0) { - StrHelper::strncpy(_prefs->bridge_secret, &config[14], sizeof(_prefs->bridge_secret)); - _callbacks->restartBridge(); - savePrefs(); - strcpy(reply, "OK"); + } else if (memcmp(config, "bridge.channel ", 15) == 0) { + int ch = atoi(&config[15]); + if (ch > 0 && ch < 15) { + _prefs->bridge_channel = (uint8_t)ch; + _callbacks->restartBridge(); + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error: channel must be between 1-14"); + } + } else if (memcmp(config, "bridge.secret ", 14) == 0) { + StrHelper::strncpy(_prefs->bridge_secret, &config[14], sizeof(_prefs->bridge_secret)); + _callbacks->restartBridge(); + savePrefs(); + strcpy(reply, "OK"); #endif - } else if (memcmp(config, "adc.multiplier ", 15) == 0) { - _prefs->adc_multiplier = atof(&config[15]); - if (_board->setAdcMultiplier(_prefs->adc_multiplier)) { - savePrefs(); - if (_prefs->adc_multiplier == 0.0f) { - strcpy(reply, "OK - using default board multiplier"); + } else if (memcmp(config, "adc.multiplier ", 15) == 0) { + _prefs->adc_multiplier = atof(&config[15]); + if (_board->setAdcMultiplier(_prefs->adc_multiplier)) { + savePrefs(); + if (_prefs->adc_multiplier == 0.0f) { + strcpy(reply, "OK - using default board multiplier"); + } else { + sprintf(reply, "OK - multiplier set to %.3f", _prefs->adc_multiplier); + } + } else { + _prefs->adc_multiplier = 0.0f; + strcpy(reply, "Error: unsupported by this board"); + }; } else { - sprintf(reply, "OK - multiplier set to %.3f", _prefs->adc_multiplier); + sprintf(reply, "unknown config: %s", config); } - } else { - _prefs->adc_multiplier = 0.0f; - strcpy(reply, "Error: unsupported by this board"); - }; - } else { - sprintf(reply, "unknown config: %s", config); - } } void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* reply) { @@ -801,9 +868,9 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep strcpy(reply, "> minimal"); } else if (_prefs->loop_detect == LOOP_DETECT_MODERATE) { strcpy(reply, "> moderate"); - } else { + } else { strcpy(reply, "> strict"); - } + } } else if (memcmp(config, "tx", 2) == 0 && (config[2] == 0 || config[2] == ' ')) { sprintf(reply, "> %d", (int32_t) _prefs->tx_power_dbm); } else if (memcmp(config, "freq", 4) == 0) { diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index ffdc7c65..828f89f0 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -59,6 +59,7 @@ struct NodePrefs { // persisted to file float adc_multiplier; char owner_info[120]; uint8_t rx_boosted_gain; // power settings + uint8_t radio_fem_rxgain; // LoRa FEM RX gain setting uint8_t path_hash_mode; // which path mode to use when sending uint8_t loop_detect; }; diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index c2d78ae0..6d527b66 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -12,17 +12,19 @@ #include #include #include -#include "driver/rtc_io.h" +#include "soc/rtc.h" +#include "esp_system.h" class ESP32Board : public mesh::MainBoard { protected: uint8_t startup_reason; bool inhibit_sleep = false; + static inline portMUX_TYPE sleepMux = portMUX_INITIALIZER_UNLOCKED; public: void begin() { // for future use, sub-classes SHOULD call this from their begin() - startup_reason = BD_STARTUP_NORMAL; + startup_reason = BD_STARTUP_NORMAL; #ifdef ESP32_CPU_FREQ setCpuFrequencyMhz(ESP32_CPU_FREQ); @@ -45,7 +47,7 @@ public: #endif #else Wire.begin(); - #endif + #endif } // Temperature from ESP32 MCU @@ -60,25 +62,60 @@ public: return raw / 4; } - void enterLightSleep(uint32_t secs) { -#if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(P_LORA_DIO_1) // Supported ESP32 variants - if (rtc_gpio_is_valid_gpio((gpio_num_t)P_LORA_DIO_1)) { // Only enter sleep mode if P_LORA_DIO_1 is RTC pin - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - esp_sleep_enable_ext1_wakeup((1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // To wake up when receiving a LoRa packet - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); // To wake up every hour to do periodically jobs - } - - esp_light_sleep_start(); // CPU enters light sleep - } -#endif + uint32_t getIRQGpio() { + return P_LORA_DIO_1; // default for SX1262 } void sleep(uint32_t secs) override { - if (!inhibit_sleep) { - enterLightSleep(secs); // To wake up after "secs" seconds or when receiving a LoRa packet + // Skip if not allow to sleep + if (inhibit_sleep) { + delay(1); // Give MCU to OTA to run + return; } + + // Use more accurate clock in sleep +#if SOC_RTC_SLOW_CLK_SUPPORT_RC_FAST_D256 + if (rtc_clk_slow_src_get() != SOC_RTC_SLOW_CLK_SRC_RC_FAST) { + + // Switch slow clock source to RC_FAST / 256 (~31.25 kHz) + rtc_clk_slow_src_set(SOC_RTC_SLOW_CLK_SRC_RC_FAST); + + // Calibrate slow clock + esp_clk_slow_boot_cal(1024); + } +#endif + + // Set GPIO wakeup + gpio_num_t wakeupPin = (gpio_num_t)getIRQGpio(); + + // Configure timer wakeup + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000ULL); // Wake up periodically to do scheduled jobs + } + + // Disable CPU interrupt servicing + portENTER_CRITICAL(&sleepMux); + + // Skip sleep if there is a LoRa packet + if (gpio_get_level(wakeupPin) == HIGH) { + portEXIT_CRITICAL(&sleepMux); + delay(1); + return; + } + + // Configure GPIO wakeup + esp_sleep_enable_gpio_wakeup(); + gpio_wakeup_enable((gpio_num_t)wakeupPin, GPIO_INTR_HIGH_LEVEL); // Wake up when receiving a LoRa packet + + // MCU enters light sleep + esp_light_sleep_start(); + + // Avoid ISR flood during wakeup due to HIGH LEVEL interrupt + gpio_wakeup_disable(wakeupPin); + gpio_set_intr_type(wakeupPin, GPIO_INTR_POSEDGE); + + // Enable CPU interrupt servicing + portEXIT_CRITICAL(&sleepMux); } uint8_t getStartupReason() const override { return startup_reason; } @@ -102,7 +139,7 @@ public: #endif uint16_t getBattMilliVolts() override { - #ifdef PIN_VBAT_READ + #ifdef PIN_VBAT_READ analogReadResolution(12); uint32_t raw = 0; @@ -130,31 +167,88 @@ public: void setInhibitSleep(bool inhibit) { inhibit_sleep = inhibit; } + + uint32_t getResetReason() const override { + return esp_reset_reason(); + } + + // https://docs.espressif.com/projects/esp-idf/en/v4.4.7/esp32/api-reference/system/system.html + const char *getResetReasonString(uint32_t reason) { + switch (reason) { + case ESP_RST_UNKNOWN: + return "Unknown or first boot"; + case ESP_RST_POWERON: + return "Power-on reset"; + case ESP_RST_EXT: + return "External reset"; + case ESP_RST_SW: + return "Software reset"; + case ESP_RST_PANIC: + return "Panic / exception reset"; + case ESP_RST_INT_WDT: + return "Interrupt watchdog reset"; + case ESP_RST_TASK_WDT: + return "Task watchdog reset"; + case ESP_RST_WDT: + return "Other watchdog reset"; + case ESP_RST_DEEPSLEEP: + return "Wake from deep sleep"; + case ESP_RST_BROWNOUT: + return "Brownout (low voltage)"; + case ESP_RST_SDIO: + return "SDIO reset"; + default: + static char buf[40]; + snprintf(buf, sizeof(buf), "Unknown reset reason (%d)", reason); + return buf; + } + } }; +static RTC_NOINIT_ATTR uint32_t _rtc_backup_time; +static RTC_NOINIT_ATTR uint32_t _rtc_backup_magic; +#define RTC_BACKUP_MAGIC 0xAA55CC33 +#define RTC_TIME_MIN 1772323200 // 1 Mar 2026 + class ESP32RTCClock : public mesh::RTCClock { public: ESP32RTCClock() { } void begin() { esp_reset_reason_t reason = esp_reset_reason(); - if (reason == ESP_RST_POWERON) { - // start with some date/time in the recent past - struct timeval tv; - tv.tv_sec = 1715770351; // 15 May 2024, 8:50pm - tv.tv_usec = 0; - settimeofday(&tv, NULL); + if (reason == ESP_RST_DEEPSLEEP) { + return; // ESP-IDF preserves system time across deep sleep } + // All other resets (power-on, crash, WDT, brownout) lose system time. + // Restore from RTC backup if valid, otherwise use hardcoded seed. + struct timeval tv; + if (_rtc_backup_magic == RTC_BACKUP_MAGIC && _rtc_backup_time > RTC_TIME_MIN) { + tv.tv_sec = _rtc_backup_time; + } else { + tv.tv_sec = 1772323200; // 1 Mar 2026 + } + tv.tv_usec = 0; + settimeofday(&tv, NULL); } uint32_t getCurrentTime() override { time_t _now; time(&_now); return _now; } - void setCurrentTime(uint32_t time) override { + void setCurrentTime(uint32_t time) override { struct timeval tv; tv.tv_sec = time; tv.tv_usec = 0; settimeofday(&tv, NULL); + _rtc_backup_time = time; + _rtc_backup_magic = RTC_BACKUP_MAGIC; + } + void tick() override { + time_t now; + time(&now); + if (now > RTC_TIME_MIN && (uint32_t)now != _rtc_backup_time) { + _rtc_backup_time = (uint32_t)now; + _rtc_backup_magic = RTC_BACKUP_MAGIC; + } } }; diff --git a/src/helpers/SensorManager.h b/src/helpers/SensorManager.h index 89a174c2..d4aa63b7 100644 --- a/src/helpers/SensorManager.h +++ b/src/helpers/SensorManager.h @@ -2,6 +2,7 @@ #include #include "sensors/LocationProvider.h" +#include #define TELEM_PERM_BASE 0x01 // 'base' permission includes battery #define TELEM_PERM_LOCATION 0x02 @@ -15,6 +16,7 @@ public: double node_altitude; // altitude in meters SensorManager() { node_lat = 0; node_lon = 0; node_altitude = 0; } + virtual bool i2c_probe(TwoWire& wire, uint8_t addr) { return false; } virtual bool begin() { return false; } virtual bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { return false; } virtual void loop() { } diff --git a/src/helpers/esp32/TBeamBoard.h b/src/helpers/esp32/TBeamBoard.h index 4ff95551..98bd16bf 100644 --- a/src/helpers/esp32/TBeamBoard.h +++ b/src/helpers/esp32/TBeamBoard.h @@ -59,13 +59,13 @@ // uint32_t P_LORA_BUSY = 0; //shared, so define at run // uint32_t P_LORA_DIO_2 = 0; //SX1276 only, so define at run - #define P_LORA_DIO_0 26 - #define P_LORA_DIO_1 33 - #define P_LORA_NSS 18 - #define P_LORA_RESET 23 - #define P_LORA_SCLK 5 - #define P_LORA_MISO 19 - #define P_LORA_MOSI 27 + // #define P_LORA_DIO_0 26 + // #define P_LORA_DIO_1 33 + // #define P_LORA_NSS 18 + // #define P_LORA_RESET 23 + // #define P_LORA_SCLK 5 + // #define P_LORA_MISO 19 + // #define P_LORA_MOSI 27 // #define PIN_GPS_RX 34 // #define PIN_GPS_TX 12 diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 19472406..749ea689 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -7,8 +7,10 @@ #endif #ifdef ENV_INCLUDE_BME680 -#ifndef TELEM_BME680_ADDRESS -#define TELEM_BME680_ADDRESS 0x76 +#if defined(TELEM_BME680_ADDRESS) +uint8_t TELEM_BME680_ADDRESSES[] = { TELEM_BME680_ADDRESS }; +#else +uint8_t TELEM_BME680_ADDRESSES[] = { 0x76, 0x77 }; // Known I2C addresses for BME680 #endif #define TELEM_BME680_SEALEVELPRESSURE_HPA (1013.25) #include @@ -28,8 +30,10 @@ static Adafruit_AHTX0 AHTX0; #endif #if ENV_INCLUDE_BME280 -#ifndef TELEM_BME280_ADDRESS -#define TELEM_BME280_ADDRESS 0x76 // BME280 environmental sensor I2C address +#if defined(TELEM_BME280_ADDRESS) +uint8_t TELEM_BME280_ADDRESSES[] = { TELEM_BME280_ADDRESS }; +#else +uint8_t TELEM_BME280_ADDRESSES[] = { 0x76, 0x77 }; // Known I2C addresses for BME280 #endif #define TELEM_BME280_SEALEVELPRESSURE_HPA (1013.25) // Athmospheric pressure at sea level #include @@ -37,8 +41,10 @@ static Adafruit_BME280 BME280; #endif #if ENV_INCLUDE_BMP280 -#ifndef TELEM_BMP280_ADDRESS -#define TELEM_BMP280_ADDRESS 0x76 // BMP280 environmental sensor I2C address +#if defined(TELEM_BMP280_ADDRESS) +uint8_t TELEM_BMP280_ADDRESSES[] = { TELEM_BMP280_ADDRESS }; +#else +uint8_t TELEM_BMP280_ADDRESSES[] = { 0x76, 0x77 }; // Known I2C addresses for BMP280 #endif #define TELEM_BMP280_SEALEVELPRESSURE_HPA (1013.25) // Athmospheric pressure at sea level #include @@ -161,6 +167,12 @@ public: static RAK12500LocationProvider RAK12500_provider; #endif +bool EnvironmentSensorManager::i2c_probe(TwoWire &wire, uint8_t addr) { + wire.beginTransmission(addr); + uint8_t error = wire.endTransmission(); + return (error == 0); // If found i2c device, the error is 0 +} + bool EnvironmentSensorManager::begin() { #if ENV_INCLUDE_GPS #ifdef RAK_WISBLOCK_GPS @@ -182,7 +194,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_AHTX0 - if (AHTX0.begin(TELEM_WIRE, 0, TELEM_AHTX_ADDRESS)) { + if (i2c_probe(*TELEM_WIRE, TELEM_AHTX_ADDRESS) && AHTX0.begin(TELEM_WIRE, 0, TELEM_AHTX_ADDRESS)) { MESH_DEBUG_PRINTLN("Found AHT10/AHT20 at address: %02X", TELEM_AHTX_ADDRESS); AHTX0_initialized = true; } else { @@ -192,46 +204,55 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_BME680 - if (BME680.begin(TELEM_BME680_ADDRESS)) { - MESH_DEBUG_PRINTLN("Found BME680 at address: %02X", TELEM_BME680_ADDRESS); - BME680_initialized = true; - } else { - BME680_initialized = false; - MESH_DEBUG_PRINTLN("BME680 was not found at I2C address %02X", TELEM_BME680_ADDRESS); + for (size_t i = 0; i < sizeof(TELEM_BME680_ADDRESSES) / sizeof(TELEM_BME680_ADDRESSES[0]); i++) { + if (i2c_probe(*TELEM_WIRE, TELEM_BME680_ADDRESSES[i]) && BME680.begin(TELEM_BME680_ADDRESSES[i], TELEM_WIRE)) { + MESH_DEBUG_PRINTLN("Found BME680 at address: %02X", TELEM_BME680_ADDRESSES[i]); + BME680_initialized = true; + break; + } else { + BME680_initialized = false; + MESH_DEBUG_PRINTLN("BME680 was not found at I2C address %02X", TELEM_BME680_ADDRESSES[i]); + } } #endif #if ENV_INCLUDE_BME280 - if (BME280.begin(TELEM_BME280_ADDRESS, TELEM_WIRE)) { - MESH_DEBUG_PRINTLN("Found BME280 at address: %02X", TELEM_BME280_ADDRESS); - MESH_DEBUG_PRINTLN("BME sensor ID: %02X", BME280.sensorID()); - // Reduce self-heating: single-shot conversions, light oversampling, long standby. - BME280.setSampling(Adafruit_BME280::MODE_FORCED, + for (size_t i = 0; i < sizeof(TELEM_BME280_ADDRESSES) / sizeof(TELEM_BME280_ADDRESSES[0]); i++) { + if (i2c_probe(*TELEM_WIRE, TELEM_BME280_ADDRESSES[i]) && BME280.begin(TELEM_BME280_ADDRESSES[i], TELEM_WIRE)) { + MESH_DEBUG_PRINTLN("Found BME280 at address: %02X", TELEM_BME280_ADDRESSES[i]); + MESH_DEBUG_PRINTLN("BME sensor ID: %02X", BME280.sensorID()); + // Reduce self-heating: single-shot conversions, light oversampling, long standby. + BME280.setSampling(Adafruit_BME280::MODE_FORCED, Adafruit_BME280::SAMPLING_X1, // temperature Adafruit_BME280::SAMPLING_X1, // pressure Adafruit_BME280::SAMPLING_X1, // humidity Adafruit_BME280::FILTER_OFF, Adafruit_BME280::STANDBY_MS_1000); - BME280_initialized = true; - } else { - BME280_initialized = false; - MESH_DEBUG_PRINTLN("BME280 was not found at I2C address %02X", TELEM_BME280_ADDRESS); + BME280_initialized = true; + break; + } else { + BME280_initialized = false; + MESH_DEBUG_PRINTLN("BME280 was not found at I2C address %02X", TELEM_BME280_ADDRESSES[i]); + } } - #endif +#endif #if ENV_INCLUDE_BMP280 - if (BMP280.begin(TELEM_BMP280_ADDRESS)) { - MESH_DEBUG_PRINTLN("Found BMP280 at address: %02X", TELEM_BMP280_ADDRESS); - MESH_DEBUG_PRINTLN("BMP sensor ID: %02X", BMP280.sensorID()); - BMP280_initialized = true; - } else { - BMP280_initialized = false; - MESH_DEBUG_PRINTLN("BMP280 was not found at I2C address %02X", TELEM_BMP280_ADDRESS); - } - #endif + for (size_t i = 0; i < sizeof(TELEM_BMP280_ADDRESSES) / sizeof(TELEM_BMP280_ADDRESSES[0]); i++) { + if (i2c_probe(*TELEM_WIRE, TELEM_BMP280_ADDRESSES[i]) && BMP280.begin(TELEM_BMP280_ADDRESSES[i])) { + MESH_DEBUG_PRINTLN("Found BMP280 at address: %02X", TELEM_BMP280_ADDRESSES[i]); + MESH_DEBUG_PRINTLN("BMP sensor ID: %02X", BMP280.sensorID()); + BMP280_initialized = true; + break; + } else { + BMP280_initialized = false; + MESH_DEBUG_PRINTLN("BMP280 was not found at I2C address %02X", TELEM_BMP280_ADDRESSES[i]); + } + } +#endif #if ENV_INCLUDE_SHTC3 - if (SHTC3.begin(TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, 0x70) && SHTC3.begin(TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found sensor: SHTC3"); SHTC3_initialized = true; } else { @@ -242,21 +263,23 @@ bool EnvironmentSensorManager::begin() { #if ENV_INCLUDE_SHT4X - SHT4X.begin(*TELEM_WIRE, TELEM_SHT4X_ADDRESS); - uint32_t serialNumber = 0; - int16_t sht4x_error; - sht4x_error = SHT4X.serialNumber(serialNumber); - if (sht4x_error == 0) { - MESH_DEBUG_PRINTLN("Found SHT4X at address: %02X", TELEM_SHT4X_ADDRESS); - SHT4X_initialized = true; - } else { - SHT4X_initialized = false; - MESH_DEBUG_PRINTLN("SHT4X was not found at I2C address %02X", TELEM_SHT4X_ADDRESS); + if (i2c_probe(*TELEM_WIRE, TELEM_SHT4X_ADDRESS)) { + SHT4X.begin(*TELEM_WIRE, TELEM_SHT4X_ADDRESS); + uint32_t serialNumber = 0; + int16_t sht4x_error; + sht4x_error = SHT4X.serialNumber(serialNumber); + if (sht4x_error == 0) { + MESH_DEBUG_PRINTLN("Found SHT4X at address: %02X", TELEM_SHT4X_ADDRESS); + SHT4X_initialized = true; + } else { + SHT4X_initialized = false; + MESH_DEBUG_PRINTLN("SHT4X was not found at I2C address %02X", TELEM_SHT4X_ADDRESS); + } } #endif #if ENV_INCLUDE_LPS22HB - if (LPS22HB.begin()) { + if (i2c_probe(*TELEM_WIRE, 0x5C) && LPS22HB.begin()) { MESH_DEBUG_PRINTLN("Found sensor: LPS22HB"); LPS22HB_initialized = true; } else { @@ -266,7 +289,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_INA3221 - if (INA3221.begin(TELEM_INA3221_ADDRESS, TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, TELEM_INA3221_ADDRESS) && INA3221.begin(TELEM_INA3221_ADDRESS, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found INA3221 at address: %02X", TELEM_INA3221_ADDRESS); MESH_DEBUG_PRINTLN("%04X %04X", INA3221.getDieID(), INA3221.getManufacturerID()); @@ -281,7 +304,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_INA219 - if (INA219.begin(TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, TELEM_INA219_ADDRESS) && INA219.begin(TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found INA219 at address: %02X", TELEM_INA219_ADDRESS); INA219_initialized = true; } else { @@ -291,17 +314,17 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_INA260 - if (INA260.begin(TELEM_INA260_ADDRESS, TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, TELEM_INA260_ADDRESS) && INA260.begin(TELEM_INA260_ADDRESS, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found INA260 at address: %02X", TELEM_INA260_ADDRESS); INA260_initialized = true; } else { INA260_initialized = false; - MESH_DEBUG_PRINTLN("INA260 was not found at I2C address %02X", TELEM_INA260_ADDRESS); + MESH_DEBUG_PRINTLN("INA260 was not found at I2C address %02X", TELEM_INA219_ADDRESS); } #endif #if ENV_INCLUDE_INA226 - if (INA226.begin()) { + if (i2c_probe(*TELEM_WIRE, TELEM_INA226_ADDRESS) && INA226.begin()) { MESH_DEBUG_PRINTLN("Found INA226 at address: %02X", TELEM_INA226_ADDRESS); INA226.setMaxCurrentShunt(TELEM_INA226_MAX_AMP, TELEM_INA226_SHUNT_VALUE); INA226_initialized = true; @@ -312,7 +335,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_MLX90614 - if (MLX90614.begin(TELEM_MLX90614_ADDRESS, TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, TELEM_MLX90614_ADDRESS) && MLX90614.begin(TELEM_MLX90614_ADDRESS, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found MLX90614 at address: %02X", TELEM_MLX90614_ADDRESS); MLX90614_initialized = true; } else { @@ -322,7 +345,7 @@ bool EnvironmentSensorManager::begin() { #endif #if ENV_INCLUDE_VL53L0X - if (VL53L0X.begin(TELEM_VL53L0X_ADDRESS, false, TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, TELEM_VL53L0X_ADDRESS) && VL53L0X.begin(TELEM_VL53L0X_ADDRESS, false, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found VL53L0X at address: %02X", TELEM_VL53L0X_ADDRESS); VL53L0X_initialized = true; } else { @@ -334,7 +357,7 @@ bool EnvironmentSensorManager::begin() { #if ENV_INCLUDE_BMP085 // First argument is MODE (aka oversampling) // choose ULTRALOWPOWER - if (BMP085.begin(0, TELEM_WIRE)) { + if (i2c_probe(*TELEM_WIRE, 0x77) && BMP085.begin(0, TELEM_WIRE)) { MESH_DEBUG_PRINTLN("Found sensor BMP085"); BMP085_initialized = true; } else { @@ -345,7 +368,7 @@ bool EnvironmentSensorManager::begin() { #if ENV_INCLUDE_RAK12035 RAK12035.setup(*TELEM_WIRE); - if (RAK12035.begin(TELEM_RAK12035_ADDRESS)) { + if (i2c_probe(*TELEM_WIRE, TELEM_RAK12035_ADDRESS) && RAK12035.begin(TELEM_RAK12035_ADDRESS)) { MESH_DEBUG_PRINTLN("Found sensor RAK12035 at address: %02X", TELEM_RAK12035_ADDRESS); RAK12035_initialized = true; } else { @@ -716,7 +739,7 @@ bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){ gps_detected = true; return true; } - + pinMode(ioPin, INPUT); MESH_DEBUG_PRINTLN("GPS did not init with this IO pin... try the next"); return false; @@ -759,7 +782,7 @@ void EnvironmentSensorManager::loop() { #if ENV_INCLUDE_GPS if (gps_active) { - _location->loop(); + _location->loop(); } if (millis() > next_gps_update) { diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index 32413ebc..38de5560 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -47,6 +47,7 @@ public: #else EnvironmentSensorManager(){}; #endif + bool i2c_probe(TwoWire& wire, uint8_t addr) override; bool begin() override; bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; #if ENV_INCLUDE_GPS diff --git a/variants/gat562_30s_mesh_kit/platformio.ini b/variants/gat562_30s_mesh_kit/platformio.ini index 1467f0fa..4266d134 100644 --- a/variants/gat562_30s_mesh_kit/platformio.ini +++ b/variants/gat562_30s_mesh_kit/platformio.ini @@ -7,7 +7,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/gat562_30s_mesh_kit -D RAK_4631 -D RAK_BOARD - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D PIN_OLED_RESET=-1 diff --git a/variants/gat562_mesh_tracker_pro/platformio.ini b/variants/gat562_mesh_tracker_pro/platformio.ini index 8a947bce..cf25424b 100644 --- a/variants/gat562_mesh_tracker_pro/platformio.ini +++ b/variants/gat562_mesh_tracker_pro/platformio.ini @@ -5,7 +5,7 @@ board_check = true build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I variants/gat562_mesh_tracker_pro - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D PIN_OLED_RESET=-1 diff --git a/variants/heltec_t114/platformio.ini b/variants/heltec_t114/platformio.ini index b985030f..8011641d 100644 --- a/variants/heltec_t114/platformio.ini +++ b/variants/heltec_t114/platformio.ini @@ -12,7 +12,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/heltec_t114 -I src/helpers/ui -D HELTEC_T114 - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D P_LORA_DIO_1=20 -D P_LORA_NSS=24 -D P_LORA_RESET=25 diff --git a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp index aabfed79..f182c905 100644 --- a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp +++ b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp @@ -82,3 +82,21 @@ void HeltecTrackerV2Board::begin() { const char* HeltecTrackerV2Board::getManufacturerName() const { return "Heltec Tracker V2"; } + + bool HeltecTrackerV2Board::setLoRaFemLnaEnabled(bool enable) { + if (!loRaFEMControl.isLnaCanControl()) { + return false; + } + + loRaFEMControl.setLNAEnable(enable); + loRaFEMControl.setRxModeEnable(); + return true; + } + + bool HeltecTrackerV2Board::canControlLoRaFemLna() const { + return loRaFEMControl.isLnaCanControl(); + } + + bool HeltecTrackerV2Board::isLoRaFemLnaEnabled() const { + return loRaFEMControl.isLNAEnabled(); + } diff --git a/variants/heltec_tracker_v2/HeltecTrackerV2Board.h b/variants/heltec_tracker_v2/HeltecTrackerV2Board.h index 33c897bc..ccbecc7a 100644 --- a/variants/heltec_tracker_v2/HeltecTrackerV2Board.h +++ b/variants/heltec_tracker_v2/HeltecTrackerV2Board.h @@ -21,5 +21,8 @@ public: void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; + bool setLoRaFemLnaEnabled(bool enable) override; + bool canControlLoRaFemLna() const override; + bool isLoRaFemLnaEnabled() const override; }; diff --git a/variants/heltec_tracker_v2/LoRaFEMControl.h b/variants/heltec_tracker_v2/LoRaFEMControl.h index 2c50b742..0ce60fff 100644 --- a/variants/heltec_tracker_v2/LoRaFEMControl.h +++ b/variants/heltec_tracker_v2/LoRaFEMControl.h @@ -12,8 +12,9 @@ class LoRaFEMControl void setRxModeEnable(void); void setRxModeEnableWhenMCUSleep(void); void setLNAEnable(bool enabled); - bool isLnaCanControl(void) { return lna_can_control; } + bool isLnaCanControl(void) const { return lna_can_control; } void setLnaCanControl(bool can_control) { lna_can_control = can_control; } + bool isLNAEnabled(void) const { return lna_enabled; } private: bool lna_enabled = false; diff --git a/variants/heltec_v2/HeltecV2Board.h b/variants/heltec_v2/HeltecV2Board.h index a6221036..fe800890 100644 --- a/variants/heltec_v2/HeltecV2Board.h +++ b/variants/heltec_v2/HeltecV2Board.h @@ -17,12 +17,12 @@ public: esp_reset_reason_t reason = esp_reset_reason(); if (reason == ESP_RST_DEEPSLEEP) { long wakeup_source = esp_sleep_get_ext1_wakeup_status(); - if (wakeup_source & (1 << P_LORA_DIO_1)) { // received a LoRa packet (while in deep sleep) + if (wakeup_source & (1 << P_LORA_DIO_0)) { // received a LoRa packet (while in deep sleep) startup_reason = BD_STARTUP_RX_PACKET; } rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS); - rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1); + rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_0); } } @@ -30,15 +30,15 @@ public: esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); + rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_0, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_0); rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_0), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_0) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn } if (secs > 0) { @@ -64,4 +64,8 @@ public: const char* getManufacturerName() const override { return "Heltec V2"; } + + uint32_t getIRQGpio() override { + return P_LORA_DIO_0; // default for SX1276 + } }; diff --git a/variants/heltec_v2/platformio.ini b/variants/heltec_v2/platformio.ini index 99f6f7e1..2ff6e9ff 100644 --- a/variants/heltec_v2/platformio.ini +++ b/variants/heltec_v2/platformio.ini @@ -7,10 +7,10 @@ build_flags = -D HELTEC_LORA_V2 -D RADIO_CLASS=CustomSX1276 -D WRAPPER_CLASS=CustomSX1276Wrapper - -D P_LORA_DIO_1=26 + -D P_LORA_DIO_0=26 + -D P_LORA_DIO_1=35 -D P_LORA_NSS=18 - -D P_LORA_RESET=RADIOLIB_NC - -D P_LORA_BUSY=RADIOLIB_NC + -D P_LORA_RESET=14 -D P_LORA_SCLK=5 -D P_LORA_MISO=19 -D P_LORA_MOSI=27 @@ -172,6 +172,31 @@ lib_deps = ${Heltec_lora32_v2.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_v2_companion_radio_ble_ps] +extends = Heltec_lora32_v2 +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${Heltec_lora32_v2.build_flags} + -I examples/companion_radio/ui-new + -D DISPLAY_CLASS=SSD1306Display + -D MAX_CONTACTS=160 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v2.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_lora32_v2.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:Heltec_v2_companion_radio_wifi] extends = Heltec_lora32_v2 build_flags = diff --git a/variants/heltec_v2/target.cpp b/variants/heltec_v2/target.cpp index 2dfb4c6e..54a2d7b2 100644 --- a/variants/heltec_v2/target.cpp +++ b/variants/heltec_v2/target.cpp @@ -5,9 +5,9 @@ HeltecV2Board board; #if defined(P_LORA_SCLK) static SPIClass spi; - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi); #else - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1); #endif WRAPPER_CLASS radio_driver(radio, board); diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 803ee683..6a62b24d 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -179,6 +179,32 @@ lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_v3_companion_radio_ble_ps] +extends = Heltec_lora32_v3 +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${Heltec_lora32_v3.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D DISPLAY_CLASS=SSD1306Display + -D BLE_PIN_CODE=123456 ; dynamic, random PIN + -D AUTO_SHUTDOWN_MILLIVOLTS=3400 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_lora32_v3.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:Heltec_v3_companion_radio_wifi] extends = Heltec_lora32_v3 build_flags = diff --git a/variants/heltec_v4/HeltecV4Board.cpp b/variants/heltec_v4/HeltecV4Board.cpp index 49580d2e..4537276f 100644 --- a/variants/heltec_v4/HeltecV4Board.cpp +++ b/variants/heltec_v4/HeltecV4Board.cpp @@ -20,7 +20,7 @@ void HeltecV4Board::begin() { rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS); rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1); } - } +} void HeltecV4Board::onBeforeTransmit(void) { digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on @@ -83,3 +83,25 @@ void HeltecV4Board::begin() { return loRaFEMControl.getFEMType() == KCT8103L_PA ? "Heltec V4.3 OLED" : "Heltec V4 OLED"; #endif } + + bool HeltecV4Board::setLoRaFemLnaEnabled(bool enable) { +#if defined(RADIO_FEM_RXGAIN) && (RADIO_FEM_RXGAIN == 0) + enable = false; +#endif + + if (!loRaFEMControl.isLnaCanControl()) { + return false; + } + + loRaFEMControl.setLNAEnable(enable); + loRaFEMControl.setRxModeEnable(); + return true; + } + + bool HeltecV4Board::canControlLoRaFemLna() const { + return loRaFEMControl.isLnaCanControl(); + } + + bool HeltecV4Board::isLoRaFemLnaEnabled() const { + return loRaFEMControl.isLNAEnabled(); + } diff --git a/variants/heltec_v4/HeltecV4Board.h b/variants/heltec_v4/HeltecV4Board.h index 4d5ee461..fe77caed 100644 --- a/variants/heltec_v4/HeltecV4Board.h +++ b/variants/heltec_v4/HeltecV4Board.h @@ -19,5 +19,8 @@ public: void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; + bool setLoRaFemLnaEnabled(bool enable) override; + bool canControlLoRaFemLna() const override; + bool isLoRaFemLnaEnabled() const override; }; diff --git a/variants/heltec_v4/LoRaFEMControl.h b/variants/heltec_v4/LoRaFEMControl.h index 75452965..d84ebe9c 100644 --- a/variants/heltec_v4/LoRaFEMControl.h +++ b/variants/heltec_v4/LoRaFEMControl.h @@ -18,8 +18,9 @@ class LoRaFEMControl void setRxModeEnable(void); void setRxModeEnableWhenMCUSleep(void); void setLNAEnable(bool enabled); - bool isLnaCanControl(void) { return lna_can_control; } + bool isLnaCanControl(void) const { return lna_can_control; } void setLnaCanControl(bool can_control) { lna_can_control = can_control; } + bool isLNAEnabled(void) const { return lna_enabled; } LoRaFEMType getFEMType(void) const { return fem_type; } private: LoRaFEMType fem_type=OTHER_FEM_TYPES; diff --git a/variants/heltec_v4/platformio.ini b/variants/heltec_v4/platformio.ini index 6f6bf2b5..bc038c8e 100644 --- a/variants/heltec_v4/platformio.ini +++ b/variants/heltec_v4/platformio.ini @@ -102,6 +102,28 @@ lib_deps = ${esp32_ota.lib_deps} bakercp/CRC32 @ ^2.0.0 +[env:heltec_v4_expansionkit_repeater] +extends = heltec_v4_oled +build_flags = + ${heltec_v4_oled.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D ADVERT_NAME='"Heltec Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + -D ENV_PIN_SDA=4 + -D ENV_PIN_SCL=3 +build_src_filter = ${heltec_v4_oled.build_src_filter} + + + +<../examples/simple_repeater> +lib_deps = + ${heltec_v4_oled.lib_deps} + ${esp32_ota.lib_deps} + bakercp/CRC32 @ ^2.0.0 + [env:heltec_v4_repeater_bridge_espnow] extends = heltec_v4_oled build_flags = @@ -200,6 +222,59 @@ lib_deps = ${heltec_v4_oled.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:heltec_v4_companion_radio_ble_ps] +extends = heltec_v4_oled +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${heltec_v4_oled.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D DISPLAY_CLASS=SSD1306Display + -D BLE_PIN_CODE=123456 ; dynamic, random PIN + -D AUTO_SHUTDOWN_MILLIVOLTS=3400 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${heltec_v4_oled.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${heltec_v4_oled.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:heltec_v4_3_companion_radio_ble_ps_femoff] +extends = heltec_v4_oled +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${heltec_v4_oled.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D DISPLAY_CLASS=SSD1306Display + -D BLE_PIN_CODE=123456 ; dynamic, random PIN + -D AUTO_SHUTDOWN_MILLIVOLTS=3400 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + -D RADIO_FEM_RXGAIN=0 ; undefined (default on), 1=on, 0=off +build_src_filter = ${heltec_v4_oled.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${heltec_v4_oled.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:heltec_v4_companion_radio_wifi] extends = heltec_v4_oled build_flags = @@ -207,7 +282,6 @@ build_flags = -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 - -D OFFLINE_QUEUE_SIZE=256 -D DISPLAY_CLASS=SSD1306Display -D WIFI_DEBUG_LOGGING=1 -D WIFI_SSID='"myssid"' diff --git a/variants/lilygo_t3s3_sx1276/LilygoT3S3SX1276Board.h b/variants/lilygo_t3s3_sx1276/LilygoT3S3SX1276Board.h new file mode 100644 index 00000000..7da620fd --- /dev/null +++ b/variants/lilygo_t3s3_sx1276/LilygoT3S3SX1276Board.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class LilygoT3S3SX1276Board : public ESP32Board { +public: + uint32_t getIRQGpio() override { + return P_LORA_DIO_0; // default for SX1276 + } +}; \ No newline at end of file diff --git a/variants/lilygo_t3s3_sx1276/target.cpp b/variants/lilygo_t3s3_sx1276/target.cpp index e7fe07a0..8236f459 100644 --- a/variants/lilygo_t3s3_sx1276/target.cpp +++ b/variants/lilygo_t3s3_sx1276/target.cpp @@ -1,7 +1,7 @@ #include #include "target.h" -ESP32Board board; +LilygoT3S3SX1276Board board; #if defined(P_LORA_SCLK) static SPIClass spi; diff --git a/variants/lilygo_t3s3_sx1276/target.h b/variants/lilygo_t3s3_sx1276/target.h index 2df4b3ed..079d2a7e 100644 --- a/variants/lilygo_t3s3_sx1276/target.h +++ b/variants/lilygo_t3s3_sx1276/target.h @@ -3,7 +3,7 @@ #define RADIOLIB_STATIC_ONLY 1 #include #include -#include +#include #include #include #include @@ -12,7 +12,7 @@ #include #endif -extern ESP32Board board; +extern LilygoT3S3SX1276Board board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SensorManager sensors; diff --git a/variants/lilygo_tbeam_SX1262/platformio.ini b/variants/lilygo_tbeam_SX1262/platformio.ini index d3bc7c99..1585dd74 100644 --- a/variants/lilygo_tbeam_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_SX1262/platformio.ini @@ -5,6 +5,13 @@ build_flags = ${esp32_base.build_flags} -I variants/lilygo_tbeam_SX1262 -D TBEAM_SX1262 + -D P_LORA_DIO_0=26 + -D P_LORA_DIO_1=33 + -D P_LORA_NSS=18 + -D P_LORA_RESET=23 + -D P_LORA_SCLK=5 + -D P_LORA_MISO=19 + -D P_LORA_MOSI=27 -D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=140 diff --git a/variants/lilygo_tbeam_SX1276/LilygoTBeamSX1276Board.h b/variants/lilygo_tbeam_SX1276/LilygoTBeamSX1276Board.h new file mode 100644 index 00000000..afe106d0 --- /dev/null +++ b/variants/lilygo_tbeam_SX1276/LilygoTBeamSX1276Board.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class LilygoTBeamSX1276Board : public TBeamBoard { +public: + uint32_t getIRQGpio() override { + return P_LORA_DIO_0; // default for SX1276 + } +}; \ No newline at end of file diff --git a/variants/lilygo_tbeam_SX1276/platformio.ini b/variants/lilygo_tbeam_SX1276/platformio.ini index 3562c40e..7482ef7b 100644 --- a/variants/lilygo_tbeam_SX1276/platformio.ini +++ b/variants/lilygo_tbeam_SX1276/platformio.ini @@ -5,6 +5,13 @@ build_flags = ${esp32_base.build_flags} -I variants/lilygo_tbeam_SX1276 -D TBEAM_SX1276 + -D P_LORA_DIO_0=26 + -D P_LORA_DIO_1=33 + -D P_LORA_NSS=18 + -D P_LORA_RESET=23 + -D P_LORA_SCLK=5 + -D P_LORA_MISO=19 + -D P_LORA_MOSI=27 -D SX127X_CURRENT_LIMIT=120 -D RADIO_CLASS=CustomSX1276 -D WRAPPER_CLASS=CustomSX1276Wrapper diff --git a/variants/lilygo_tbeam_SX1276/target.cpp b/variants/lilygo_tbeam_SX1276/target.cpp index 495337b8..5481d672 100644 --- a/variants/lilygo_tbeam_SX1276/target.cpp +++ b/variants/lilygo_tbeam_SX1276/target.cpp @@ -1,7 +1,7 @@ #include #include "target.h" -TBeamBoard board; +LilygoTBeamSX1276Board board; #if defined(P_LORA_SCLK) static SPIClass spi; diff --git a/variants/lilygo_tbeam_SX1276/target.h b/variants/lilygo_tbeam_SX1276/target.h index ad385645..abeaef46 100644 --- a/variants/lilygo_tbeam_SX1276/target.h +++ b/variants/lilygo_tbeam_SX1276/target.h @@ -3,7 +3,7 @@ #define RADIOLIB_STATIC_ONLY 1 //#include #include -#include +#include #include #include #include @@ -12,7 +12,7 @@ #include #endif -extern TBeamBoard board; +extern LilygoTBeamSX1276Board board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern EnvironmentSensorManager sensors; diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index ffee37a9..249e6871 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -5,6 +5,13 @@ build_flags = ${esp32_base.build_flags} -I variants/lilygo_tbeam_supreme_SX1262 -D TBEAM_SUPREME_SX1262 + -D P_LORA_DIO_0=26 + -D P_LORA_DIO_1=33 + -D P_LORA_NSS=18 + -D P_LORA_RESET=23 + -D P_LORA_SCLK=5 + -D P_LORA_MISO=19 + -D P_LORA_MOSI=27 -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 -D USE_SX1262 diff --git a/variants/lilygo_tlora_v2_1/LilyGoTLoraBoard.h b/variants/lilygo_tlora_v2_1/LilyGoTLoraBoard.h index 545219b2..f126f006 100644 --- a/variants/lilygo_tlora_v2_1/LilyGoTLoraBoard.h +++ b/variants/lilygo_tlora_v2_1/LilyGoTLoraBoard.h @@ -21,4 +21,8 @@ public: return (2 * raw); } + + uint32_t getIRQGpio() override { + return P_LORA_DIO_0; // default for SX1276 + } }; \ No newline at end of file diff --git a/variants/rak3401/platformio.ini b/variants/rak3401/platformio.ini index 3d2d4a3e..e1207114 100644 --- a/variants/rak3401/platformio.ini +++ b/variants/rak3401/platformio.ini @@ -6,7 +6,7 @@ build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I variants/rak3401 -D RAK_3401 - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index ea7e49c3..a96374a8 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -7,7 +7,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/rak4631 -D RAK_4631 -D RAK_BOARD - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D PIN_GPS_TX=PIN_SERIAL1_RX diff --git a/variants/sensecap_solar/platformio.ini b/variants/sensecap_solar/platformio.ini index aabbcf00..6aeb509b 100644 --- a/variants/sensecap_solar/platformio.ini +++ b/variants/sensecap_solar/platformio.ini @@ -10,7 +10,7 @@ build_flags = ${nrf52_base.build_flags} -I src/helpers/nrf52 -D NRF52_PLATFORM=1 -D USE_SX1262 - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D P_LORA_TX_LED=11 diff --git a/variants/xiao_c3/platformio.ini b/variants/xiao_c3/platformio.ini index c5254b46..244a39f1 100644 --- a/variants/xiao_c3/platformio.ini +++ b/variants/xiao_c3/platformio.ini @@ -90,6 +90,28 @@ lib_deps = ${esp32_ota.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Xiao_C3_companion_radio_ble_ps] +extends = Xiao_esp32_C3 +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_src_filter = ${Xiao_esp32_C3.build_src_filter} + +<../examples/companion_radio/*.cpp> + + +build_flags = + ${Xiao_esp32_C3.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D OFFLINE_QUEUE_SIZE=256 + ; -D BLE_DEBUG_LOGGING=1 + ; -D MESH_PACKET_LOGGING=1 + ; -D MESH_DEBUG=1 +lib_deps = + ${Xiao_esp32_C3.lib_deps} + ${esp32_ota.lib_deps} + densaugeo/base64 @ ~1.4.0 +board_build.partitions = min_spiffs.csv ; get around 4mb flash limit + [env:Xiao_C3_companion_radio_usb] extends = Xiao_esp32_C3 build_src_filter = ${Xiao_esp32_C3.build_src_filter} diff --git a/variants/xiao_nrf52/platformio.ini b/variants/xiao_nrf52/platformio.ini index a0854336..f4d1b93e 100644 --- a/variants/xiao_nrf52/platformio.ini +++ b/variants/xiao_nrf52/platformio.ini @@ -9,7 +9,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/xiao_nrf52 -UENV_INCLUDE_GPS -D NRF52_PLATFORM - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D XIAO_NRF52 -D USE_SX1262 -D RADIO_CLASS=CustomSX1262 diff --git a/variants/xiao_s3/XiaoS3Board.h b/variants/xiao_s3/XiaoS3Board.h new file mode 100644 index 00000000..288fcf62 --- /dev/null +++ b/variants/xiao_s3/XiaoS3Board.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +class XiaoS3Board : public ESP32Board { +public: + XiaoS3Board() { } + + const char* getManufacturerName() const override { + return "Xiao S3"; + } +}; diff --git a/variants/xiao_s3/platformio.ini b/variants/xiao_s3/platformio.ini new file mode 100644 index 00000000..58bcc60b --- /dev/null +++ b/variants/xiao_s3/platformio.ini @@ -0,0 +1,180 @@ +[Xiao_S3] +extends = esp32_base +board = seeed_xiao_esp32s3 +board_check = true +board_build.mcu = esp32s3 +build_flags = ${esp32_base.build_flags} + ${sensor_base.build_flags} + -I variants/xiao_s3 + -UENV_INCLUDE_GPS + -D SEEED_XIAO_S3 + -D PIN_VBAT_READ=1 ; D0 + -D P_LORA_DIO_1=2 ; D1 + -D P_LORA_NSS=5 ; D4 + -D P_LORA_RESET=3 ; D2 + -D P_LORA_BUSY=4 ; D3 + -D P_LORA_SCLK=7 ; D8 + -D P_LORA_MISO=8 ; D0 + -D P_LORA_MOSI=9 ; D10 + -D PIN_USER_BTN=-1 ; NC + -D PIN_STATUS_LED=21 ; Orange user led, LOW=On + -D PIN_BOARD_SDA=D6 ; D6=43 + -D PIN_BOARD_SCL=D7 ; D7=44 + -D SX126X_RXEN=6 ; D5 + -D SX126X_TXEN=RADIOLIB_NC + -D SX126X_DIO2_AS_RF_SWITCH=true + -D SX126X_DIO3_TCXO_VOLTAGE=1.8 + -D SX126X_CURRENT_LIMIT=140 + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D SX126X_RX_BOOSTED_GAIN=1 +build_src_filter = ${esp32_base.build_src_filter} + +<../variants/xiao_s3> + + +lib_deps = + ${esp32_base.lib_deps} + ${sensor_base.lib_deps} + +[env:Xiao_S3_repeater] +extends = Xiao_S3 +build_src_filter = ${Xiao_S3.build_src_filter} + +<../examples/simple_repeater/*.cpp> +build_flags = + ${Xiao_S3.build_flags} + -D ADVERT_NAME='"Xiao S3 Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Xiao_S3.lib_deps} + ${esp32_ota.lib_deps} + +[env:Xiao_S3_repeater_bridge_espnow] +extends = Xiao_S3 +build_src_filter = ${Xiao_S3.build_src_filter} + + + +<../examples/simple_repeater/*.cpp> +build_flags = + ${Xiao_S3.build_flags} + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 + -D WITH_ESPNOW_BRIDGE=1 +; -D BRIDGE_DEBUG=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Xiao_S3.lib_deps} + ${esp32_ota.lib_deps} + +[env:Xiao_S3_room_server] +extends = Xiao_S3 +build_src_filter = ${Xiao_S3.build_src_filter} + +<../examples/simple_room_server> +build_flags = + ${Xiao_S3.build_flags} + -D ADVERT_NAME='"Xiao S3 Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Xiao_S3.lib_deps} + ${esp32_ota.lib_deps} + +[env:Xiao_S3_companion_radio_ble] +extends = Xiao_S3 +build_flags = + ${Xiao_S3.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D DISPLAY_CLASS=SSD1306Display + -D OFFLINE_QUEUE_SIZE=256 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Xiao_S3.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Xiao_S3.lib_deps} + densaugeo/base64 @ ~1.4.0 + adafruit/Adafruit SSD1306 @ ^2.5.13 + +[env:Xiao_S3_companion_radio_ble_ps] +extends = Xiao_S3 +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${Xiao_S3.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D DISPLAY_CLASS=SSD1306Display + -D OFFLINE_QUEUE_SIZE=256 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Xiao_S3.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Xiao_S3.lib_deps} + densaugeo/base64 @ ~1.4.0 + adafruit/Adafruit SSD1306 @ ^2.5.13 + +[env:Xiao_S3_companion_radio_usb] +extends = Xiao_S3 +build_flags = + ${Xiao_S3.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D DISPLAY_CLASS=SSD1306Display + -D OFFLINE_QUEUE_SIZE=256 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Xiao_S3.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Xiao_S3.lib_deps} + densaugeo/base64 @ ~1.4.0 + adafruit/Adafruit SSD1306 @ ^2.5.13 + +[env:Xiao_S3_sensor] +extends = Xiao_S3 +build_src_filter = ${Xiao_S3.build_src_filter} + +<../examples/simple_sensor> +build_flags = + ${Xiao_S3.build_flags} + -D ADVERT_NAME='"Xiao S3 Sensor"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +lib_deps = + ${Xiao_S3.lib_deps} + ${esp32_ota.lib_deps} \ No newline at end of file diff --git a/variants/xiao_s3/target.cpp b/variants/xiao_s3/target.cpp new file mode 100644 index 00000000..014b2552 --- /dev/null +++ b/variants/xiao_s3/target.cpp @@ -0,0 +1,56 @@ +#include +#include "target.h" + +XiaoS3Board board; + +#if defined(P_LORA_SCLK) + static SPIClass spi; + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); +#else + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); +#endif + +WRAPPER_CLASS radio_driver(radio, board); + +ESP32RTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); +EnvironmentSensorManager sensors; + +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); +#endif + +bool radio_init() { + fallback_clock.begin(); + rtc_clock.begin(Wire); + pinMode(21, INPUT); + pinMode(48, OUTPUT); + + #if defined(P_LORA_SCLK) + spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); + return radio.std_init(&spi); +#else + return radio.std_init(); +#endif +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); +} + +void radio_set_tx_power(int8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} diff --git a/variants/xiao_s3/target.h b/variants/xiao_s3/target.h new file mode 100644 index 00000000..93b27862 --- /dev/null +++ b/variants/xiao_s3/target.h @@ -0,0 +1,30 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include +#ifdef DISPLAY_CLASS + #include + #include +#endif +#include "XiaoS3Board.h" + +extern XiaoS3Board board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern EnvironmentSensorManager sensors; + +#ifdef DISPLAY_CLASS + extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; +#endif + +bool radio_init(); +uint32_t radio_get_rng_seed(); +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); +void radio_set_tx_power(int8_t dbm); +mesh::LocalIdentity radio_new_identity(); diff --git a/variants/xiao_s3_wio/platformio.ini b/variants/xiao_s3_wio/platformio.ini index 13d40679..7fcebce2 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -173,6 +173,32 @@ lib_deps = densaugeo/base64 @ ~1.4.0 adafruit/Adafruit SSD1306 @ ^2.5.13 +[env:Xiao_S3_WIO_companion_radio_ble_ps] +extends = Xiao_S3_WIO +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${Xiao_S3_WIO.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D DISPLAY_CLASS=SSD1306Display + -D OFFLINE_QUEUE_SIZE=256 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Xiao_S3_WIO.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Xiao_S3_WIO.lib_deps} + densaugeo/base64 @ ~1.4.0 + adafruit/Adafruit SSD1306 @ ^2.5.13 + [env:Xiao_S3_WIO_companion_radio_serial] extends = Xiao_S3_WIO build_flags = From 0702065dd273ab5b703625425883932f61c689b9 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Fri, 24 Apr 2026 23:05:31 +0700 Subject: [PATCH 014/214] Delay sleep when BLE is active writing. --- examples/companion_radio/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 90903097..b4e8d386 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -266,7 +266,9 @@ void loop() { #if defined(NRF52_PLATFORM) board.sleep(0); // nrf ignores seconds param, sleeps whenever possible #else if defined(ESP32_PLATFORM) - vTaskDelay(pdMS_TO_TICKS(50)); // attempt to sleep + if (!serial_interface.isWriteBusy()) { // BLE is not busy + vTaskDelay(pdMS_TO_TICKS(50)); // attempt to sleep + } #endif } } From 68360157ecee382e6221d593724e473dd47cede5 Mon Sep 17 00:00:00 2001 From: OhYou-0 Date: Fri, 24 Apr 2026 10:16:19 -0700 Subject: [PATCH 015/214] Add LilyGo T-ETH Elite board support --- variants/lilygo_teth_elite/TETHEliteBoard.h | 10 +++ variants/lilygo_teth_elite/platformio.ini | 99 +++++++++++++++++++++ variants/lilygo_teth_elite/target.cpp | 43 +++++++++ variants/lilygo_teth_elite/target.h | 20 +++++ 4 files changed, 172 insertions(+) create mode 100644 variants/lilygo_teth_elite/TETHEliteBoard.h create mode 100644 variants/lilygo_teth_elite/platformio.ini create mode 100644 variants/lilygo_teth_elite/target.cpp create mode 100644 variants/lilygo_teth_elite/target.h diff --git a/variants/lilygo_teth_elite/TETHEliteBoard.h b/variants/lilygo_teth_elite/TETHEliteBoard.h new file mode 100644 index 00000000..15eb9533 --- /dev/null +++ b/variants/lilygo_teth_elite/TETHEliteBoard.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class TETHEliteBoard : public ESP32Board { +public: + const char* getManufacturerName() const override { + return "LilyGO T-ETH Elite"; + } +}; diff --git a/variants/lilygo_teth_elite/platformio.ini b/variants/lilygo_teth_elite/platformio.ini new file mode 100644 index 00000000..97728f8b --- /dev/null +++ b/variants/lilygo_teth_elite/platformio.ini @@ -0,0 +1,99 @@ +[LilyGo_TETH_Elite_sx1262] +extends = esp32_base +board = esp32s3box +board_build.partitions = default_16MB.csv +board_upload.flash_size = 16MB +build_flags = + ${esp32_base.build_flags} + -I variants/lilygo_teth_elite + -D BOARD_HAS_PSRAM + -D LILYGO_TETH_ELITE + -D LILYGO_T_ETH_ELITE_ESP32S3 + -D ARDUINO_USB_CDC_ON_BOOT=1 + -D P_LORA_DIO_1=8 + -D P_LORA_NSS=40 + -D P_LORA_RESET=46 + -D P_LORA_BUSY=16 + -D P_LORA_SCLK=10 + -D P_LORA_MISO=9 + -D P_LORA_MOSI=11 + -D P_LORA_TX_LED=38 + -D SX126X_DIO2_AS_RF_SWITCH=true + -D SX126X_DIO3_TCXO_VOLTAGE=1.8 + -D SX126X_CURRENT_LIMIT=140 + -D USE_SX1262 + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=8 + -D SX126X_RX_BOOSTED_GAIN=1 +build_src_filter = ${esp32_base.build_src_filter} + +<../variants/lilygo_teth_elite> +lib_deps = + ${esp32_base.lib_deps} + +[env:LilyGo_TETH_Elite_sx1262_repeater] +extends = LilyGo_TETH_Elite_sx1262 +build_flags = + ${LilyGo_TETH_Elite_sx1262.build_flags} + -D ADVERT_NAME='"T-ETH Elite Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TETH_Elite_sx1262.build_src_filter} + +<../examples/simple_repeater> +lib_deps = + ${LilyGo_TETH_Elite_sx1262.lib_deps} + ${esp32_ota.lib_deps} + +[env:LilyGo_TETH_Elite_sx1262_room_server] +extends = LilyGo_TETH_Elite_sx1262 +build_flags = + ${LilyGo_TETH_Elite_sx1262.build_flags} + -D ADVERT_NAME='"T-ETH Elite Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TETH_Elite_sx1262.build_src_filter} + +<../examples/simple_room_server> +lib_deps = + ${LilyGo_TETH_Elite_sx1262.lib_deps} + ${esp32_ota.lib_deps} + +[env:LilyGo_TETH_Elite_sx1262_companion_radio_usb] +extends = LilyGo_TETH_Elite_sx1262 +build_flags = + ${LilyGo_TETH_Elite_sx1262.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TETH_Elite_sx1262.build_src_filter} + +<../examples/companion_radio/*.cpp> +lib_deps = + ${LilyGo_TETH_Elite_sx1262.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:LilyGo_TETH_Elite_sx1262_companion_radio_ble] +extends = LilyGo_TETH_Elite_sx1262 +build_flags = + ${LilyGo_TETH_Elite_sx1262.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TETH_Elite_sx1262.build_src_filter} + + + +<../examples/companion_radio/*.cpp> +lib_deps = + ${LilyGo_TETH_Elite_sx1262.lib_deps} + densaugeo/base64 @ ~1.4.0 diff --git a/variants/lilygo_teth_elite/target.cpp b/variants/lilygo_teth_elite/target.cpp new file mode 100644 index 00000000..4dc377d6 --- /dev/null +++ b/variants/lilygo_teth_elite/target.cpp @@ -0,0 +1,43 @@ +#include +#include "target.h" + +TETHEliteBoard board; + +static SPIClass spi(HSPI); +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); +WRAPPER_CLASS radio_driver(radio, board); + +ESP32RTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); +SensorManager sensors; + +#ifndef LORA_CR + #define LORA_CR 5 +#endif + +bool radio_init() { + fallback_clock.begin(); + rtc_clock.begin(Wire); + + return radio.std_init(&spi); +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); +} + +void radio_set_tx_power(int8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); +} diff --git a/variants/lilygo_teth_elite/target.h b/variants/lilygo_teth_elite/target.h new file mode 100644 index 00000000..a842186c --- /dev/null +++ b/variants/lilygo_teth_elite/target.h @@ -0,0 +1,20 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include "TETHEliteBoard.h" + +extern TETHEliteBoard board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern SensorManager sensors; + +bool radio_init(); +uint32_t radio_get_rng_seed(); +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); +void radio_set_tx_power(int8_t dbm); +mesh::LocalIdentity radio_new_identity(); From 756268e2ee46c992e662bcb0c0318de25d266783 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 24 Apr 2026 14:41:07 -0700 Subject: [PATCH 016/214] Fix issue with packet prefixes getting added to the table. --- docs/cli_commands.md | 6 +- examples/simple_repeater/MyMesh.cpp | 93 ++++++++++++++++++++++------- src/helpers/SimpleMeshTables.h | 24 ++++---- 3 files changed, 91 insertions(+), 32 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index e798cf0e..b6c87a7f 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -119,19 +119,23 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Usage:** - `get recent.repeater` - `get recent.repeater all` +- `get recent.repeater all ` - `get recent.repeater first ` +- `get recent.repeater first ` - `get recent.repeater last ` +- `get recent.repeater last ` - `set recent.repeater ` **Parameters:** - `prefix_hex`: 1-3 bytes of next-hop prefix (hex) - `snr_db`: SNR in dB (supports decimals; stored at x4 precision) - `count`: number of entries to print +- `offset`: zero-based row offset into the selected order **Notes:** - `set` is rejected when the prefix already exists in neighbors. - `all` prints oldest to newest; `first` prints the oldest N; `last` prints the newest N. -- Remote CLI replies include rows too, but may truncate when the packet payload limit is reached. +- Over LoRa remote CLI, replies are packet-size limited; use `offset` to page through all rows. --- diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 220f9704..907ce2c8 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1372,7 +1372,11 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply auto* tables = (SimpleMeshTables*)getTables(); - if (is_set || (!is_get && *sub != 0 && strcmp(sub, "all") != 0 && strncmp(sub, "first ", 6) != 0 && strncmp(sub, "last ", 5) != 0)) { + bool is_all = (strcmp(sub, "all") == 0 || strncmp(sub, "all ", 4) == 0); + bool is_first = (strncmp(sub, "first ", 6) == 0); + bool is_last = (strncmp(sub, "last ", 5) == 0); + + if (is_set || (!is_get && *sub != 0 && !is_all && !is_first && !is_last)) { char* params = (char*) sub; char* arg_snr = strchr(params, ' '); if (arg_snr == NULL) { @@ -1409,62 +1413,111 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply mesh::Utils::toHex(hex, info->prefix, info->prefix_len); sprintf(reply, "> %s,%s", hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); } - } else if (strcmp(sub, "all") == 0 || strncmp(sub, "first ", 6) == 0 || strncmp(sub, "last ", 5) == 0) { + } else if (is_all || is_first || is_last) { int total = tables->getRecentRepeaterCount(); if (total <= 0) { strcpy(reply, "> none"); } else { bool newest_first = false; int limit = total; + int offset = 0; const char* mode = "all"; - if (strncmp(sub, "first ", 6) == 0 || strncmp(sub, "last ", 5) == 0) { - const char* nstr = sub + (sub[0] == 'f' ? 6 : 5); + + if (is_first || is_last) { + const char* nstr = sub + (is_first ? 6 : 5); while (*nstr == ' ') nstr++; if (*nstr == 0) { - strcpy(reply, "Err - usage: get recent.repeater first|last "); + strcpy(reply, "Err - usage: get recent.repeater first|last [offset]"); return; } + char* end_ptr = NULL; - long parsed = strtol(nstr, &end_ptr, 10); + long parsed_count = strtol(nstr, &end_ptr, 10); while (end_ptr != NULL && *end_ptr == ' ') end_ptr++; - if (end_ptr == NULL || *end_ptr != 0 || parsed <= 0) { + if (end_ptr == NULL || parsed_count <= 0) { strcpy(reply, "Err - count must be > 0"); return; } - limit = (int)parsed; - if (sub[0] == 'l') { + + if (*end_ptr != 0) { + char* end_ptr2 = NULL; + long parsed_offset = strtol(end_ptr, &end_ptr2, 10); + while (end_ptr2 != NULL && *end_ptr2 == ' ') end_ptr2++; + if (end_ptr2 == NULL || *end_ptr2 != 0 || parsed_offset < 0) { + strcpy(reply, "Err - offset must be >= 0"); + return; + } + offset = (int)parsed_offset; + } + + limit = (int)parsed_count; + if (is_last) { newest_first = true; mode = "last"; } else { mode = "first"; } + } else if (strncmp(sub, "all ", 4) == 0) { + const char* arg = sub + 4; + while (*arg == ' ') arg++; + if (*arg == 0) { + strcpy(reply, "Err - usage: get recent.repeater all "); + return; + } + + char* end_ptr = NULL; + long parsed_a = strtol(arg, &end_ptr, 10); + while (end_ptr != NULL && *end_ptr == ' ') end_ptr++; + if (end_ptr == NULL || parsed_a <= 0) { + strcpy(reply, "Err - count must be > 0"); + return; + } + + char* end_ptr2 = NULL; + long parsed_b = strtol(end_ptr, &end_ptr2, 10); + while (end_ptr2 != NULL && *end_ptr2 == ' ') end_ptr2++; + if (end_ptr2 == NULL || *end_ptr2 != 0 || parsed_b < 0) { + strcpy(reply, "Err - usage: get recent.repeater all "); + return; + } + limit = (int)parsed_a; + offset = (int)parsed_b; } - if (limit > total) { - limit = total; + + if (offset >= total) { + sprintf(reply, "> none (%s off=%d/%d)", mode, offset, total); + return; + } + + int available = total - offset; + if (limit > available) { + limit = available; } if (sender_timestamp == 0) { - Serial.printf("Recent repeater table (%s %d/%d):\n", mode, limit, total); + Serial.printf("Recent repeater table (%s %d/%d, off=%d):\n", mode, limit, total, offset); for (int i = 0; i < limit; i++) { - const auto* info = newest_first ? tables->getRecentRepeaterNewestByIdx(i) : tables->getRecentRepeaterOldestByIdx(i); + int idx = offset + i; + const auto* info = newest_first ? tables->getRecentRepeaterNewestByIdx(idx) : tables->getRecentRepeaterOldestByIdx(idx); if (info == NULL) { continue; } char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; mesh::Utils::toHex(hex, info->prefix, info->prefix_len); - Serial.printf("%02d: %s,%s\n", i + 1, hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); + Serial.printf("%02d: %s,%s\n", idx + 1, hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); } - sprintf(reply, "> showing %d/%d (%s)", limit, total, mode); + sprintf(reply, "> %s off=%d n=%d/%d", mode, offset, limit, total); } else { // Remote CLI replies are packet-bound, so include as many rows as fit. - int written = snprintf(reply, 160, "> showing %d/%d (%s)", limit, total, mode); + int written = snprintf(reply, 160, "> %s off=%d n=%d/%d", mode, offset, limit, total); bool truncated = false; if (written < 0) { reply[0] = 0; written = 0; } for (int i = 0; i < limit; i++) { - const auto* info = newest_first ? tables->getRecentRepeaterNewestByIdx(i) : tables->getRecentRepeaterOldestByIdx(i); + int idx = offset + i; + const auto* info = newest_first ? tables->getRecentRepeaterNewestByIdx(idx) : tables->getRecentRepeaterOldestByIdx(idx); if (info == NULL) { continue; } @@ -1474,7 +1527,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; mesh::Utils::toHex(hex, info->prefix, info->prefix_len); - int n = snprintf(reply + written, 160 - written, "\n%02d:%s,%s", i + 1, hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); + int n = snprintf(reply + written, 160 - written, "\n%02d:%s,%s", idx + 1, hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); if (n < 0 || n >= (160 - written)) { truncated = true; break; @@ -1482,12 +1535,12 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply written += n; } if (truncated && written < 156) { - snprintf(reply + written, 160 - written, "\n..."); + snprintf(reply + written, 160 - written, "\n... use offset"); } } } } else { - strcpy(reply, "Err - usage: get recent.repeater [all|first |last ]"); + strcpy(reply, "Err - usage: get recent.repeater [all|all |first [offset]|last [offset]]"); } } else if (memcmp(command, "discover.neighbors", 18) == 0) { const char* sub = command + 18; diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index 705869ad..539bd5b6 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -70,6 +70,19 @@ private: bool extractRecentRepeater(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const { // Learn repeater prefixes only from packet shapes that expose a trustworthy repeater ID. + // For flood traffic, the last path entry is the repeater we directly heard. + if (packet->isRouteFlood() && packet->getPathHashCount() > 0) { + prefix_len = packet->getPathHashSize(); + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + + const uint8_t* last_hop = &packet->path[(packet->getPathHashCount() - 1) * packet->getPathHashSize()]; + memcpy(prefix, last_hop, prefix_len); + return true; + } + + // If there is no flood path to inspect, fall back to payload-derived identities. if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->payload_len >= PUB_KEY_SIZE) { memcpy(prefix, packet->payload, MAX_ROUTE_HASH_BYTES); prefix_len = MAX_ROUTE_HASH_BYTES; @@ -86,17 +99,6 @@ private: return true; } - if (packet->isRouteFlood() && packet->getPathHashCount() > 0) { - prefix_len = packet->getPathHashSize(); - if (prefix_len > MAX_ROUTE_HASH_BYTES) { - prefix_len = MAX_ROUTE_HASH_BYTES; - } - - const uint8_t* last_hop = &packet->path[(packet->getPathHashCount() - 1) * packet->getPathHashSize()]; - memcpy(prefix, last_hop, prefix_len); - return true; - } - return false; } From 0243ec41e8e71828afecb175346848c91071113d Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 24 Apr 2026 15:19:48 -0700 Subject: [PATCH 017/214] Round up the SNR vs replacement to get a weighted average. --- src/helpers/SimpleMeshTables.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index 539bd5b6..effea219 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -68,6 +68,21 @@ private: return n > 0 && memcmp(a, b, n) == 0; } + int8_t avgSnrX4RoundUp(int8_t curr_snr_x4, int8_t new_snr_x4) const { + int16_t sum = (int16_t)curr_snr_x4 + (int16_t)new_snr_x4; + int16_t avg = sum / 2; // truncates toward zero + // "Round up" means ceil(), which only differs from truncation for positive odd sums. + if (sum > 0 && (sum & 1)) { + avg++; + } + if (avg > 127) { + avg = 127; + } else if (avg < -128) { + avg = -128; + } + return (int8_t)avg; + } + bool extractRecentRepeater(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const { // Learn repeater prefixes only from packet shapes that expose a trustworthy repeater ID. // For flood traffic, the last path entry is the repeater we directly heard. @@ -258,7 +273,7 @@ public: memcpy(existing.prefix, prefix, prefix_len); existing.prefix_len = prefix_len; } - existing.snr_x4 = snr_x4; + existing.snr_x4 = avgSnrX4RoundUp(existing.snr_x4, snr_x4); return true; } From 22f07b3e4871bfde1e8013b25155988694721a04 Mon Sep 17 00:00:00 2001 From: Quency-D Date: Sat, 25 Apr 2026 15:33:59 +0800 Subject: [PATCH 018/214] Revert "Merge branch 'meshcore-dev:main' into cli-lna-command" This reverts commit ca047fe0c05315ed77e64cfd72ee7a17b810a929, reversing changes made to ddedb3c7a776f3f075d0bde2123b1c6f31186b90. --- README.md | 2 +- docs/faq.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f8b9e5e0..ebad1f6f 100644 --- a/README.md +++ b/README.md @@ -117,4 +117,4 @@ There are a number of fairly major features in the pipeline, with no particular - Report bugs and request features on the [GitHub Issues](https://github.com/ripplebiz/MeshCore/issues) page. - Find additional guides and components on [my site](https://buymeacoffee.com/ripplebiz). -- Join [MeshCore Discord](https://meshcore.gg) to chat with the developers and get help from the community. +- Join [MeshCore Discord](https://discord.gg/BMwCtwHj5V) to chat with the developers and get help from the community. diff --git a/docs/faq.md b/docs/faq.md index 3edc0a69..9fe1534a 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -194,7 +194,7 @@ Recently, as of October 2025, many regions have moved to the "narrow" setting, a After extensive testing, many regions have switched or about to switch over to BW62.5 and SF7, 8, or 9. Narrower bandwidth setting and lower SF setting allow MeshCore's radio signals to fit between interference in the ISM band, provide for a lower noise floor, better SNR, and faster transmissions. -If you have consensus from your community in your region to update your region's preset recommendation, please post your update request on the [#meshcore-app](https://discord.com/channels/1343693475589263471/1391681655911088241) channel on the [MeshCore Discord server ](https://meshcore.gg) to let Liam Cottle know. +If you have consensus from your community in your region to update your region's preset recommendation, please post your update request on the [#meshcore-app](https://discord.com/channels/1343693475589263471/1391681655911088241) channel on the [MeshCore Discord server ](https://discord.gg/cYtQNYCCRK) to let Liam Cottle know. @@ -526,7 +526,7 @@ The third character is the capital letter 'O', not zero `0` - Firmware repo: https://github.com/meshcore-dev/MeshCore ### 5.8. Q: How can I support MeshCore? -**A:** Provide your honest feedback on GitHub and on [MeshCore Discord server](https://meshcore.gg). Spread the word of MeshCore to your friends and communities; help them get started with MeshCore. Support Scott's MeshCore development at . +**A:** Provide your honest feedback on GitHub and on [MeshCore Discord server](https://discord.gg/BMwCtwHj5V). Spread the word of MeshCore to your friends and communities; help them get started with MeshCore. Support Scott's MeshCore development at . Support Liam Cottle's smartphone client development by unlocking the server administration wait gate with in-app purchase From c42f6db0ebf53f821c4161e54a444ddaa4dd22b4 Mon Sep 17 00:00:00 2001 From: Quency-D Date: Sat, 25 Apr 2026 16:01:26 +0800 Subject: [PATCH 019/214] Revise according to the review comments. Co-authored-by: Copilot --- examples/companion_radio/MyMesh.cpp | 11 ++++++----- src/helpers/CommonCLI.cpp | 10 +++++----- variants/heltec_v4/HeltecV4Board.h | 1 - 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 91da9bcc..e98a7873 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -46,8 +46,7 @@ #define CMD_SET_CUSTOM_VAR 41 #define CMD_GET_ADVERT_PATH 42 #define CMD_GET_TUNING_PARAMS 43 -#define CMD_GET_RADIO_FEM_RXGAIN 44 -#define CMD_SET_RADIO_FEM_RXGAIN 45 + // NOTE: CMD range 46..49 parked, potentially for WiFi operations #define CMD_SEND_BINARY_REQ 50 #define CMD_FACTORY_RESET 51 @@ -63,6 +62,8 @@ #define CMD_SEND_CHANNEL_DATA 62 #define CMD_SET_DEFAULT_FLOOD_SCOPE 63 #define CMD_GET_DEFAULT_FLOOD_SCOPE 64 +#define CMD_GET_RADIO_FEM_RXGAIN 65 +#define CMD_SET_RADIO_FEM_RXGAIN 66 // Stats sub-types for CMD_GET_STATS #define STATS_TYPE_CORE 0 @@ -1808,9 +1809,9 @@ void MyMesh::handleCmdFrame(size_t len) { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); } else { out_frame[0] = RESP_CODE_OK; - uint32_t value = board.isLoRaFemLnaEnabled() ? 1 : 0; - memcpy(&out_frame[1], &value, 4); - _serial->writeFrame(out_frame, 5); + uint8_t value = board.isLoRaFemLnaEnabled() ? 1 : 0; + memcpy(&out_frame[1], &value, 1); + _serial->writeFrame(out_frame, 2); } } else if (cmd_frame[0] == CMD_SET_RADIO_FEM_RXGAIN && len >= 2) { uint8_t value = cmd_frame[1]; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index b9152f4c..d22e08a8 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -559,7 +559,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep #endif } else if (memcmp(config, "radio.fem.rxgain ", 17) == 0) { if (!_board->canControlLoRaFemLna()) { - strcpy(reply, "Error: unsupported by this board"); + strcpy(reply, "Error: unsupported"); } else if (memcmp(&config[17], "on", 2) == 0) { if (_board->setLoRaFemLnaEnabled(true)) { _prefs->radio_fem_rxgain = 1; @@ -750,7 +750,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } } else { _prefs->adc_multiplier = 0.0f; - strcpy(reply, "Error: unsupported by this board"); + strcpy(reply, "Error: unsupported"); }; } else { strcpy(reply, "unknown config: "); @@ -800,7 +800,7 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep #endif } else if (memcmp(config, "radio.fem.rxgain", 16) == 0) { if (!_board->canControlLoRaFemLna()) { - strcpy(reply, "Error: unsupported by this board"); + strcpy(reply, "Error: unsupported"); } else { sprintf(reply, "> %s", _board->isLoRaFemLnaEnabled() ? "on" : "off"); } @@ -885,12 +885,12 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep strcpy(reply, "> unknown"); } #else - strcpy(reply, "ERROR: unsupported"); + strcpy(reply, "Error: unsupported"); #endif } else if (memcmp(config, "adc.multiplier", 14) == 0) { float adc_mult = _board->getAdcMultiplier(); if (adc_mult == 0.0f) { - strcpy(reply, "Error: unsupported by this board"); + strcpy(reply, "Error: unsupported"); } else { sprintf(reply, "> %.3f", adc_mult); } diff --git a/variants/heltec_v4/HeltecV4Board.h b/variants/heltec_v4/HeltecV4Board.h index b2caaa35..fc37b9f6 100644 --- a/variants/heltec_v4/HeltecV4Board.h +++ b/variants/heltec_v4/HeltecV4Board.h @@ -25,7 +25,6 @@ public: void onAfterTransmit(void) override; void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); void powerOff() override; - const char* getManufacturerName() const override ; bool setLoRaFemLnaEnabled(bool enable) override; bool canControlLoRaFemLna() const override; bool isLoRaFemLnaEnabled() const override; From 9d26953398d75c1c57c5c818957b5384e86921e8 Mon Sep 17 00:00:00 2001 From: Quency-D <55523105+Quency-D@users.noreply.github.com> Date: Sat, 25 Apr 2026 16:10:57 +0800 Subject: [PATCH 020/214] Remove unnecessary blank line in MyMesh.cpp --- examples/companion_radio/MyMesh.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index e98a7873..1b664eea 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -46,7 +46,6 @@ #define CMD_SET_CUSTOM_VAR 41 #define CMD_GET_ADVERT_PATH 42 #define CMD_GET_TUNING_PARAMS 43 - // NOTE: CMD range 46..49 parked, potentially for WiFi operations #define CMD_SEND_BINARY_REQ 50 #define CMD_FACTORY_RESET 51 From 62b0d82682ec9cf51c0e8f08aa835a6c8bf6aefc Mon Sep 17 00:00:00 2001 From: Quency-D <55523105+Quency-D@users.noreply.github.com> Date: Sat, 25 Apr 2026 16:12:40 +0800 Subject: [PATCH 021/214] Restore WiFi operation command scope comments --- examples/companion_radio/MyMesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 1b664eea..2d1f6dcd 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -46,7 +46,7 @@ #define CMD_SET_CUSTOM_VAR 41 #define CMD_GET_ADVERT_PATH 42 #define CMD_GET_TUNING_PARAMS 43 -// NOTE: CMD range 46..49 parked, potentially for WiFi operations +// NOTE: CMD range 44..49 parked, potentially for WiFi operations #define CMD_SEND_BINARY_REQ 50 #define CMD_FACTORY_RESET 51 #define CMD_SEND_PATH_DISCOVERY_REQ 52 From 528bf3f61e8909c4b16601e9835afb2df8feb94a Mon Sep 17 00:00:00 2001 From: Liam Cottle Date: Sun, 26 Apr 2026 00:24:40 +1200 Subject: [PATCH 022/214] add FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..262a9ee4 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: meshcore-dev From a44137662eb92a142fb67894aa8b9dc521914b1a Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Sat, 25 Apr 2026 23:37:57 +0700 Subject: [PATCH 023/214] Added TimeTrim and Kept RTC 8M during sleep --- src/helpers/ESP32Board.h | 50 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index 6d527b66..ee874ff0 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -66,6 +66,50 @@ public: return P_LORA_DIO_1; // default for SX1262 } + // 27 mins drift in 28 days and 40 mins = 669 ppm + const int64_t MICROSECONDS_PER_SECOND_DRIFT = 669; + const int64_t DRIFT_THRESHOLD_US = 60000000LL; // 1 minute in microseconds + + void applyTimeTrim() { + static int64_t last_trim_us = 0; + static int64_t accumulated_drift_us = 0; + + struct timeval tv; + gettimeofday(&tv, NULL); + int64_t now_us = (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec; + + if (last_trim_us == 0) { + last_trim_us = now_us; + return; + } + + // Calculate elapsed time + int64_t elapsed_us = now_us - last_trim_us; + + // Add this interval's drift to accumulated drift + accumulated_drift_us += (elapsed_us * MICROSECONDS_PER_SECOND_DRIFT) / 1000000LL; + + // Only trim when accumulated drift exceeds threshold + if (accumulated_drift_us >= DRIFT_THRESHOLD_US) { + // Calculate full seconds to trim + uint32_t seconds_to_trim = (uint32_t)(accumulated_drift_us / 1000000LL); + + // Set back the trimmed time to RTC + tv.tv_sec -= (time_t)seconds_to_trim; + settimeofday(&tv, NULL); + + // Keep the fractional remainder (anything less than a full second) + accumulated_drift_us %= 1000000LL; + + // Save for next trim + gettimeofday(&tv, NULL); + last_trim_us = (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec; + } else { + // Skip trimming + last_trim_us = now_us; + } + } + void sleep(uint32_t secs) override { // Skip if not allow to sleep if (inhibit_sleep) { @@ -85,6 +129,9 @@ public: } #endif + // Keep RTC 8M during sleep + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC8M, ESP_PD_OPTION_ON); + // Set GPIO wakeup gpio_num_t wakeupPin = (gpio_num_t)getIRQGpio(); @@ -116,6 +163,9 @@ public: // Enable CPU interrupt servicing portEXIT_CRITICAL(&sleepMux); + + // Apply the software trim to correct for RC drift + applyTimeTrim(); } uint8_t getStartupReason() const override { return startup_reason; } From aa400da9459f228969ce9914950c46f7e8d99ef4 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Sat, 25 Apr 2026 23:38:38 +0700 Subject: [PATCH 024/214] Clear stale wakeup sources to avoid ghost wakeup --- variants/heltec_v3/HeltecV3Board.h | 4 ++++ variants/heltec_v4/HeltecV4Board.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/variants/heltec_v3/HeltecV3Board.h b/variants/heltec_v3/HeltecV3Board.h index ba22a7f2..5361d029 100644 --- a/variants/heltec_v3/HeltecV3Board.h +++ b/variants/heltec_v3/HeltecV3Board.h @@ -53,6 +53,10 @@ public: } void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { + // Clear stale wakeup sources to avoid ghost wakeup + // This is required when Power Management and automatic lightsleep are enabled + esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep diff --git a/variants/heltec_v4/HeltecV4Board.cpp b/variants/heltec_v4/HeltecV4Board.cpp index 4537276f..f9dafb3b 100644 --- a/variants/heltec_v4/HeltecV4Board.cpp +++ b/variants/heltec_v4/HeltecV4Board.cpp @@ -33,6 +33,10 @@ void HeltecV4Board::begin() { } void HeltecV4Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { + // Clear stale wakeup sources to avoid ghost wakeup + // This is required when Power Management and automatic lightsleep are enabled + esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep From 5a45d54afefcbdaf18037eca50a462dc472e8b00 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Sun, 26 Apr 2026 16:16:43 +0700 Subject: [PATCH 025/214] Added LilyGo_TBeam_1W_companion_radio_ble_ps --- variants/lilygo_tbeam_1w/platformio.ini | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/variants/lilygo_tbeam_1w/platformio.ini b/variants/lilygo_tbeam_1w/platformio.ini index 7c845307..60b3291b 100644 --- a/variants/lilygo_tbeam_1w/platformio.ini +++ b/variants/lilygo_tbeam_1w/platformio.ini @@ -146,6 +146,31 @@ lib_deps = ${LilyGo_TBeam_1W.lib_deps} densaugeo/base64 @ ~1.4.0 +; === LILYGO T-Beam 1W Companion Radio PS (BLE PS) === +[env:LilyGo_TBeam_1W_companion_radio_ble_ps] +extends = LilyGo_TBeam_1W +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${LilyGo_TBeam_1W.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D OFFLINE_QUEUE_SIZE=256 + -D PERSISTANT_GPS=1 + -D ENV_SKIP_GPS_DETECT=1 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TBeam_1W.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${LilyGo_TBeam_1W.lib_deps} + densaugeo/base64 @ ~1.4.0 + ; === LILYGO T-Beam 1W Companion Radio (WiFi) === [env:LilyGo_TBeam_1W_companion_radio_wifi] extends = LilyGo_TBeam_1W From 34db93150a4be32dd5780b1f59da8a2104a0bbb7 Mon Sep 17 00:00:00 2001 From: uncle lit <43320854+LitBomb@users.noreply.github.com> Date: Sun, 26 Apr 2026 18:35:02 -0700 Subject: [PATCH 026/214] Removed links to outdated resources and links Removed links to outdated resources and links --- docs/faq.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 3edc0a69..c5866fb5 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -111,7 +111,6 @@ Anyone is able to build anything they like on top of MeshCore without paying any - MeshCore Firmware on GitHub: [https://github.com/meshcore-dev/MeshCore](https://github.com/meshcore-dev/MeshCore) - MeshCore Companion Web App: [https://app.meshcore.nz](https://app.meshcore.nz) - MeshCore Map: [https://map.meshcore.io](https://map.meshcore.io) -- Andy Kirby's [MeshCore Intro Video](https://www.youtube.com/watch?v=t1qne8uJBAc) - Liam Cottle's [MeshCore Technical Presentation](https://www.youtube.com/watch?v=OwmkVkZQTf4) You need LoRa hardware devices to run MeshCore firmware as clients or server (repeater and room server). @@ -404,9 +403,6 @@ Another way to download map tiles is to use this Python script to get the tiles There is also a modified script that adds additional error handling and parallel downloads: -UK map tiles are available separately from Andy Kirby on his discord server: - - ### 4.8. Q: Where do the map tiles go? Once you have the tiles downloaded, copy the `\tiles` folder to the root of your T-Deck's SD card. @@ -563,10 +559,6 @@ pio run -e RAK_4631_Repeater ``` then you'll find `firmware.zip` in `.pio/build/RAK_4631_Repeater` -Andy also has a video on how to build using VS Code: -*How to build and flash Meshcore repeater firmware | Heltec V3* - *(Link referenced in the Discord post)* - ### 5.10. Q: Are there other MeshCore related open source projects? **A:** [Liam Cottle](https://liamcottle.net)'s MeshCore web client and MeshCore Javascript library are open source under MIT license. From b948369d71c6b83ff01f3207cd6437b1b2ba8003 Mon Sep 17 00:00:00 2001 From: Keith Tweed Date: Sun, 26 Apr 2026 19:51:33 -0600 Subject: [PATCH 027/214] Update script link in FAQ 4.7 --- docs/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.md b/docs/faq.md index 3edc0a69..18f7ce35 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -402,7 +402,7 @@ Another way to download map tiles is to use this Python script to get the tiles There is also a modified script that adds additional error handling and parallel downloads: - + UK map tiles are available separately from Andy Kirby on his discord server: From 181ddb2268ec3ccd1032fc0a388e54e0bc40659d Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Mon, 27 Apr 2026 10:18:55 +0700 Subject: [PATCH 028/214] Added Heltec_WSL3_companion_radio_ble_ps --- variants/heltec_v3/platformio.ini | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 6a62b24d..fe9cd926 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -346,6 +346,26 @@ lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_WSL3_companion_radio_ble_ps] +extends = Heltec_lora32_v3 +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${Heltec_lora32_v3.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + +<../examples/companion_radio/*.cpp> +lib_deps = + ${Heltec_lora32_v3.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:Heltec_WSL3_companion_radio_usb] extends = Heltec_lora32_v3 build_flags = From fa9f3bd9350762a0da47beb9b350dd154f375e83 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Mon, 27 Apr 2026 11:07:36 +0700 Subject: [PATCH 029/214] Added Heltec_Wireless_Tracker_companion_radio_ble_ps --- variants/heltec_tracker/platformio.ini | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/variants/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini index e0a8f5fa..2c915526 100644 --- a/variants/heltec_tracker/platformio.ini +++ b/variants/heltec_tracker/platformio.ini @@ -99,6 +99,33 @@ lib_deps = ${Heltec_tracker_base.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_Wireless_Tracker_companion_radio_ble_ps] +extends = Heltec_tracker_base +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${Heltec_tracker_base.build_flags} + -I src/helpers/ui + -I examples/companion_radio/ui-new + -D DISPLAY_ROTATION=1 + -D DISPLAY_CLASS=ST7735Display + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 ; HWT will use display for pin + -D OFFLINE_QUEUE_SIZE=256 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_tracker_base.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> + + +lib_deps = + ${Heltec_tracker_base.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:Heltec_Wireless_Tracker_repeater] extends = Heltec_tracker_base build_flags = From e608503aa02e9c0dfa3ed583b4154157f5f2d421 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Mon, 27 Apr 2026 12:15:46 +0700 Subject: [PATCH 030/214] Merged latest changes from "Add CLI control to LoRa's fem LNA" https://github.com/meshcore-dev/MeshCore/pull/2140 --- docs/cli_commands.md | 14 + examples/companion_radio/DataStore.cpp | 2 + examples/companion_radio/MyMesh.cpp | 12 +- examples/companion_radio/NodePrefs.h | 2 +- examples/simple_repeater/UITask.cpp | 3 +- examples/simple_room_server/UITask.cpp | 3 +- examples/simple_sensor/SensorMesh.cpp | 2 + examples/simple_sensor/UITask.cpp | 3 +- src/helpers/CommonCLI.cpp | 540 ++++++++++---------- variants/heltec_t096/LoRaFEMControl.h | 5 +- variants/heltec_t096/T096Board.cpp | 20 +- variants/heltec_t096/T096Board.h | 3 + variants/heltec_tracker_v2/LoRaFEMControl.h | 2 +- variants/heltec_v4/HeltecV4Board.cpp | 2 +- variants/heltec_v4/HeltecV4Board.h | 4 +- 15 files changed, 333 insertions(+), 284 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index fb698228..c17910a5 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -277,6 +277,20 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### View or change the LoRa FEM receive-path gain state on supported boards +**Usage:** +- `get radio.fem.rxgain` +- `set radio.fem.rxgain ` + +**Parameters:** +- `state`: `on`|`off` + +**Notes:** +- This controls the external LoRa FEM receive-path LNA where the board supports it. +- This is separate from `radio.rxgain`, which controls the radio chip receive gain mode. + +--- + ### System #### View or change this node's name diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index c7988bb3..362ba68a 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -233,6 +233,7 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no file.read((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89 file.read((uint8_t *)_prefs.default_scope_name, sizeof(_prefs.default_scope_name)); // 90 file.read((uint8_t *)_prefs.default_scope_key, sizeof(_prefs.default_scope_key)); // 121 + file.read((uint8_t *)&_prefs.radio_fem_rxgain, sizeof(_prefs.radio_fem_rxgain)); // 122 file.close(); } @@ -273,6 +274,7 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ file.write((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89 file.write((uint8_t *)_prefs.default_scope_name, sizeof(_prefs.default_scope_name)); // 90 file.write((uint8_t *)_prefs.default_scope_key, sizeof(_prefs.default_scope_key)); // 121 + file.write((uint8_t *)&_prefs.radio_fem_rxgain, sizeof(_prefs.radio_fem_rxgain)); // 122 file.close(); } diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 67ff0a81..8a6f63c5 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -46,9 +46,7 @@ #define CMD_SET_CUSTOM_VAR 41 #define CMD_GET_ADVERT_PATH 42 #define CMD_GET_TUNING_PARAMS 43 -#define CMD_GET_RADIO_FEM_RXGAIN 44 -#define CMD_SET_RADIO_FEM_RXGAIN 45 -// NOTE: CMD range 46..49 parked, potentially for WiFi operations +// NOTE: CMD range 44..49 parked, potentially for WiFi operations #define CMD_SEND_BINARY_REQ 50 #define CMD_FACTORY_RESET 51 #define CMD_SEND_PATH_DISCOVERY_REQ 52 @@ -63,6 +61,8 @@ #define CMD_SEND_CHANNEL_DATA 62 #define CMD_SET_DEFAULT_FLOOD_SCOPE 63 #define CMD_GET_DEFAULT_FLOOD_SCOPE 64 +#define CMD_GET_RADIO_FEM_RXGAIN 65 +#define CMD_SET_RADIO_FEM_RXGAIN 66 // Stats sub-types for CMD_GET_STATS #define STATS_TYPE_CORE 0 @@ -1808,9 +1808,9 @@ void MyMesh::handleCmdFrame(size_t len) { writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); } else { out_frame[0] = RESP_CODE_OK; - uint32_t value = board.isLoRaFemLnaEnabled() ? 1 : 0; - memcpy(&out_frame[1], &value, 4); - _serial->writeFrame(out_frame, 5); + uint8_t value = board.isLoRaFemLnaEnabled() ? 1 : 0; + memcpy(&out_frame[1], &value, 1); + _serial->writeFrame(out_frame, 2); } } else if (cmd_frame[0] == CMD_SET_RADIO_FEM_RXGAIN && len >= 2) { uint8_t value = cmd_frame[1]; diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 6598a69c..ecb117bd 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -35,4 +35,4 @@ struct NodePrefs { // persisted to file uint8_t autoadd_max_hops; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64) char default_scope_name[31]; uint8_t default_scope_key[16]; -}; \ No newline at end of file +}; diff --git a/examples/simple_repeater/UITask.cpp b/examples/simple_repeater/UITask.cpp index acb46325..ac6958ba 100644 --- a/examples/simple_repeater/UITask.cpp +++ b/examples/simple_repeater/UITask.cpp @@ -41,7 +41,8 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi } // v1.2.3 (1 Jan 2025) - sprintf(_version_info, "%s (%s)", version, build_date); + snprintf(_version_info, sizeof(_version_info), "%s (%s)", version, build_date); + free(version); } void UITask::renderCurrScreen() { diff --git a/examples/simple_room_server/UITask.cpp b/examples/simple_room_server/UITask.cpp index 42bc14d4..6445baf6 100644 --- a/examples/simple_room_server/UITask.cpp +++ b/examples/simple_room_server/UITask.cpp @@ -41,7 +41,8 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi } // v1.2.3 (1 Jan 2025) - sprintf(_version_info, "%s (%s)", version, build_date); + snprintf(_version_info, sizeof(_version_info), "%s (%s)", version, build_date); + free(version); } void UITask::renderCurrScreen() { diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index b8fe1e57..05b4a9dd 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -731,6 +731,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise _prefs.gps_enabled = 0; _prefs.gps_interval = 0; _prefs.advert_loc_policy = ADVERT_LOC_PREFS; + _prefs.radio_fem_rxgain = 1; memset(default_scope.key, 0, sizeof(default_scope.key)); } @@ -766,6 +767,7 @@ void SensorMesh::begin(FILESYSTEM* fs) { radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); radio_set_tx_power(_prefs.tx_power_dbm); + board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); updateAdvertTimer(); updateFloodAdvertTimer(); diff --git a/examples/simple_sensor/UITask.cpp b/examples/simple_sensor/UITask.cpp index 0e78fee0..698b9fd2 100644 --- a/examples/simple_sensor/UITask.cpp +++ b/examples/simple_sensor/UITask.cpp @@ -41,7 +41,8 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi } // v1.2.3 (1 Jan 2025) - sprintf(_version_info, "%s (%s)", version, build_date); + snprintf(_version_info, sizeof(_version_info), "%s (%s)", version, build_date); + free(version); } void UITask::renderCurrScreen() { diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 1aa4265c..1322a36c 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -90,7 +90,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290 - // next: 291 + file.read((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 291 + // next: 292 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -182,7 +183,6 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290 - // next: 291 file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 291 // next: 292 @@ -522,279 +522,279 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "OK - %d.%d%%", a_int, a_frac); } } else if (memcmp(config, "af ", 3) == 0) { - _prefs->airtime_factor = atof(&config[3]); - savePrefs(); - strcpy(reply, "OK"); - } else if (memcmp(config, "int.thresh ", 11) == 0) { - _prefs->interference_threshold = atoi(&config[11]); - savePrefs(); - strcpy(reply, "OK"); - } else if (memcmp(config, "agc.reset.interval ", 19) == 0) { - _prefs->agc_reset_interval = atoi(&config[19]) / 4; - savePrefs(); - sprintf(reply, "OK - interval rounded to %d", ((uint32_t) _prefs->agc_reset_interval) * 4); - } else if (memcmp(config, "multi.acks ", 11) == 0) { - _prefs->multi_acks = atoi(&config[11]); - savePrefs(); - strcpy(reply, "OK"); - } else if (memcmp(config, "allow.read.only ", 16) == 0) { - _prefs->allow_read_only = memcmp(&config[16], "on", 2) == 0; - savePrefs(); - strcpy(reply, "OK"); - } else if (memcmp(config, "flood.advert.interval ", 22) == 0) { - int hours = _atoi(&config[22]); - if ((hours > 0 && hours < 3) || (hours > 168)) { - strcpy(reply, "Error: interval range is 3-168 hours"); - } else { - _prefs->flood_advert_interval = (uint8_t)(hours); - _callbacks->updateFloodAdvertTimer(); - savePrefs(); - strcpy(reply, "OK"); - } - } else if (memcmp(config, "advert.interval ", 16) == 0) { - int mins = _atoi(&config[16]); - if ((mins > 0 && mins < MIN_LOCAL_ADVERT_INTERVAL) || (mins > 240)) { - sprintf(reply, "Error: interval range is %d-240 minutes", MIN_LOCAL_ADVERT_INTERVAL); - } else { - _prefs->advert_interval = (uint8_t)(mins / 2); - _callbacks->updateAdvertTimer(); - savePrefs(); - strcpy(reply, "OK"); - } - } else if (memcmp(config, "guest.password ", 15) == 0) { - StrHelper::strncpy(_prefs->guest_password, &config[15], sizeof(_prefs->guest_password)); - savePrefs(); - strcpy(reply, "OK"); - } else if (memcmp(config, "prv.key ", 8) == 0) { - uint8_t prv_key[PRV_KEY_SIZE]; - bool success = mesh::Utils::fromHex(prv_key, PRV_KEY_SIZE, &config[8]); - // only allow rekey if key is valid - if (success && mesh::LocalIdentity::validatePrivateKey(prv_key)) { - mesh::LocalIdentity new_id; - new_id.readFrom(prv_key, PRV_KEY_SIZE); - _callbacks->saveIdentity(new_id); - strcpy(reply, "OK, reboot to apply! New pubkey: "); - mesh::Utils::toHex(&reply[33], new_id.pub_key, PUB_KEY_SIZE); - } else { - strcpy(reply, "Error, bad key"); - } - } else if (memcmp(config, "name ", 5) == 0) { - if (isValidName(&config[5])) { - StrHelper::strncpy(_prefs->node_name, &config[5], sizeof(_prefs->node_name)); - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error, bad chars"); - } - } else if (memcmp(config, "repeat ", 7) == 0) { - _prefs->disable_fwd = memcmp(&config[7], "off", 3) == 0; - savePrefs(); - strcpy(reply, _prefs->disable_fwd ? "OK - repeat is now OFF" : "OK - repeat is now ON"); + _prefs->airtime_factor = atof(&config[3]); + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "int.thresh ", 11) == 0) { + _prefs->interference_threshold = atoi(&config[11]); + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "agc.reset.interval ", 19) == 0) { + _prefs->agc_reset_interval = atoi(&config[19]) / 4; + savePrefs(); + sprintf(reply, "OK - interval rounded to %d", ((uint32_t) _prefs->agc_reset_interval) * 4); + } else if (memcmp(config, "multi.acks ", 11) == 0) { + _prefs->multi_acks = atoi(&config[11]); + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "allow.read.only ", 16) == 0) { + _prefs->allow_read_only = memcmp(&config[16], "on", 2) == 0; + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "flood.advert.interval ", 22) == 0) { + int hours = _atoi(&config[22]); + if ((hours > 0 && hours < 3) || (hours > 168)) { + strcpy(reply, "Error: interval range is 3-168 hours"); + } else { + _prefs->flood_advert_interval = (uint8_t)(hours); + _callbacks->updateFloodAdvertTimer(); + savePrefs(); + strcpy(reply, "OK"); + } + } else if (memcmp(config, "advert.interval ", 16) == 0) { + int mins = _atoi(&config[16]); + if ((mins > 0 && mins < MIN_LOCAL_ADVERT_INTERVAL) || (mins > 240)) { + sprintf(reply, "Error: interval range is %d-240 minutes", MIN_LOCAL_ADVERT_INTERVAL); + } else { + _prefs->advert_interval = (uint8_t)(mins / 2); + _callbacks->updateAdvertTimer(); + savePrefs(); + strcpy(reply, "OK"); + } + } else if (memcmp(config, "guest.password ", 15) == 0) { + StrHelper::strncpy(_prefs->guest_password, &config[15], sizeof(_prefs->guest_password)); + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "prv.key ", 8) == 0) { + uint8_t prv_key[PRV_KEY_SIZE]; + bool success = mesh::Utils::fromHex(prv_key, PRV_KEY_SIZE, &config[8]); + // only allow rekey if key is valid + if (success && mesh::LocalIdentity::validatePrivateKey(prv_key)) { + mesh::LocalIdentity new_id; + new_id.readFrom(prv_key, PRV_KEY_SIZE); + _callbacks->saveIdentity(new_id); + strcpy(reply, "OK, reboot to apply! New pubkey: "); + mesh::Utils::toHex(&reply[33], new_id.pub_key, PUB_KEY_SIZE); + } else { + strcpy(reply, "Error, bad key"); + } + } else if (memcmp(config, "name ", 5) == 0) { + if (isValidName(&config[5])) { + StrHelper::strncpy(_prefs->node_name, &config[5], sizeof(_prefs->node_name)); + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, bad chars"); + } + } else if (memcmp(config, "repeat ", 7) == 0) { + _prefs->disable_fwd = memcmp(&config[7], "off", 3) == 0; + savePrefs(); + strcpy(reply, _prefs->disable_fwd ? "OK - repeat is now OFF" : "OK - repeat is now ON"); #if defined(USE_SX1262) || defined(USE_SX1268) } else if (memcmp(config, "radio.rxgain ", 13) == 0) { - _prefs->rx_boosted_gain = memcmp(&config[13], "on", 2) == 0; - strcpy(reply, "OK"); - savePrefs(); - _callbacks->setRxBoostedGain(_prefs->rx_boosted_gain); + _prefs->rx_boosted_gain = memcmp(&config[13], "on", 2) == 0; + strcpy(reply, "OK"); + savePrefs(); + _callbacks->setRxBoostedGain(_prefs->rx_boosted_gain); #endif - } else if (memcmp(config, "radio.fem.rxgain ", 17) == 0) { - if (!_board->canControlLoRaFemLna()) { - strcpy(reply, "Error: unsupported by this board"); - } else if (memcmp(&config[17], "on", 2) == 0) { - if (_board->setLoRaFemLnaEnabled(true)) { - _prefs->radio_fem_rxgain = 1; - savePrefs(); - strcpy(reply, "OK - LoRa FEM RX gain on"); - } else { - strcpy(reply, "Error: failed to apply LoRa FEM RX gain"); - } - } else if (memcmp(&config[17], "off", 3) == 0) { - if (_board->setLoRaFemLnaEnabled(false)) { - _prefs->radio_fem_rxgain = 0; - savePrefs(); - strcpy(reply, "OK - LoRa FEM RX gain off"); - } else { - strcpy(reply, "Error: failed to apply LoRa FEM RX gain"); - } - } else { - strcpy(reply, "Error: state must be on or off"); - } - } else if (memcmp(config, "radio ", 6) == 0) { - strcpy(tmp, &config[6]); - const char *parts[4]; - int num = mesh::Utils::parseTextParts(tmp, parts, 4); - float freq = num > 0 ? strtof(parts[0], nullptr) : 0.0f; - float bw = num > 1 ? strtof(parts[1], nullptr) : 0.0f; - uint8_t sf = num > 2 ? atoi(parts[2]) : 0; - uint8_t cr = num > 3 ? atoi(parts[3]) : 0; + } else if (memcmp(config, "radio.fem.rxgain ", 17) == 0) { + if (!_board->canControlLoRaFemLna()) { + strcpy(reply, "Error: unsupported"); + } else if (memcmp(&config[17], "on", 2) == 0) { + if (_board->setLoRaFemLnaEnabled(true)) { + _prefs->radio_fem_rxgain = 1; + savePrefs(); + strcpy(reply, "OK - LoRa FEM RX gain on"); + } else { + strcpy(reply, "Error: failed to apply LoRa FEM RX gain"); + } + } else if (memcmp(&config[17], "off", 3) == 0) { + if (_board->setLoRaFemLnaEnabled(false)) { + _prefs->radio_fem_rxgain = 0; + savePrefs(); + strcpy(reply, "OK - LoRa FEM RX gain off"); + } else { + strcpy(reply, "Error: failed to apply LoRa FEM RX gain"); + } + } else { + strcpy(reply, "Error: state must be on or off"); + } + } else if (memcmp(config, "radio ", 6) == 0) { + strcpy(tmp, &config[6]); + const char *parts[4]; + int num = mesh::Utils::parseTextParts(tmp, parts, 4); + float freq = num > 0 ? strtof(parts[0], nullptr) : 0.0f; + float bw = num > 1 ? strtof(parts[1], nullptr) : 0.0f; + uint8_t sf = num > 2 ? atoi(parts[2]) : 0; + uint8_t cr = num > 3 ? atoi(parts[3]) : 0; if (freq >= 150.0f && freq <= 2500.0f && sf >= 5 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7.0f && bw <= 500.0f) { - _prefs->sf = sf; - _prefs->cr = cr; - _prefs->freq = freq; - _prefs->bw = bw; - _callbacks->savePrefs(); - strcpy(reply, "OK - reboot to apply"); - } else { - strcpy(reply, "Error, invalid radio params"); - } - } else if (memcmp(config, "lat ", 4) == 0) { - _prefs->node_lat = atof(&config[4]); - savePrefs(); - strcpy(reply, "OK"); - } else if (memcmp(config, "lon ", 4) == 0) { - _prefs->node_lon = atof(&config[4]); - savePrefs(); - strcpy(reply, "OK"); - } else if (memcmp(config, "rxdelay ", 8) == 0) { - float db = atof(&config[8]); - if (db >= 0) { - _prefs->rx_delay_base = db; - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error, cannot be negative"); - } - } else if (memcmp(config, "txdelay ", 8) == 0) { - float f = atof(&config[8]); - if (f >= 0) { - _prefs->tx_delay_factor = f; - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error, cannot be negative"); - } - } else if (memcmp(config, "flood.max ", 10) == 0) { - uint8_t m = atoi(&config[10]); - if (m <= 64) { - _prefs->flood_max = m; - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error, max 64"); - } - } else if (memcmp(config, "direct.txdelay ", 15) == 0) { - float f = atof(&config[15]); - if (f >= 0) { - _prefs->direct_tx_delay_factor = f; - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error, cannot be negative"); - } - } else if (memcmp(config, "owner.info ", 11) == 0) { - config += 11; - char *dp = _prefs->owner_info; - while (*config && dp - _prefs->owner_info < sizeof(_prefs->owner_info)-1) { - *dp++ = (*config == '|') ? '\n' : *config; // translate '|' to newline chars - config++; - } - *dp = 0; - savePrefs(); - strcpy(reply, "OK"); - } else if (memcmp(config, "path.hash.mode ", 15) == 0) { - config += 15; - uint8_t mode = atoi(config); - if (mode < 3) { - _prefs->path_hash_mode = mode; - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error, must be 0,1, or 2"); - } - } else if (memcmp(config, "loop.detect ", 12) == 0) { - config += 12; - uint8_t mode; - if (memcmp(config, "off", 3) == 0) { - mode = LOOP_DETECT_OFF; - } else if (memcmp(config, "minimal", 7) == 0) { - mode = LOOP_DETECT_MINIMAL; - } else if (memcmp(config, "moderate", 8) == 0) { - mode = LOOP_DETECT_MODERATE; - } else if (memcmp(config, "strict", 6) == 0) { - mode = LOOP_DETECT_STRICT; - } else { - mode = 0xFF; - strcpy(reply, "Error, must be: off, minimal, moderate, or strict"); - } - if (mode != 0xFF) { - _prefs->loop_detect = mode; - savePrefs(); - strcpy(reply, "OK"); - } - } else if (memcmp(config, "tx ", 3) == 0) { - _prefs->tx_power_dbm = atoi(&config[3]); - savePrefs(); - _callbacks->setTxPower(_prefs->tx_power_dbm); - strcpy(reply, "OK"); - } else if (sender_timestamp == 0 && memcmp(config, "freq ", 5) == 0) { - _prefs->freq = atof(&config[5]); - savePrefs(); - strcpy(reply, "OK - reboot to apply"); + _prefs->sf = sf; + _prefs->cr = cr; + _prefs->freq = freq; + _prefs->bw = bw; + _callbacks->savePrefs(); + strcpy(reply, "OK - reboot to apply"); + } else { + strcpy(reply, "Error, invalid radio params"); + } + } else if (memcmp(config, "lat ", 4) == 0) { + _prefs->node_lat = atof(&config[4]); + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "lon ", 4) == 0) { + _prefs->node_lon = atof(&config[4]); + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "rxdelay ", 8) == 0) { + float db = atof(&config[8]); + if (db >= 0) { + _prefs->rx_delay_base = db; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, cannot be negative"); + } + } else if (memcmp(config, "txdelay ", 8) == 0) { + float f = atof(&config[8]); + if (f >= 0) { + _prefs->tx_delay_factor = f; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, cannot be negative"); + } + } else if (memcmp(config, "flood.max ", 10) == 0) { + uint8_t m = atoi(&config[10]); + if (m <= 64) { + _prefs->flood_max = m; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, max 64"); + } + } else if (memcmp(config, "direct.txdelay ", 15) == 0) { + float f = atof(&config[15]); + if (f >= 0) { + _prefs->direct_tx_delay_factor = f; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, cannot be negative"); + } + } else if (memcmp(config, "owner.info ", 11) == 0) { + config += 11; + char *dp = _prefs->owner_info; + while (*config && dp - _prefs->owner_info < sizeof(_prefs->owner_info)-1) { + *dp++ = (*config == '|') ? '\n' : *config; // translate '|' to newline chars + config++; + } + *dp = 0; + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "path.hash.mode ", 15) == 0) { + config += 15; + uint8_t mode = atoi(config); + if (mode < 3) { + _prefs->path_hash_mode = mode; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be 0,1, or 2"); + } + } else if (memcmp(config, "loop.detect ", 12) == 0) { + config += 12; + uint8_t mode; + if (memcmp(config, "off", 3) == 0) { + mode = LOOP_DETECT_OFF; + } else if (memcmp(config, "minimal", 7) == 0) { + mode = LOOP_DETECT_MINIMAL; + } else if (memcmp(config, "moderate", 8) == 0) { + mode = LOOP_DETECT_MODERATE; + } else if (memcmp(config, "strict", 6) == 0) { + mode = LOOP_DETECT_STRICT; + } else { + mode = 0xFF; + strcpy(reply, "Error, must be: off, minimal, moderate, or strict"); + } + if (mode != 0xFF) { + _prefs->loop_detect = mode; + savePrefs(); + strcpy(reply, "OK"); + } + } else if (memcmp(config, "tx ", 3) == 0) { + _prefs->tx_power_dbm = atoi(&config[3]); + savePrefs(); + _callbacks->setTxPower(_prefs->tx_power_dbm); + strcpy(reply, "OK"); + } else if (sender_timestamp == 0 && memcmp(config, "freq ", 5) == 0) { + _prefs->freq = atof(&config[5]); + savePrefs(); + strcpy(reply, "OK - reboot to apply"); #ifdef WITH_BRIDGE - } else if (memcmp(config, "bridge.enabled ", 15) == 0) { - _prefs->bridge_enabled = memcmp(&config[15], "on", 2) == 0; - _callbacks->setBridgeState(_prefs->bridge_enabled); - savePrefs(); - strcpy(reply, "OK"); - } else if (memcmp(config, "bridge.delay ", 13) == 0) { - int delay = _atoi(&config[13]); - if (delay >= 0 && delay <= 10000) { - _prefs->bridge_delay = (uint16_t)delay; - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error: delay must be between 0-10000 ms"); - } - } else if (memcmp(config, "bridge.source ", 14) == 0) { - _prefs->bridge_pkt_src = memcmp(&config[14], "rx", 2) == 0; - savePrefs(); - strcpy(reply, "OK"); + } else if (memcmp(config, "bridge.enabled ", 15) == 0) { + _prefs->bridge_enabled = memcmp(&config[15], "on", 2) == 0; + _callbacks->setBridgeState(_prefs->bridge_enabled); + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "bridge.delay ", 13) == 0) { + int delay = _atoi(&config[13]); + if (delay >= 0 && delay <= 10000) { + _prefs->bridge_delay = (uint16_t)delay; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error: delay must be between 0-10000 ms"); + } + } else if (memcmp(config, "bridge.source ", 14) == 0) { + _prefs->bridge_pkt_src = memcmp(&config[14], "rx", 2) == 0; + savePrefs(); + strcpy(reply, "OK"); #endif #ifdef WITH_RS232_BRIDGE - } else if (memcmp(config, "bridge.baud ", 12) == 0) { - uint32_t baud = atoi(&config[12]); - if (baud >= 9600 && baud <= BRIDGE_MAX_BAUD) { - _prefs->bridge_baud = (uint32_t)baud; - _callbacks->restartBridge(); - savePrefs(); - strcpy(reply, "OK"); - } else { - sprintf(reply, "Error: baud rate must be between 9600-%d",BRIDGE_MAX_BAUD); - } + } else if (memcmp(config, "bridge.baud ", 12) == 0) { + uint32_t baud = atoi(&config[12]); + if (baud >= 9600 && baud <= BRIDGE_MAX_BAUD) { + _prefs->bridge_baud = (uint32_t)baud; + _callbacks->restartBridge(); + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error: baud rate must be between 9600-%d",BRIDGE_MAX_BAUD); + } #endif #ifdef WITH_ESPNOW_BRIDGE - } else if (memcmp(config, "bridge.channel ", 15) == 0) { - int ch = atoi(&config[15]); - if (ch > 0 && ch < 15) { - _prefs->bridge_channel = (uint8_t)ch; - _callbacks->restartBridge(); - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error: channel must be between 1-14"); - } - } else if (memcmp(config, "bridge.secret ", 14) == 0) { - StrHelper::strncpy(_prefs->bridge_secret, &config[14], sizeof(_prefs->bridge_secret)); - _callbacks->restartBridge(); - savePrefs(); - strcpy(reply, "OK"); + } else if (memcmp(config, "bridge.channel ", 15) == 0) { + int ch = atoi(&config[15]); + if (ch > 0 && ch < 15) { + _prefs->bridge_channel = (uint8_t)ch; + _callbacks->restartBridge(); + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error: channel must be between 1-14"); + } + } else if (memcmp(config, "bridge.secret ", 14) == 0) { + StrHelper::strncpy(_prefs->bridge_secret, &config[14], sizeof(_prefs->bridge_secret)); + _callbacks->restartBridge(); + savePrefs(); + strcpy(reply, "OK"); #endif - } else if (memcmp(config, "adc.multiplier ", 15) == 0) { - _prefs->adc_multiplier = atof(&config[15]); - if (_board->setAdcMultiplier(_prefs->adc_multiplier)) { - savePrefs(); - if (_prefs->adc_multiplier == 0.0f) { - strcpy(reply, "OK - using default board multiplier"); - } else { - sprintf(reply, "OK - multiplier set to %.3f", _prefs->adc_multiplier); - } - } else { - _prefs->adc_multiplier = 0.0f; - strcpy(reply, "Error: unsupported by this board"); - }; + } else if (memcmp(config, "adc.multiplier ", 15) == 0) { + _prefs->adc_multiplier = atof(&config[15]); + if (_board->setAdcMultiplier(_prefs->adc_multiplier)) { + savePrefs(); + if (_prefs->adc_multiplier == 0.0f) { + strcpy(reply, "OK - using default board multiplier"); } else { - sprintf(reply, "unknown config: %s", config); + sprintf(reply, "OK - multiplier set to %.3f", _prefs->adc_multiplier); } + } else { + _prefs->adc_multiplier = 0.0f; + strcpy(reply, "Error: unsupported"); + }; + } else { + strcpy(reply, "unknown config: "); + } } void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* reply) { @@ -837,6 +837,12 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep } else if (memcmp(config, "radio.rxgain", 12) == 0) { sprintf(reply, "> %s", _prefs->rx_boosted_gain ? "on" : "off"); #endif + } else if (memcmp(config, "radio.fem.rxgain", 16) == 0) { + if (!_board->canControlLoRaFemLna()) { + strcpy(reply, "Error: unsupported"); + } else { + sprintf(reply, "> %s", _board->isLoRaFemLnaEnabled() ? "on" : "off"); + } } else if (memcmp(config, "radio", 5) == 0) { char freq[16], bw[16]; strcpy(freq, StrHelper::ftoa(_prefs->freq)); @@ -868,9 +874,9 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep strcpy(reply, "> minimal"); } else if (_prefs->loop_detect == LOOP_DETECT_MODERATE) { strcpy(reply, "> moderate"); - } else { + } else { strcpy(reply, "> strict"); - } + } } else if (memcmp(config, "tx", 2) == 0 && (config[2] == 0 || config[2] == ' ')) { sprintf(reply, "> %d", (int32_t) _prefs->tx_power_dbm); } else if (memcmp(config, "freq", 4) == 0) { @@ -917,12 +923,12 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep strcpy(reply, "> unknown"); } #else - strcpy(reply, "ERROR: unsupported"); + strcpy(reply, "Error: unsupported"); #endif } else if (memcmp(config, "adc.multiplier", 14) == 0) { float adc_mult = _board->getAdcMultiplier(); if (adc_mult == 0.0f) { - strcpy(reply, "Error: unsupported by this board"); + strcpy(reply, "Error: unsupported"); } else { sprintf(reply, "> %.3f", adc_mult); } diff --git a/variants/heltec_t096/LoRaFEMControl.h b/variants/heltec_t096/LoRaFEMControl.h index 2c50b742..a3b5c4ed 100644 --- a/variants/heltec_t096/LoRaFEMControl.h +++ b/variants/heltec_t096/LoRaFEMControl.h @@ -12,10 +12,11 @@ class LoRaFEMControl void setRxModeEnable(void); void setRxModeEnableWhenMCUSleep(void); void setLNAEnable(bool enabled); - bool isLnaCanControl(void) { return lna_can_control; } + bool isLnaCanControl(void) const { return lna_can_control; } void setLnaCanControl(bool can_control) { lna_can_control = can_control; } + bool isLNAEnabled(void) const { return lna_enabled; } private: - bool lna_enabled = false; + bool lna_enabled = true; bool lna_can_control = false; }; diff --git a/variants/heltec_t096/T096Board.cpp b/variants/heltec_t096/T096Board.cpp index 55013157..54425145 100644 --- a/variants/heltec_t096/T096Board.cpp +++ b/variants/heltec_t096/T096Board.cpp @@ -123,4 +123,22 @@ void T096Board::powerOff() { const char* T096Board::getManufacturerName() const { return "Heltec T096"; -} \ No newline at end of file +} + +bool T096Board::setLoRaFemLnaEnabled(bool enable) { + if (!loRaFEMControl.isLnaCanControl()) { + return false; + } + + loRaFEMControl.setLNAEnable(enable); + loRaFEMControl.setRxModeEnable(); + return true; +} + +bool T096Board::canControlLoRaFemLna() const { + return loRaFEMControl.isLnaCanControl(); +} + +bool T096Board::isLoRaFemLnaEnabled() const { + return loRaFEMControl.isLNAEnabled(); +} diff --git a/variants/heltec_t096/T096Board.h b/variants/heltec_t096/T096Board.h index d1e3bdfd..15c7e68b 100644 --- a/variants/heltec_t096/T096Board.h +++ b/variants/heltec_t096/T096Board.h @@ -25,4 +25,7 @@ public: uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; void powerOff() override; + bool setLoRaFemLnaEnabled(bool enable) override; + bool canControlLoRaFemLna() const override; + bool isLoRaFemLnaEnabled() const override; }; diff --git a/variants/heltec_tracker_v2/LoRaFEMControl.h b/variants/heltec_tracker_v2/LoRaFEMControl.h index 0ce60fff..a3b5c4ed 100644 --- a/variants/heltec_tracker_v2/LoRaFEMControl.h +++ b/variants/heltec_tracker_v2/LoRaFEMControl.h @@ -17,6 +17,6 @@ class LoRaFEMControl bool isLNAEnabled(void) const { return lna_enabled; } private: - bool lna_enabled = false; + bool lna_enabled = true; bool lna_can_control = false; }; diff --git a/variants/heltec_v4/HeltecV4Board.cpp b/variants/heltec_v4/HeltecV4Board.cpp index f9dafb3b..87791e6b 100644 --- a/variants/heltec_v4/HeltecV4Board.cpp +++ b/variants/heltec_v4/HeltecV4Board.cpp @@ -20,7 +20,7 @@ void HeltecV4Board::begin() { rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS); rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1); } -} + } void HeltecV4Board::onBeforeTransmit(void) { digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on diff --git a/variants/heltec_v4/HeltecV4Board.h b/variants/heltec_v4/HeltecV4Board.h index fe77caed..f96e161c 100644 --- a/variants/heltec_v4/HeltecV4Board.h +++ b/variants/heltec_v4/HeltecV4Board.h @@ -17,10 +17,10 @@ public: void onAfterTransmit(void) override; void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); void powerOff() override; - uint16_t getBattMilliVolts() override; - const char* getManufacturerName() const override ; bool setLoRaFemLnaEnabled(bool enable) override; bool canControlLoRaFemLna() const override; bool isLoRaFemLnaEnabled() const override; + uint16_t getBattMilliVolts() override; + const char* getManufacturerName() const override ; }; From 444dcfb8fd564fb10a131aa75415d41f7f66107c Mon Sep 17 00:00:00 2001 From: Quency-D <55523105+Quency-D@users.noreply.github.com> Date: Mon, 27 Apr 2026 14:06:21 +0800 Subject: [PATCH 031/214] Change write to read for radio_fem_rxgain --- examples/companion_radio/DataStore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index f1dc678e..362ba68a 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -233,7 +233,7 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no file.read((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89 file.read((uint8_t *)_prefs.default_scope_name, sizeof(_prefs.default_scope_name)); // 90 file.read((uint8_t *)_prefs.default_scope_key, sizeof(_prefs.default_scope_key)); // 121 - file.write((uint8_t *)&_prefs.radio_fem_rxgain, sizeof(_prefs.radio_fem_rxgain)); // 122 + file.read((uint8_t *)&_prefs.radio_fem_rxgain, sizeof(_prefs.radio_fem_rxgain)); // 122 file.close(); } From 6c20a1062fd46d1cb18b7dc0d7c930227d96f04e Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 27 Apr 2026 16:33:39 -0700 Subject: [PATCH 032/214] Refine direct retry SNR handling and recent repeater controls --- docs/cli_commands.md | 28 +- examples/simple_repeater/MyMesh.cpp | 446 ++++++++++++++++++++-------- examples/simple_repeater/MyMesh.h | 1 + src/Mesh.cpp | 15 +- src/helpers/CommonCLI.cpp | 75 ++++- src/helpers/CommonCLI.h | 2 +- src/helpers/SimpleMeshTables.h | 194 ++++++++++-- 7 files changed, 584 insertions(+), 177 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index b6c87a7f..9b4723e3 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -118,24 +118,20 @@ This document provides an overview of CLI commands that can be sent to MeshCore ### Get or set recent repeater fallback prefix/SNR **Usage:** - `get recent.repeater` -- `get recent.repeater all` -- `get recent.repeater all ` -- `get recent.repeater first ` -- `get recent.repeater first ` -- `get recent.repeater last ` -- `get recent.repeater last ` -- `set recent.repeater ` +- `get recent.repeater ` +- `get recent.repeater page ` +- `set recent.repeater ` **Parameters:** -- `prefix_hex`: 1-3 bytes of next-hop prefix (hex) +- `prefix_hex_6`: Exactly 3 bytes of next-hop prefix in hex (6 chars) - `snr_db`: SNR in dB (supports decimals; stored at x4 precision) -- `count`: number of entries to print -- `offset`: zero-based row offset into the selected order +- `page`: 1-based page number **Notes:** - `set` is rejected when the prefix already exists in neighbors. -- `all` prints oldest to newest; `first` prints the oldest N; `last` prints the newest N. -- Over LoRa remote CLI, replies are packet-size limited; use `offset` to page through all rows. +- Rows are shown newest-first. +- Serial CLI prints all rows (no paging). +- Over LoRa remote CLI, page size is fixed at `4` rows; choose page with `get recent.repeater `. --- @@ -536,7 +532,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Parameters:** - `state`: `on`|`off` -**Default:** `off` +**Default:** `on` **Note:** When enabled, a repeater can use recently-heard non-duplicate repeater prefixes as a fallback for direct retry eligibility when no suitable neighbor entry is available. @@ -548,9 +544,9 @@ This document provides an overview of CLI commands that can be sent to MeshCore - `set direct.retry.margin ` **Parameters:** -- `value`: Margin in dB above the SF-specific receive floor (minimum `0`, default `5`) +- `value`: Margin in dB above the SF-specific receive floor (minimum `0`, maximum `40`, quarter-dB precision, default `2.5`) -**Default:** `5` +**Default:** `2.5` **Note:** The retry gate uses the active SF floor of `SF5=-2.5`, `SF6=-5`, `SF7=-7.5`, `SF8=-10`, `SF9=-12.5`, `SF10=-15`, `SF11=-17.5`, `SF12=-20`, then adds this margin. @@ -564,7 +560,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Parameters:** - `value`: Retry attempts after initial TX (`1`-`15`) -**Default:** `3` +**Default:** `15` --- diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 907ce2c8..29b126fc 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -86,6 +86,64 @@ bool MyMesh::allowRecentRepeaterPrefixStore(const uint8_t* prefix, uint8_t prefi return self->findNeighbourByHash(prefix, prefix_len) == NULL; } +static void formatRecentRepeaterPrefix(const SimpleMeshTables::RecentRepeaterInfo* info, char* out, size_t out_len) { + if (out == NULL || out_len == 0) { + return; + } + out[0] = 0; + if (info == NULL) { + return; + } + + uint8_t prefix_len = info->prefix_len; + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + if (prefix_len > 0) { + mesh::Utils::toHex(out, info->prefix, prefix_len); + } + + size_t used = strlen(out); + const size_t target_len = MAX_ROUTE_HASH_BYTES * 2; + while (used < target_len && used + 1 < out_len) { + out[used++] = ' '; + } + out[used] = 0; +} + +static void formatRecentRepeaterSnrX4(int8_t snr_x4, char* out, size_t out_len) { + if (out == NULL || out_len == 0) { + return; + } + + const char* snr_text = StrHelper::ftoa(((float)snr_x4) / 4.0f); + if (snr_text[0] == '-') { + snprintf(out, out_len, "%s", snr_text); + } else { + snprintf(out, out_len, " %s", snr_text); + } +} + +static uint8_t decodeTraceHashSize(uint8_t flags, uint8_t route_bytes) { + uint8_t code = flags & 0x03; + uint8_t size_pow2 = (uint8_t)(1U << code); // legacy TRACE interpretation + uint8_t size_linear = (uint8_t)(code + 1U); // packed-size interpretation (1..4) + + bool pow2_ok = size_pow2 > 0 && (route_bytes % size_pow2) == 0; + bool linear_ok = size_linear > 0 && (route_bytes % size_linear) == 0; + + if (pow2_ok && !linear_ok) { + return size_pow2; + } + if (linear_ok && !pow2_ok) { + return size_linear; + } + if (pow2_ok) { + return size_pow2; + } + return size_linear; +} + void MyMesh::putNeighbour(const mesh::Identity &id, uint32_t timestamp, float snr) { #if MAX_NEIGHBOURS // check if neighbours enabled // find existing neighbour, else use least recently updated @@ -456,6 +514,30 @@ void MyMesh::sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, ui bool MyMesh::allowPacketForward(const mesh::Packet *packet) { if (_prefs.disable_fwd) return false; + + if (packet->isRouteDirect() && packet->getPayloadType() == PAYLOAD_TYPE_TRACE && packet->payload_len >= 9) { + auto* tables = (SimpleMeshTables *)getTables(); + uint8_t route_bytes = packet->payload_len - 9; + uint8_t hash_size = decodeTraceHashSize(packet->payload[8], route_bytes); + uint16_t offset = (uint16_t)packet->path_len * (uint16_t)hash_size; + uint8_t sf = constrain(active_sf, (uint8_t)5, (uint8_t)12); + int16_t fallback_snr_x4 = direct_retry_floor_x4[sf - 5] + 40; // fixed +10 dB above SF floor + + // A successful TRACE forward reveals the downstream next-hop hash. Seed/update the recent table immediately. + if (hash_size > 0 && offset + (2U * hash_size) <= route_bytes) { + uint8_t prefix_len = hash_size; + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + const uint8_t* next_hop_prefix = &packet->payload[9 + offset + hash_size]; + const auto* existing = tables->findRecentRepeaterByHash(next_hop_prefix, prefix_len); + // This point only proves we can forward TO next_hop; packet->_snr is upstream RX and not a + // trustworthy metric for next_hop. Seed with existing table value or fallback only. + int8_t trace_snr_x4 = (existing != NULL) ? existing->snr_x4 : (int8_t)constrain(fallback_snr_x4, -128, 127); + tables->setRecentRepeater(next_hop_prefix, prefix_len, trace_snr_x4, false, true); + } + } + if (packet->isRouteFlood() && packet->getPathHashCount() >= _prefs.flood_max) return false; if (packet->isRouteFlood() && recv_pkt_region == NULL) { MESH_DEBUG_PRINTLN("allowPacketForward: unknown transport code, or wildcard not allowed for FLOOD packet"); @@ -564,23 +646,102 @@ void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, u return; } - MESH_DEBUG_PRINTLN("%s direct retry %s (type=%d, route=%s, payload_len=%d, delay=%lu)", + uint8_t prefix[MAX_ROUTE_HASH_BYTES] = {0}; + uint8_t prefix_len = 0; + bool has_prefix = extractDirectRetryPrefix(packet, prefix, prefix_len); + auto* tables = (SimpleMeshTables *)getTables(); + const auto* existing = has_prefix ? tables->findRecentRepeaterByHash(prefix, prefix_len) : NULL; + char next_hop_hex[(MAX_ROUTE_HASH_BYTES * 2) + 1] = {0}; + if (has_prefix && prefix_len > 0) { + mesh::Utils::toHex(next_hop_hex, prefix, prefix_len); + } + const char* next_hop = (has_prefix && prefix_len > 0) ? next_hop_hex : "unknown"; + // Direct-retry events are TX-side and usually have no trustworthy RX SNR. + // Cap event SNR at fixed SF floor + 10 dB so trace-start retries can't inflate table SNR. + uint8_t sf = constrain(active_sf, (uint8_t)5, (uint8_t)12); + int16_t fallback_snr_x4_raw = direct_retry_floor_x4[sf - 5] + 40; + int8_t fallback_snr_x4 = (int8_t)constrain(fallback_snr_x4_raw, -128, 127); + bool is_success_event = (strcmp(event, "good") == 0 || strcmp(event, "canceled_echo") == 0); + int8_t retry_event_snr_x4; + const char* snr_src; + if (is_success_event && packet->_snr != 0) { + // On success, Mesh.cpp injects echo RX SNR for TRACE retries. + retry_event_snr_x4 = packet->_snr; + snr_src = "packet"; + } else if (existing != NULL) { + retry_event_snr_x4 = existing->snr_x4; + snr_src = "table"; + } else { + retry_event_snr_x4 = fallback_snr_x4; + snr_src = "fallback"; + } + char snr_used_text[12]; + char snr_pkt_text[12]; + char snr_table_text[12]; + snprintf(snr_used_text, sizeof(snr_used_text), "%s", StrHelper::ftoa(((float)retry_event_snr_x4) / 4.0f)); + snprintf(snr_pkt_text, sizeof(snr_pkt_text), "%s", StrHelper::ftoa(((float)packet->_snr) / 4.0f)); + if (existing != NULL) { + snprintf(snr_table_text, sizeof(snr_table_text), "%s", StrHelper::ftoa(((float)existing->snr_x4) / 4.0f)); + } else { + snprintf(snr_table_text, sizeof(snr_table_text), "na"); + } + + if (has_prefix && is_success_event) { + // Refresh SNR only on successful echo/progress events, not on queued/resent bookkeeping. + tables->setRecentRepeater(prefix, prefix_len, retry_event_snr_x4, false, true); + } + + if (strcmp(event, "resent") == 0) { + if (has_prefix) { + // Retry stats should be visible even when the prefix was never learned into recent.repeater. + tables->incrementRecentRepeaterRetryCount(prefix, prefix_len, true, retry_event_snr_x4, true); + } + } else if (strcmp(event, "failed_all_tries") == 0) { + if (has_prefix) { + // A failed_all_tries event means all retry attempts for this packet failed. + // Count failures by retry-attempts so fail% reflects failed retries, not just failed sessions. + uint8_t give_up_retries = getDirectRetryMaxAttempts(packet); + uint8_t failed_retries = give_up_retries; + if (failed_retries < 1) { + failed_retries = 1; + } + for (uint8_t i = 0; i < failed_retries; i++) { + tables->incrementRecentRepeaterFailCount(prefix, prefix_len, true, retry_event_snr_x4, true); + } + if (failed_retries >= give_up_retries && give_up_retries > 0) { + // If all configured retry attempts still fail, slightly degrade stored path quality. + tables->decrementRecentRepeaterSnrX4(prefix, prefix_len, 1); + } + } + } + + MESH_DEBUG_PRINTLN("%s direct retry %s (type=%d, route=%s, payload_len=%d, next_hop=%s, snr=%s, snr_src=%s, pkt_snr=%s, table_snr=%s, delay=%lu)", getLogDateTime(), event, (uint32_t)packet->getPayloadType(), packet->isRouteDirect() ? "D" : "F", (uint32_t)packet->payload_len, + next_hop, + snr_used_text, + snr_src, + snr_pkt_text, + snr_table_text, (unsigned long)delay_millis); if (_logging) { File f = openAppend(PACKET_LOG_FILE); if (f) { f.print(getLogDateTime()); - f.printf(": DIRECT RETRY %s (type=%d, route=%s, payload_len=%d, delay=%lu)\n", + f.printf(": DIRECT RETRY %s (type=%d, route=%s, payload_len=%d, next_hop=%s, snr=%s, snr_src=%s, pkt_snr=%s, table_snr=%s, delay=%lu)\n", event, (uint32_t)packet->getPayloadType(), packet->isRouteDirect() ? "D" : "F", (uint32_t)packet->payload_len, + next_hop, + snr_used_text, + snr_src, + snr_pkt_text, + snr_table_text, (unsigned long)delay_millis); f.close(); } @@ -603,28 +764,59 @@ uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) { int8_t MyMesh::getDirectRetryMinSNRX4() const { // Use the live SF so `tempradio` changes immediately affect the retry threshold. uint8_t sf = constrain(active_sf, (uint8_t)5, (uint8_t)12); - int16_t threshold = direct_retry_floor_x4[sf - 5] + ((int16_t)_prefs.direct_retry_snr_margin_db * 4); + int16_t threshold = direct_retry_floor_x4[sf - 5] + (int16_t)_prefs.direct_retry_snr_margin_db; return (int8_t)constrain(threshold, -128, 127); } +bool MyMesh::extractDirectRetryPrefix(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const { + if (packet == NULL || prefix == NULL) { + return false; + } + + // TRACE direct routes encode repeater hashes in payload; packet->path carries SNR trail bytes. + if (packet->isRouteDirect() && packet->getPayloadType() == PAYLOAD_TYPE_TRACE && packet->payload_len >= 9) { + uint8_t route_bytes = packet->payload_len - 9; + uint8_t hash_size = decodeTraceHashSize(packet->payload[8], route_bytes); + uint8_t offset = packet->path_len * hash_size; + if (hash_size > 0 && offset + hash_size <= route_bytes) { + prefix_len = hash_size; + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + memcpy(prefix, &packet->payload[9 + offset], prefix_len); + return true; + } + } + + if (packet->isRouteDirect() && packet->getPathHashCount() > 0) { + prefix_len = packet->getPathHashSize(); + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + if (prefix_len == 0) { + return false; + } + memcpy(prefix, packet->path, prefix_len); + return true; + } + + return false; +} bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const { if (_prefs.disable_fwd) { return false; } int8_t min_snr_x4 = getDirectRetryMinSNRX4(); + if (_prefs.direct_retry_recent_enabled) { + // Prefer the 64-entry recent-prefix cache first, then fall back to neighbours. + const auto* recent = ((const SimpleMeshTables *)getTables())->findRecentRepeaterByHash(next_hop_hash, next_hop_hash_len); + if (recent != NULL && recent->snr_x4 >= min_snr_x4) { + return true; + } + } + const NeighbourInfo* neighbour = findNeighbourByHash(next_hop_hash, next_hop_hash_len); - // Prefer the explicit neighbor table first; it is the strongest signal that this hop is still reachable. - if (neighbour != NULL && neighbour->snr >= min_snr_x4) { - return true; - } - - if (!_prefs.direct_retry_recent_enabled) { - return false; - } - - // If no neighbor entry exists, fall back to the recent-heard repeater cache keyed by the same path prefix. - const auto* recent = ((const SimpleMeshTables *)getTables())->findRecentRepeaterByHash(next_hop_hash, next_hop_hash_len); - return recent != NULL && recent->snr_x4 >= min_snr_x4; + return neighbour != NULL && neighbour->snr >= min_snr_x4; } uint32_t MyMesh::getDirectRetryEchoDelay(const mesh::Packet* packet) const { uint32_t base_wait_millis = constrain((uint32_t)_prefs.direct_retry_base_ms, (uint32_t)10, (uint32_t)5000); @@ -973,9 +1165,9 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.rx_delay_base = 0.0f; // turn off by default, was 10.0; _prefs.tx_delay_factor = 0.5f; // was 0.25f _prefs.direct_tx_delay_factor = 0.3f; // was 0.2 - _prefs.direct_retry_recent_enabled = 0; - _prefs.direct_retry_snr_margin_db = 5; - _prefs.direct_retry_attempts = 3; + _prefs.direct_retry_recent_enabled = 1; + _prefs.direct_retry_snr_margin_db = 10; // 2.5 dB stored in x4 units + _prefs.direct_retry_attempts = 15; _prefs.direct_retry_base_ms = 200; StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); _prefs.node_lat = ADVERT_LAT; @@ -1354,9 +1546,11 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply reply[0] = 0; } else if (strncmp(command, "get recent.repeater", 19) == 0 || strncmp(command, "set recent.repeater", 19) == 0 + || strncmp(command, "clear recent.repeater", 21) == 0 || strncmp(command, "recent.repeater", 15) == 0) { bool is_get = false; bool is_set = false; + bool is_clear = false; const char* sub = command; if (strncmp(command, "get recent.repeater", 19) == 0) { @@ -1365,38 +1559,55 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } else if (strncmp(command, "set recent.repeater", 19) == 0) { is_set = true; sub = command + 19; + } else if (strncmp(command, "clear recent.repeater", 21) == 0) { + is_clear = true; + sub = command + 21; } else { sub = command + 15; // legacy command format } while (*sub == ' ') sub++; auto* tables = (SimpleMeshTables*)getTables(); + if (!is_get && !is_set && !is_clear && strncmp(sub, "clear", 5) == 0 && (sub[5] == 0 || sub[5] == ' ')) { + is_clear = true; + sub += 5; + while (*sub == ' ') sub++; + } - bool is_all = (strcmp(sub, "all") == 0 || strncmp(sub, "all ", 4) == 0); - bool is_first = (strncmp(sub, "first ", 6) == 0); - bool is_last = (strncmp(sub, "last ", 5) == 0); - - if (is_set || (!is_get && *sub != 0 && !is_all && !is_first && !is_last)) { + if (is_clear) { + if (*sub != 0) { + strcpy(reply, "Err - usage: clear recent.repeater"); + } else { + tables->clearRecentRepeaters(); + strcpy(reply, "OK"); + } + } else if (is_set) { char* params = (char*) sub; char* arg_snr = strchr(params, ' '); if (arg_snr == NULL) { - strcpy(reply, "Err - usage: set recent.repeater "); + strcpy(reply, "Err - usage: set recent.repeater "); } else { *arg_snr++ = 0; while (*arg_snr == ' ') arg_snr++; if (*arg_snr == 0) { - strcpy(reply, "Err - usage: set recent.repeater "); + strcpy(reply, "Err - usage: set recent.repeater "); } else { - int hex_len = strlen(params); - int prefix_len = hex_len / 2; uint8_t prefix[MAX_ROUTE_HASH_BYTES] = {0}; - if ((hex_len % 2) != 0 || prefix_len <= 0 || prefix_len > MAX_ROUTE_HASH_BYTES || !mesh::Utils::fromHex(prefix, prefix_len, params)) { - strcpy(reply, "Err - prefix must be 1-3 bytes hex"); + int hex_len = strlen(params); + if (hex_len != (MAX_ROUTE_HASH_BYTES * 2) || !mesh::Utils::fromHex(prefix, MAX_ROUTE_HASH_BYTES, params)) { + strcpy(reply, "Err - prefix must be exactly 3 bytes hex (6 chars)"); } else { - float snr_db = strtof(arg_snr, nullptr); + char* end_snr = NULL; + float snr_db = strtof(arg_snr, &end_snr); + while (end_snr != NULL && *end_snr == ' ') end_snr++; + if (end_snr == arg_snr || (end_snr != NULL && *end_snr != 0)) { + strcpy(reply, "Err - snr must be numeric"); + return; + } + int snr_x4 = (int)(snr_db * 4.0f + (snr_db >= 0.0f ? 0.5f : -0.5f)); snr_x4 = constrain(snr_x4, -128, 127); - if (tables->setRecentRepeater(prefix, (uint8_t)prefix_len, (int8_t)snr_x4)) { + if (tables->setRecentRepeater(prefix, MAX_ROUTE_HASH_BYTES, (int8_t)snr_x4, true)) { strcpy(reply, "OK"); } else { strcpy(reply, "Err - prefix is already in neighbors"); @@ -1404,120 +1615,82 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } } } - } else if (*sub == 0) { - const auto* info = tables->getLatestRecentRepeater(); - if (info == NULL) { - strcpy(reply, "> none"); - } else { - char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; - mesh::Utils::toHex(hex, info->prefix, info->prefix_len); - sprintf(reply, "> %s,%s", hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); - } - } else if (is_all || is_first || is_last) { + } else { int total = tables->getRecentRepeaterCount(); if (total <= 0) { strcpy(reply, "> none"); } else { - bool newest_first = false; - int limit = total; - int offset = 0; - const char* mode = "all"; - - if (is_first || is_last) { - const char* nstr = sub + (is_first ? 6 : 5); - while (*nstr == ' ') nstr++; - if (*nstr == 0) { - strcpy(reply, "Err - usage: get recent.repeater first|last [offset]"); - return; - } - - char* end_ptr = NULL; - long parsed_count = strtol(nstr, &end_ptr, 10); - while (end_ptr != NULL && *end_ptr == ' ') end_ptr++; - if (end_ptr == NULL || parsed_count <= 0) { - strcpy(reply, "Err - count must be > 0"); - return; - } - - if (*end_ptr != 0) { - char* end_ptr2 = NULL; - long parsed_offset = strtol(end_ptr, &end_ptr2, 10); - while (end_ptr2 != NULL && *end_ptr2 == ' ') end_ptr2++; - if (end_ptr2 == NULL || *end_ptr2 != 0 || parsed_offset < 0) { - strcpy(reply, "Err - offset must be >= 0"); - return; - } - offset = (int)parsed_offset; - } - - limit = (int)parsed_count; - if (is_last) { - newest_first = true; - mode = "last"; - } else { - mode = "first"; - } - } else if (strncmp(sub, "all ", 4) == 0) { - const char* arg = sub + 4; - while (*arg == ' ') arg++; - if (*arg == 0) { - strcpy(reply, "Err - usage: get recent.repeater all "); - return; - } - - char* end_ptr = NULL; - long parsed_a = strtol(arg, &end_ptr, 10); - while (end_ptr != NULL && *end_ptr == ' ') end_ptr++; - if (end_ptr == NULL || parsed_a <= 0) { - strcpy(reply, "Err - count must be > 0"); - return; - } - - char* end_ptr2 = NULL; - long parsed_b = strtol(end_ptr, &end_ptr2, 10); - while (end_ptr2 != NULL && *end_ptr2 == ' ') end_ptr2++; - if (end_ptr2 == NULL || *end_ptr2 != 0 || parsed_b < 0) { - strcpy(reply, "Err - usage: get recent.repeater all "); - return; - } - limit = (int)parsed_a; - offset = (int)parsed_b; - } - - if (offset >= total) { - sprintf(reply, "> none (%s off=%d/%d)", mode, offset, total); - return; - } - - int available = total - offset; - if (limit > available) { - limit = available; - } - if (sender_timestamp == 0) { - Serial.printf("Recent repeater table (%s %d/%d, off=%d):\n", mode, limit, total, offset); - for (int i = 0; i < limit; i++) { - int idx = offset + i; - const auto* info = newest_first ? tables->getRecentRepeaterNewestByIdx(idx) : tables->getRecentRepeaterOldestByIdx(idx); + // Serial CLI: print all entries (no paging). + Serial.printf("Recent repeater table (newest first, total=%d):\n", total); + for (int i = 0; i < total; i++) { + const auto* info = tables->getRecentRepeaterNewestByIdx(i); if (info == NULL) { continue; } + char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; - mesh::Utils::toHex(hex, info->prefix, info->prefix_len); - Serial.printf("%02d: %s,%s\n", idx + 1, hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); + formatRecentRepeaterPrefix(info, hex, sizeof(hex)); + char snr_text[12]; + formatRecentRepeaterSnrX4(info->snr_x4, snr_text, sizeof(snr_text)); + uint32_t fail_pct_x10 = 0; + if (info->retry_count > 0) { + fail_pct_x10 = (((uint32_t)info->fail_count * 1000UL) + (info->retry_count / 2U)) / (uint32_t)info->retry_count; + } + Serial.printf("%03d: %s,%s,fp=%lu.%01lu%%,r=%u,f=%u%s\n", + i + 1, + hex, + snr_text, + (unsigned long)(fail_pct_x10 / 10U), + (unsigned long)(fail_pct_x10 % 10U), + (uint32_t)info->retry_count, + (uint32_t)info->fail_count, + info->snr_locked ? ",l" : ""); } - sprintf(reply, "> %s off=%d n=%d/%d", mode, offset, limit, total); + sprintf(reply, "> n=%d/%d", total, total); } else { - // Remote CLI replies are packet-bound, so include as many rows as fit. - int written = snprintf(reply, 160, "> %s off=%d n=%d/%d", mode, offset, limit, total); + // Remote CLI: page by fixed size to fit packet-limited reply payload. + long page_num = 1; + const long page_size = 4; + const char* arg = sub; + + if (strncmp(arg, "page ", 5) == 0) { + arg += 5; + while (*arg == ' ') arg++; + } + + if (*arg != 0) { + char* end_ptr = NULL; + page_num = strtol(arg, &end_ptr, 10); + while (end_ptr != NULL && *end_ptr == ' ') end_ptr++; + if (end_ptr == NULL || page_num <= 0 || (end_ptr != NULL && *end_ptr != 0)) { + strcpy(reply, "Err - usage: get recent.repeater [page]"); + return; + } + } + + int total_pages = (total + (int)page_size - 1) / (int)page_size; + if (page_num > total_pages) { + sprintf(reply, "> none (page=%ld/%d)", page_num, total_pages); + return; + } + + int offset = ((int)page_num - 1) * (int)page_size; + int limit = total - offset; + if (limit > (int)page_size) { + limit = (int)page_size; + } + + int written = snprintf(reply, 160, "> page=%ld/%d n=%d/%d", page_num, total_pages, limit, total); bool truncated = false; if (written < 0) { reply[0] = 0; written = 0; } + for (int i = 0; i < limit; i++) { int idx = offset + i; - const auto* info = newest_first ? tables->getRecentRepeaterNewestByIdx(idx) : tables->getRecentRepeaterOldestByIdx(idx); + const auto* info = tables->getRecentRepeaterNewestByIdx(idx); if (info == NULL) { continue; } @@ -1525,9 +1698,20 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply truncated = true; break; } + char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; - mesh::Utils::toHex(hex, info->prefix, info->prefix_len); - int n = snprintf(reply + written, 160 - written, "\n%02d:%s,%s", idx + 1, hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); + formatRecentRepeaterPrefix(info, hex, sizeof(hex)); + char snr_text[12]; + formatRecentRepeaterSnrX4(info->snr_x4, snr_text, sizeof(snr_text)); + int n = snprintf(reply + written, + 160 - written, + "\n%03d:%s,%s,r=%u,f=%u%s", + idx + 1, + hex, + snr_text, + (uint32_t)info->retry_count, + (uint32_t)info->fail_count, + info->snr_locked ? ",l" : ""); if (n < 0 || n >= (160 - written)) { truncated = true; break; @@ -1535,12 +1719,10 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply written += n; } if (truncated && written < 156) { - snprintf(reply + written, 160 - written, "\n... use offset"); + snprintf(reply + written, 160 - written, "\n... next page"); } } } - } else { - strcpy(reply, "Err - usage: get recent.repeater [all|all |first [offset]|last [offset]]"); } } else if (memcmp(command, "discover.neighbors", 18) == 0) { const char* sub = command + 18; diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 00a8a31b..b2626e60 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -123,6 +123,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { #endif const NeighbourInfo* findNeighbourByHash(const uint8_t* hash, uint8_t hash_len) const; + bool extractDirectRetryPrefix(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const; static bool allowRecentRepeaterPrefixStore(const uint8_t* prefix, uint8_t prefix_len, void* ctx); int8_t getDirectRetryMinSNRX4() const; void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr); diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 47fc6e8d..f07484d6 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -3,7 +3,7 @@ namespace mesh { -static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT = 3; +static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT = 15; static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX = 15; void Mesh::begin() { @@ -491,6 +491,12 @@ bool Mesh::cancelDirectRetryOnEcho(const Packet* packet) { } if (_direct_retries[i].queued) { + if (_direct_retries[i].expect_path_growth + && _direct_retries[i].packet != NULL + && _direct_retries[i].progress_marker < packet->path_len) { + // For retry-good quality, use the received echo packet SNR (return-link quality). + _direct_retries[i].packet->_snr = packet->_snr; + } for (int j = 0; j < _mgr->getOutboundTotal(); j++) { if (_mgr->getOutboundByIdx(j) == _direct_retries[i].packet) { Packet* pending = _mgr->removeOutboundByIdx(j); @@ -504,6 +510,12 @@ bool Mesh::cancelDirectRetryOnEcho(const Packet* packet) { onDirectRetryEvent("good", _direct_retries[i].packet, 0); clearDirectRetrySlot(i); } else { + if (_direct_retries[i].expect_path_growth + && _direct_retries[i].trigger_packet != NULL + && _direct_retries[i].progress_marker < packet->path_len) { + // For retry-good quality, use the received echo packet SNR (return-link quality). + _direct_retries[i].trigger_packet->_snr = packet->_snr; + } onDirectRetryEvent("canceled_echo", _direct_retries[i].trigger_packet, 0); onDirectRetryEvent("good", _direct_retries[i].trigger_packet, 0); clearDirectRetrySlot(i); @@ -532,6 +544,7 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { max_attempts = DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX; } if (_direct_retries[i].retry_attempts_sent >= max_attempts) { + onDirectRetryEvent("failed_all_tries", packet, 0); onDirectRetryEvent("failure", packet, 0); clearDirectRetrySlot(i); continue; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 02d27830..9460a28f 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -11,12 +11,13 @@ // These bytes used to be reserved/unused in persisted prefs, so keep a marker before trusting them. #define DIRECT_RETRY_PREFS_MAGIC_0 0xD4 #define DIRECT_RETRY_PREFS_MAGIC_1 0x52 -#define DIRECT_RETRY_RECENT_DEFAULT 0 -#define DIRECT_RETRY_SNR_MARGIN_DB_DEFAULT 5 -#define DIRECT_RETRY_SNR_MARGIN_DB_MAX 40 +#define DIRECT_RETRY_RECENT_DEFAULT 1 +#define DIRECT_RETRY_SNR_MARGIN_DB_DEFAULT_X4 10 +#define DIRECT_RETRY_SNR_MARGIN_DB_MAX 40 +#define DIRECT_RETRY_SNR_MARGIN_X4_MAX (DIRECT_RETRY_SNR_MARGIN_DB_MAX * 4) #define DIRECT_RETRY_TIMING_MAGIC_0 0xD5 #define DIRECT_RETRY_TIMING_MAGIC_1 0x54 -#define DIRECT_RETRY_COUNT_DEFAULT 3 +#define DIRECT_RETRY_COUNT_DEFAULT 15 #define DIRECT_RETRY_COUNT_MIN 1 #define DIRECT_RETRY_COUNT_MAX 15 #define DIRECT_RETRY_BASE_MS_DEFAULT 200 @@ -33,6 +34,15 @@ static uint32_t _atoi(const char* sp) { return n; } +static uint8_t directRetryMarginDbToX4(float margin_db) { + int32_t scaled_x4 = (int32_t)((margin_db * 4.0f) + 0.5f); // nearest 0.25 dB + return (uint8_t)constrain(scaled_x4, 0, DIRECT_RETRY_SNR_MARGIN_X4_MAX); +} + +static float directRetryMarginX4ToDb(uint8_t margin_x4) { + return ((float)margin_x4) / 4.0f; +} + static bool isValidName(const char *n) { while (*n) { if (*n == '[' || *n == ']' || *n == '\\' || *n == ':' || *n == ',' || *n == '?' || *n == '*') return false; @@ -127,10 +137,10 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { if (_prefs->direct_retry_prefs_magic[0] != DIRECT_RETRY_PREFS_MAGIC_0 || _prefs->direct_retry_prefs_magic[1] != DIRECT_RETRY_PREFS_MAGIC_1) { _prefs->direct_retry_recent_enabled = DIRECT_RETRY_RECENT_DEFAULT; - _prefs->direct_retry_snr_margin_db = DIRECT_RETRY_SNR_MARGIN_DB_DEFAULT; + _prefs->direct_retry_snr_margin_db = DIRECT_RETRY_SNR_MARGIN_DB_DEFAULT_X4; } else { _prefs->direct_retry_recent_enabled = constrain(_prefs->direct_retry_recent_enabled, 0, 1); - _prefs->direct_retry_snr_margin_db = constrain(_prefs->direct_retry_snr_margin_db, 0, DIRECT_RETRY_SNR_MARGIN_DB_MAX); + _prefs->direct_retry_snr_margin_db = constrain(_prefs->direct_retry_snr_margin_db, 0, DIRECT_RETRY_SNR_MARGIN_X4_MAX); } if (_prefs->direct_retry_timing_magic[0] != DIRECT_RETRY_TIMING_MAGIC_0 || _prefs->direct_retry_timing_magic[1] != DIRECT_RETRY_TIMING_MAGIC_1) { @@ -385,7 +395,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else if (memcmp(config, "direct.retry.heard", 18) == 0) { sprintf(reply, "> %s", _prefs->direct_retry_recent_enabled ? "on" : "off"); } else if (memcmp(config, "direct.retry.margin", 19) == 0) { - sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_snr_margin_db); + sprintf(reply, "> %s", StrHelper::ftoa(directRetryMarginX4ToDb(_prefs->direct_retry_snr_margin_db))); } else if (memcmp(config, "direct.retry.count", 18) == 0) { sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_attempts); } else if (memcmp(config, "direct.retry.base", 17) == 0) { @@ -652,9 +662,9 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re strcpy(reply, "Error, must be on or off"); } } else if (memcmp(config, "direct.retry.margin ", 20) == 0) { - int db = atoi(&config[20]); + float db = atof(&config[20]); if (db >= 0 && db <= DIRECT_RETRY_SNR_MARGIN_DB_MAX) { - _prefs->direct_retry_snr_margin_db = (uint8_t)db; + _prefs->direct_retry_snr_margin_db = directRetryMarginDbToX4(db); savePrefs(); strcpy(reply, "OK"); } else { @@ -1113,6 +1123,45 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { strcpy(reply, "Error, cannot be negative"); } + } else if (memcmp(config, "direct.retry.heard ", 19) == 0) { + if (memcmp(&config[19], "on", 2) == 0) { + _prefs->direct_retry_recent_enabled = 1; + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(&config[19], "off", 3) == 0) { + _prefs->direct_retry_recent_enabled = 0; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be on or off"); + } + } else if (memcmp(config, "direct.retry.margin ", 20) == 0) { + float db = atof(&config[20]); + if (db >= 0 && db <= DIRECT_RETRY_SNR_MARGIN_DB_MAX) { + _prefs->direct_retry_snr_margin_db = directRetryMarginDbToX4(db); + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error, min 0 and max %d", DIRECT_RETRY_SNR_MARGIN_DB_MAX); + } + } else if (memcmp(config, "direct.retry.count ", 19) == 0) { + int count = atoi(&config[19]); + if (count >= DIRECT_RETRY_COUNT_MIN && count <= DIRECT_RETRY_COUNT_MAX) { + _prefs->direct_retry_attempts = (uint8_t)count; + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error, min %d and max %d", DIRECT_RETRY_COUNT_MIN, DIRECT_RETRY_COUNT_MAX); + } + } else if (memcmp(config, "direct.retry.base ", 18) == 0) { + int delay_ms = atoi(&config[18]); + if (delay_ms >= DIRECT_RETRY_BASE_MS_MIN && delay_ms <= DIRECT_RETRY_BASE_MS_MAX) { + _prefs->direct_retry_base_ms = (uint16_t)delay_ms; + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error, min %d and max %d", DIRECT_RETRY_BASE_MS_MIN, DIRECT_RETRY_BASE_MS_MAX); + } } else if (memcmp(config, "owner.info ", 11) == 0) { config += 11; char *dp = _prefs->owner_info; @@ -1282,6 +1331,14 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "> %d", (uint32_t)_prefs->flood_max); } else if (memcmp(config, "direct.txdelay", 14) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor)); + } else if (memcmp(config, "direct.retry.heard", 18) == 0) { + sprintf(reply, "> %s", _prefs->direct_retry_recent_enabled ? "on" : "off"); + } else if (memcmp(config, "direct.retry.margin", 19) == 0) { + sprintf(reply, "> %s", StrHelper::ftoa(directRetryMarginX4ToDb(_prefs->direct_retry_snr_margin_db))); + } else if (memcmp(config, "direct.retry.count", 18) == 0) { + sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_attempts); + } else if (memcmp(config, "direct.retry.base", 17) == 0) { + sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_base_ms); } else if (memcmp(config, "owner.info", 10) == 0) { *reply++ = '>'; *reply++ = ' '; diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 03b1fb64..ddc92ff1 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -34,7 +34,7 @@ struct NodePrefs { // persisted to file char guest_password[16]; float direct_tx_delay_factor; uint8_t direct_retry_recent_enabled; - uint8_t direct_retry_snr_margin_db; + uint8_t direct_retry_snr_margin_db; // stored in quarter-dB units (x4) uint8_t direct_retry_prefs_magic[2]; uint8_t sf; uint8_t cr; diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index effea219..ac6d01b5 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -8,7 +8,14 @@ #define MAX_PACKET_HASHES 128 #define MAX_PACKET_ACKS 64 -#define MAX_RECENT_REPEATERS 64 +#ifndef MAX_RECENT_REPEATERS + // Two defaults. Can be overridden with -D MAX_RECENT_REPEATERS=. + #if defined(ESP32) + #define MAX_RECENT_REPEATERS 512 + #else + #define MAX_RECENT_REPEATERS 64 + #endif +#endif #define MAX_ROUTE_HASH_BYTES 3 class SimpleMeshTables : public mesh::MeshTables { @@ -17,9 +24,12 @@ public: struct RecentRepeaterInfo { // Just enough identity to match a next-hop path prefix plus the SNR that heard it. + uint16_t retry_count; + uint16_t fail_count; uint8_t prefix[MAX_ROUTE_HASH_BYTES]; uint8_t prefix_len; int8_t snr_x4; + uint8_t snr_locked; }; private: @@ -68,19 +78,20 @@ private: return n > 0 && memcmp(a, b, n) == 0; } - int8_t avgSnrX4RoundUp(int8_t curr_snr_x4, int8_t new_snr_x4) const { - int16_t sum = (int16_t)curr_snr_x4 + (int16_t)new_snr_x4; - int16_t avg = sum / 2; // truncates toward zero - // "Round up" means ceil(), which only differs from truncation for positive odd sums. - if (sum > 0 && (sum & 1)) { - avg++; + int8_t weightedSnrX4RoundUp(int8_t curr_snr_x4, int8_t new_snr_x4) const { + // Keep existing SNR heavier than a single new sample: 75% existing + 25% new. + int16_t weighted_sum = ((int16_t)curr_snr_x4 * 3) + (int16_t)new_snr_x4; + int16_t blended = weighted_sum / 4; // truncates toward zero + // "Round up" means ceil(), which only differs from truncation for positive remainders. + if (weighted_sum > 0 && (weighted_sum % 4) != 0) { + blended++; } - if (avg > 127) { - avg = 127; - } else if (avg < -128) { - avg = -128; + if (blended > 127) { + blended = 127; + } else if (blended < -128) { + blended = -128; } - return (int8_t)avg; + return (int8_t)blended; } bool extractRecentRepeater(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const { @@ -249,7 +260,8 @@ public: _recent_repeater_allow_fn = fn; _recent_repeater_allow_ctx = ctx; } - bool setRecentRepeater(const uint8_t* prefix, uint8_t prefix_len, int8_t snr_x4) { + bool setRecentRepeater(const uint8_t* prefix, uint8_t prefix_len, int8_t snr_x4, bool snr_locked = false, + bool bypass_allow_filter = false) { if (prefix == NULL || prefix_len == 0) { return false; } @@ -258,7 +270,8 @@ public: prefix_len = MAX_ROUTE_HASH_BYTES; } - if (_recent_repeater_allow_fn != NULL && !_recent_repeater_allow_fn(prefix, prefix_len, _recent_repeater_allow_ctx)) { + if (!bypass_allow_filter && _recent_repeater_allow_fn != NULL + && !_recent_repeater_allow_fn(prefix, prefix_len, _recent_repeater_allow_ctx)) { return false; } @@ -273,19 +286,160 @@ public: memcpy(existing.prefix, prefix, prefix_len); existing.prefix_len = prefix_len; } - existing.snr_x4 = avgSnrX4RoundUp(existing.snr_x4, snr_x4); + if (snr_locked) { + existing.snr_x4 = snr_x4; + existing.snr_locked = 1; + } else if (!existing.snr_locked) { + existing.snr_x4 = weightedSnrX4RoundUp(existing.snr_x4, snr_x4); + } return true; } - // Ring buffer is enough here; retry fallback only needs a recent prefix->SNR observation. - RecentRepeaterInfo& slot = _recent_repeaters[_next_recent_repeater_idx]; + int slot_idx = -1; + // Prefer empty slots first while preserving newest-order iteration. + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + int idx = (_next_recent_repeater_idx + i) % MAX_RECENT_REPEATERS; + if (_recent_repeaters[idx].prefix_len == 0) { + slot_idx = idx; + break; + } + } + if (slot_idx < 0) { + // Table is full: evict the weakest observed SNR entry. + slot_idx = 0; + int8_t min_snr_x4 = _recent_repeaters[0].snr_x4; + for (int i = 1; i < MAX_RECENT_REPEATERS; i++) { + if (_recent_repeaters[i].snr_x4 < min_snr_x4) { + min_snr_x4 = _recent_repeaters[i].snr_x4; + slot_idx = i; + } + } + } + + RecentRepeaterInfo& slot = _recent_repeaters[slot_idx]; memset(slot.prefix, 0, sizeof(slot.prefix)); memcpy(slot.prefix, prefix, prefix_len); slot.prefix_len = prefix_len; slot.snr_x4 = snr_x4; - _next_recent_repeater_idx = (_next_recent_repeater_idx + 1) % MAX_RECENT_REPEATERS; + slot.retry_count = 0; + slot.fail_count = 0; + slot.snr_locked = snr_locked ? 1 : 0; + _next_recent_repeater_idx = (slot_idx + 1) % MAX_RECENT_REPEATERS; return true; } + bool incrementRecentRepeaterRetryCount(const uint8_t* prefix, uint8_t prefix_len, + bool create_if_missing = false, int8_t seed_snr_x4 = 0, + bool bypass_allow_filter = false) { + if (prefix == NULL || prefix_len == 0) { + return false; + } + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + RecentRepeaterInfo& existing = _recent_repeaters[i]; + if (existing.prefix_len == 0 || !prefixesOverlap(existing.prefix, existing.prefix_len, prefix, prefix_len)) { + continue; + } + if (prefix_len > existing.prefix_len) { + memset(existing.prefix, 0, sizeof(existing.prefix)); + memcpy(existing.prefix, prefix, prefix_len); + existing.prefix_len = prefix_len; + } + if (existing.retry_count < 0xFFFF) { + existing.retry_count++; + } + return true; + } + + if (!create_if_missing || !setRecentRepeater(prefix, prefix_len, seed_snr_x4, false, bypass_allow_filter)) { + return false; + } + + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + RecentRepeaterInfo& existing = _recent_repeaters[i]; + if (existing.prefix_len == 0 || !prefixesOverlap(existing.prefix, existing.prefix_len, prefix, prefix_len)) { + continue; + } + if (existing.retry_count < 0xFFFF) { + existing.retry_count++; + } + return true; + } + return false; + } + bool incrementRecentRepeaterFailCount(const uint8_t* prefix, uint8_t prefix_len, + bool create_if_missing = false, int8_t seed_snr_x4 = 0, + bool bypass_allow_filter = false) { + if (prefix == NULL || prefix_len == 0) { + return false; + } + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + RecentRepeaterInfo& existing = _recent_repeaters[i]; + if (existing.prefix_len == 0 || !prefixesOverlap(existing.prefix, existing.prefix_len, prefix, prefix_len)) { + continue; + } + if (prefix_len > existing.prefix_len) { + memset(existing.prefix, 0, sizeof(existing.prefix)); + memcpy(existing.prefix, prefix, prefix_len); + existing.prefix_len = prefix_len; + } + if (existing.fail_count < 0xFFFF) { + existing.fail_count++; + } + return true; + } + + if (!create_if_missing || !setRecentRepeater(prefix, prefix_len, seed_snr_x4, false, bypass_allow_filter)) { + return false; + } + + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + RecentRepeaterInfo& existing = _recent_repeaters[i]; + if (existing.prefix_len == 0 || !prefixesOverlap(existing.prefix, existing.prefix_len, prefix, prefix_len)) { + continue; + } + if (existing.fail_count < 0xFFFF) { + existing.fail_count++; + } + return true; + } + return false; + } + bool decrementRecentRepeaterSnrX4(const uint8_t* prefix, uint8_t prefix_len, uint8_t amount_x4 = 1) { + if (prefix == NULL || prefix_len == 0 || amount_x4 == 0) { + return false; + } + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + RecentRepeaterInfo& existing = _recent_repeaters[i]; + if (existing.prefix_len == 0 || !prefixesOverlap(existing.prefix, existing.prefix_len, prefix, prefix_len)) { + continue; + } + if (prefix_len > existing.prefix_len) { + memset(existing.prefix, 0, sizeof(existing.prefix)); + memcpy(existing.prefix, prefix, prefix_len); + existing.prefix_len = prefix_len; + } + if (!existing.snr_locked) { + int16_t lowered = (int16_t)existing.snr_x4 - (int16_t)amount_x4; + if (lowered < -128) { + lowered = -128; + } + existing.snr_x4 = (int8_t)lowered; + } + return true; + } + return false; + } const RecentRepeaterInfo* getLatestRecentRepeater() const { for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { int idx = (_next_recent_repeater_idx - 1 - i + MAX_RECENT_REPEATERS) % MAX_RECENT_REPEATERS; @@ -360,6 +514,10 @@ public: } return NULL; } + void clearRecentRepeaters() { + memset(_recent_repeaters, 0, sizeof(_recent_repeaters)); + _next_recent_repeater_idx = 0; + } void resetStats() { _direct_dups = _flood_dups = 0; } }; From 65a28582dde00f8774e8e04b34282341b7ae377a Mon Sep 17 00:00:00 2001 From: hank Date: Tue, 28 Apr 2026 20:54:11 -0700 Subject: [PATCH 033/214] Add PERM_ACL_REGION_MGR role for delegated region management Introduces a fourth ACL role (value 4) that can manage the region map without full admin privileges. The role is intended for trusted users who curate regions on a repeater but should not have access to general admin commands. ClientACL: - Widen PERM_ACL_ROLE_MASK from 2 to 3 bits so the new value fits. - Add PERM_ACL_REGION_MGR and ClientInfo::isRegionMgr(). - Exempt region_mgr entries from least-recently-active eviction in putClient(), same as admins. simple_repeater: - Phones may still gate UI on the legacy is_admin byte (reply_data[6]), so report region_mgr as admin there. Without this, the phone CLI falls back to guest view. - Allow region_mgr to send TXT_MSG CLI commands. handleCommand() gates non-whitelisted commands with "Err - not permitted". The whitelist covers region.* (read+write) plus a small set of read-only queries (get, ver, board, neighbors, clock, sensor get/list). - Pass the ClientInfo* through to handleCommand and drop the redundant sender_timestamp parameter (derived from sender->last_timestamp; NULL means Serial CLI). - Use ~PERM_ACL_ROLE_MASK instead of ~0x03 when clearing role bits on login, so the wider mask is honored. --- examples/simple_repeater/MyMesh.cpp | 39 +++++++++++++++++++++++++---- examples/simple_repeater/MyMesh.h | 2 +- examples/simple_repeater/main.cpp | 2 +- src/helpers/ClientACL.cpp | 3 ++- src/helpers/ClientACL.h | 4 ++- 5 files changed, 41 insertions(+), 9 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 666f79fc..24bcfac5 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -119,7 +119,7 @@ uint8_t MyMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* secr MESH_DEBUG_PRINTLN("Login success!"); client->last_timestamp = sender_timestamp; client->last_activity = getRTCClock()->getCurrentTime(); - client->permissions &= ~0x03; + client->permissions &= ~PERM_ACL_ROLE_MASK; client->permissions |= perms; memcpy(client->shared_secret, secret, PUB_KEY_SIZE); @@ -136,7 +136,7 @@ uint8_t MyMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* secr memcpy(reply_data, &now, 4); // response packets always prefixed with timestamp reply_data[4] = RESP_SERVER_LOGIN_OK; reply_data[5] = 0; // Legacy: was recommended keep-alive interval (secs / 16) - reply_data[6] = client->isAdmin() ? 1 : 0; + reply_data[6] = (client->isAdmin() || client->isRegionMgr()) ? 1 : 0; reply_data[7] = client->permissions; getRNG()->random(&reply_data[8], 4); // random blob to help packet-hash uniqueness reply_data[12] = FIRMWARE_VER_LEVEL; // New field @@ -682,7 +682,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, } else { MESH_DEBUG_PRINTLN("onPeerDataRecv: possible replay attack detected"); } - } else if (type == PAYLOAD_TYPE_TXT_MSG && len > 5 && client->isAdmin()) { // a CLI command + } else if (type == PAYLOAD_TYPE_TXT_MSG && len > 5 && (client->isAdmin() || client->isRegionMgr())) { // a CLI command uint32_t sender_timestamp; memcpy(&sender_timestamp, data, 4); // timestamp (by sender's RTC clock - which could be wrong) uint8_t flags = (data[4] >> 2); // message attempt number, and other flags @@ -719,7 +719,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, if (is_retry) { *reply = 0; } else { - handleCommand(sender_timestamp, command, reply); + handleCommand(client, command, reply); } int text_len = strlen(reply); if (text_len > 0) { @@ -1165,7 +1165,27 @@ void MyMesh::clearStats() { ((SimpleMeshTables *)getTables())->resetStats(); } -void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply) { +// Whitelist helper for region manager command perms +static bool isRegionMgrAllowed(const char* cmd) { + while(*cmd == ' ') cmd++; // skip leading spaces + // region commands (read + write region map) + if (memcmp(cmd, "region", 6) == 0) return true; + // read-only getters / status + if (memcmp(cmd, "get ", 4) == 0) return true; + if (memcmp(cmd, "ver", 3) == 0) return true; + if (memcmp(cmd, "board", 5) == 0) return true; + // "neighbors" (plural) is read-only; reject "neighbor.remove" by checking next char + if (memcmp(cmd, "neighbors", 9) == 0) return true; + // bare "clock" is read-only; "clock sync" must be denied + if (memcmp(cmd, "clock", 5) == 0 && memcmp(cmd, "clock sync", 10) != 0) return true; + // sensor reads only + if (memcmp(cmd, "sensor get ", 11) == 0) return true; + if (memcmp(cmd, "sensor list", 11) == 0) return true; + return false; +} + +void MyMesh::handleCommand(ClientInfo* sender, char *command, char *reply) { + uint32_t sender_timestamp = sender ? sender->last_timestamp : 0; // Serial CLI passes NULL if (region_load_active) { if (StrHelper::isBlank(command)) { // empty/blank line, signal to terminate 'load' operation region_map = temp_map; // copy over the temp instance as new current map @@ -1208,6 +1228,15 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply command += 3; } + // Region managers are limited to read-only queries and region commands + // Admins are unrestricted + if (sender && !sender->isAdmin() && sender->isRegionMgr()) { + if (!isRegionMgrAllowed(command)) { + strcpy(reply, "Err - not permitted"); + return; + } + } + // handle ACL related commands if (memcmp(command, "setperm ", 8) == 0) { // format: setperm {pubkey-hex} {permissions-int8} char* hex = &command[8]; diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 8ed0317e..c14a25d7 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -223,7 +223,7 @@ public: void saveIdentity(const mesh::LocalIdentity& new_id) override; void clearStats() override; - void handleCommand(uint32_t sender_timestamp, char* command, char* reply); + void handleCommand(ClientInfo* sender, char* command, char* reply); void loop(); #if defined(WITH_BRIDGE) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index e37078ce..5d6bb260 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -124,7 +124,7 @@ void loop() { Serial.print('\n'); command[len - 1] = 0; // replace newline with C string null terminator char reply[160]; - the_mesh.handleCommand(0, command, reply); // NOTE: there is no sender_timestamp via serial! + the_mesh.handleCommand(NULL, command, reply); // NOTE: sender is NULL via serial if (reply[0]) { Serial.print(" -> "); Serial.println(reply); } diff --git a/src/helpers/ClientACL.cpp b/src/helpers/ClientACL.cpp index 12823827..8abdd565 100644 --- a/src/helpers/ClientACL.cpp +++ b/src/helpers/ClientACL.cpp @@ -99,7 +99,8 @@ ClientInfo* ClientACL::putClient(const mesh::Identity& id, uint8_t init_perms) { ClientInfo* oldest = &clients[MAX_CLIENTS - 1]; for (int i = 0; i < num_clients; i++) { if (id.matches(clients[i].id)) return &clients[i]; // already known - if (!clients[i].isAdmin() && clients[i].last_activity < min_time) { + if ( (!clients[i].isAdmin() && !clients[i].isRegionMgr()) + && clients[i].last_activity < min_time) { oldest = &clients[i]; min_time = oldest->last_activity; } diff --git a/src/helpers/ClientACL.h b/src/helpers/ClientACL.h index b758f706..1961546d 100644 --- a/src/helpers/ClientACL.h +++ b/src/helpers/ClientACL.h @@ -4,11 +4,12 @@ #include #include -#define PERM_ACL_ROLE_MASK 3 // lower 2 bits +#define PERM_ACL_ROLE_MASK 7 // lower 3 bits #define PERM_ACL_GUEST 0 #define PERM_ACL_READ_ONLY 1 #define PERM_ACL_READ_WRITE 2 #define PERM_ACL_ADMIN 3 +#define PERM_ACL_REGION_MGR 4 #define OUT_PATH_UNKNOWN 0xFF @@ -31,6 +32,7 @@ struct ClientInfo { } extra; bool isAdmin() const { return (permissions & PERM_ACL_ROLE_MASK) == PERM_ACL_ADMIN; } + bool isRegionMgr() const { return (permissions & PERM_ACL_ROLE_MASK) == PERM_ACL_REGION_MGR; } }; #ifndef MAX_CLIENTS From cb521b44443db2b76ad32d5ac1ab1682e618925e Mon Sep 17 00:00:00 2001 From: hank Date: Tue, 28 Apr 2026 20:58:11 -0700 Subject: [PATCH 034/214] handleLoginReq: layer ACL between admin pw and guest/blank pw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reorder login auth so that, in priority: 1. Admin password match -> ADMIN (overrides ACL — a region_mgr or other ACL'd client who knows the admin pw can still upgrade). 2. Sender pubkey is in the ACL -> use the stored permissions (no password required; pubkey is itself an identity assertion). 3. Guest password match (or blank pw when guest pw is unset) -> GUEST. 4. Otherwise -> reject. Previously the ACL was consulted only when `data[0] == 0` (blank pw), so a non-blank password would skip the ACL entirely and fall through to the password block. Phones that sent any non-empty password field — even a stale one — would never see their ACL-granted role; they'd either be rejected or assigned the role implied by the password match. Implementation is a single-line change: gate the ACL lookup on "the admin pw did NOT match". When admin matches, we skip the lookup so the existing password block sets ADMIN unconditionally (the right behavior for the upgrade case). When admin does not match, the ACL takes over; only on an ACL miss do we fall through to guest/reject. --- examples/simple_repeater/MyMesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 666f79fc..71176db9 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -89,7 +89,7 @@ void MyMesh::putNeighbour(const mesh::Identity &id, uint32_t timestamp, float sn uint8_t MyMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood) { ClientInfo* client = NULL; - if (data[0] == 0) { // blank password, just check if sender is in ACL + if (strcmp((char *)data, _prefs.password) != 0) { // admin pw bypasses ACL (allows upgrade) client = acl.getClient(sender.pub_key, PUB_KEY_SIZE); if (client == NULL) { #if MESH_DEBUG From 8e787f6ee401e7671446bf05ce4839b614656252 Mon Sep 17 00:00:00 2001 From: jirogit Date: Tue, 21 Apr 2026 22:23:42 -0700 Subject: [PATCH 035/214] fix(RegionMap): save() returns actual write success instead of hardcoded true --- src/helpers/RegionMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/RegionMap.cpp b/src/helpers/RegionMap.cpp index 7b8399e2..36c1d19d 100644 --- a/src/helpers/RegionMap.cpp +++ b/src/helpers/RegionMap.cpp @@ -139,7 +139,7 @@ bool RegionMap::save(FILESYSTEM* _fs, const char* path) { } } file.close(); - return true; + return success; } return false; // failed } From 7f02cbe80b51ce7d250e913da3b5be3d5dd34d55 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Mon, 4 May 2026 22:04:11 +0700 Subject: [PATCH 036/214] Delayed sleep for BLE companions if BLE read is busy. Reduced sleep period from 50 sticks to 10 sticks --- examples/companion_radio/main.cpp | 4 ++-- src/helpers/BaseSerialInterface.h | 1 + src/helpers/esp32/SerialBLEInterface.cpp | 4 ++++ src/helpers/esp32/SerialBLEInterface.h | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index b4e8d386..bb350fd0 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -266,8 +266,8 @@ void loop() { #if defined(NRF52_PLATFORM) board.sleep(0); // nrf ignores seconds param, sleeps whenever possible #else if defined(ESP32_PLATFORM) - if (!serial_interface.isWriteBusy()) { // BLE is not busy - vTaskDelay(pdMS_TO_TICKS(50)); // attempt to sleep + if (!serial_interface.isReadBusy() && !serial_interface.isWriteBusy()) { // BLE is not busy + vTaskDelay(pdMS_TO_TICKS(10)); // attempt to sleep } #endif } diff --git a/src/helpers/BaseSerialInterface.h b/src/helpers/BaseSerialInterface.h index e6092765..ddde4830 100644 --- a/src/helpers/BaseSerialInterface.h +++ b/src/helpers/BaseSerialInterface.h @@ -15,6 +15,7 @@ public: virtual bool isConnected() const = 0; + virtual bool isReadBusy() const = 0; virtual bool isWriteBusy() const = 0; virtual size_t writeFrame(const uint8_t src[], size_t len) = 0; virtual size_t checkRecvFrame(uint8_t dest[]) = 0; diff --git a/src/helpers/esp32/SerialBLEInterface.cpp b/src/helpers/esp32/SerialBLEInterface.cpp index dcfa0e1e..50e1501e 100644 --- a/src/helpers/esp32/SerialBLEInterface.cpp +++ b/src/helpers/esp32/SerialBLEInterface.cpp @@ -182,6 +182,10 @@ size_t SerialBLEInterface::writeFrame(const uint8_t src[], size_t len) { #define BLE_WRITE_MIN_INTERVAL 60 +bool SerialBLEInterface::isReadBusy() const { + return (recv_queue_len > 0); +} + bool SerialBLEInterface::isWriteBusy() const { return millis() < _last_write + BLE_WRITE_MIN_INTERVAL; // still too soon to start another write? } diff --git a/src/helpers/esp32/SerialBLEInterface.h b/src/helpers/esp32/SerialBLEInterface.h index 965e90fd..19e024b0 100644 --- a/src/helpers/esp32/SerialBLEInterface.h +++ b/src/helpers/esp32/SerialBLEInterface.h @@ -76,6 +76,7 @@ public: bool isConnected() const override; + bool isReadBusy() const override; bool isWriteBusy() const override; size_t writeFrame(const uint8_t src[], size_t len) override; size_t checkRecvFrame(uint8_t dest[]) override; From eac4f7201e03805917bbae4288862e145a413fb6 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Wed, 6 May 2026 12:38:22 +0700 Subject: [PATCH 037/214] Commented NRF52_POWER_MANAGEMENT and added PowerSaving BLE companions --- variants/gat562_mesh_evb_pro/platformio.ini | 2 +- variants/gat562_mesh_watch13/platformio.ini | 2 +- variants/heltec_t096/platformio.ini | 26 +++++++++++++++++- variants/heltec_tracker_v2/platformio.ini | 27 +++++++++++++++++++ variants/heltec_wireless_paper/platformio.ini | 23 ++++++++++++++++ 5 files changed, 77 insertions(+), 3 deletions(-) diff --git a/variants/gat562_mesh_evb_pro/platformio.ini b/variants/gat562_mesh_evb_pro/platformio.ini index cede9c97..f098237f 100644 --- a/variants/gat562_mesh_evb_pro/platformio.ini +++ b/variants/gat562_mesh_evb_pro/platformio.ini @@ -5,7 +5,7 @@ board_check = true build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I variants/gat562_mesh_evb_pro - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D RADIO_CLASS=CustomSX1262 diff --git a/variants/gat562_mesh_watch13/platformio.ini b/variants/gat562_mesh_watch13/platformio.ini index ef30829d..f34f0167 100644 --- a/variants/gat562_mesh_watch13/platformio.ini +++ b/variants/gat562_mesh_watch13/platformio.ini @@ -8,7 +8,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/gat562_mesh_watch13 -D RAK_4631 -D RAK_BOARD - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D PIN_OLED_RESET=-1 diff --git a/variants/heltec_t096/platformio.ini b/variants/heltec_t096/platformio.ini index 19b05f3c..00cc5d10 100644 --- a/variants/heltec_t096/platformio.ini +++ b/variants/heltec_t096/platformio.ini @@ -9,7 +9,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/heltec_t096 -I src/helpers/ui -D HELTEC_T096 - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D P_LORA_DIO_1=21 -D P_LORA_NSS=5 -D P_LORA_RESET=16 @@ -126,6 +126,30 @@ lib_deps = ${Heltec_t096.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_t096_companion_radio_ble_femoff] +extends = Heltec_t096 +board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld +board_upload.maximum_size = 712704 +build_flags = + ${Heltec_t096.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D ENV_INCLUDE_GPS=1 ; enable the GPS page in UI +; -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + -D RADIO_FEM_RXGAIN=0 ; undefined (default on), 1=on, 0=off +build_src_filter = ${Heltec_t096.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_t096.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:Heltec_t096_companion_radio_usb] extends = Heltec_t096 board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld diff --git a/variants/heltec_tracker_v2/platformio.ini b/variants/heltec_tracker_v2/platformio.ini index 956b1ec7..f7b87133 100644 --- a/variants/heltec_tracker_v2/platformio.ini +++ b/variants/heltec_tracker_v2/platformio.ini @@ -176,6 +176,33 @@ lib_deps = ${Heltec_tracker_v2.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:heltec_tracker_v2_companion_radio_ble_ps] +extends = Heltec_tracker_v2 +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${Heltec_tracker_v2.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D DISPLAY_CLASS=ST7735Display + -D BLE_PIN_CODE=123456 ; dynamic, random PIN + -D AUTO_SHUTDOWN_MILLIVOLTS=3400 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 + ; -D BLE_DEBUG_LOGGING=1 + ; -D MESH_PACKET_LOGGING=1 + ; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_tracker_v2.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_tracker_v2.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:heltec_tracker_v2_companion_radio_wifi] extends = Heltec_tracker_v2 build_flags = diff --git a/variants/heltec_wireless_paper/platformio.ini b/variants/heltec_wireless_paper/platformio.ini index c6fe657d..2e4c4c9d 100644 --- a/variants/heltec_wireless_paper/platformio.ini +++ b/variants/heltec_wireless_paper/platformio.ini @@ -64,6 +64,29 @@ lib_deps = densaugeo/base64 @ ~1.4.0 bakercp/CRC32 @ ^2.0.0 +[env:Heltec_Wireless_Paper_companion_radio_ble_ps] +extends = Heltec_Wireless_Paper_base +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${Heltec_Wireless_Paper_base.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D DISPLAY_CLASS=E213Display + -D BLE_PIN_CODE=123456 ; dynamic, random PIN + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +build_src_filter = ${Heltec_Wireless_Paper_base.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_Wireless_Paper_base.lib_deps} + densaugeo/base64 @ ~1.4.0 + bakercp/CRC32 @ ^2.0.0 + [env:Heltec_Wireless_Paper_companion_radio_usb] extends = Heltec_Wireless_Paper_base build_flags = From 517d3ed6438b9c38375e6b6e345ca1773f47d3be Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Wed, 6 May 2026 12:40:52 +0700 Subject: [PATCH 038/214] Added isReadBusy for NRF52 BLE --- src/helpers/nrf52/SerialBLEInterface.cpp | 4 ++++ src/helpers/nrf52/SerialBLEInterface.h | 1 + 2 files changed, 5 insertions(+) diff --git a/src/helpers/nrf52/SerialBLEInterface.cpp b/src/helpers/nrf52/SerialBLEInterface.cpp index 75a4e3b0..a846e744 100644 --- a/src/helpers/nrf52/SerialBLEInterface.cpp +++ b/src/helpers/nrf52/SerialBLEInterface.cpp @@ -401,6 +401,10 @@ bool SerialBLEInterface::isConnected() const { return _isDeviceConnected && Bluefruit.connected() > 0; } +bool SerialBLEInterface::isReadBusy() const { + return (recv_queue_len > 0); +} + bool SerialBLEInterface::isWriteBusy() const { return send_queue_len >= (FRAME_QUEUE_SIZE * 2 / 3); } diff --git a/src/helpers/nrf52/SerialBLEInterface.h b/src/helpers/nrf52/SerialBLEInterface.h index de103054..d3cc5055 100644 --- a/src/helpers/nrf52/SerialBLEInterface.h +++ b/src/helpers/nrf52/SerialBLEInterface.h @@ -66,6 +66,7 @@ public: void disable() override; bool isEnabled() const override { return _isEnabled; } bool isConnected() const override; + bool isReadBusy() const override; bool isWriteBusy() const override; size_t writeFrame(const uint8_t src[], size_t len) override; size_t checkRecvFrame(uint8_t dest[]) override; From 3281ea2924943a368e38b04aede041e7e3b6ca1a Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Wed, 6 May 2026 15:07:59 +0700 Subject: [PATCH 039/214] To drift forward less than 7s/day (3.5 minutes/month) for ESP32-based repeaters with Power Saving. --- examples/simple_repeater/main.cpp | 2 +- src/helpers/CommonCLI.cpp | 2 +- src/helpers/ESP32Board.h | 68 ++----------------------------- 3 files changed, 5 insertions(+), 67 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index b3fbbe5b..17a3c192 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -155,7 +155,7 @@ void loop() { board.sleep(1800); // nrf ignores seconds param, sleeps whenever possible #else if (the_mesh.millisHasNowPassed(POWERSAVING_FIRSTSLEEP_SECS * 1000)) { // To check if it is time to sleep - board.sleep(1800); // Sleep. Wake up after 30 minutes or when receiving a LoRa packet + board.sleep(30); // Sleep. Wake up after some seconds or when receiving a LoRa packet } #endif } diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 1322a36c..20fd13f8 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -246,7 +246,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else if (memcmp(command, "clock", 5) == 0) { uint32_t now = getRTCClock()->getCurrentTime(); DateTime dt = DateTime(now); - sprintf(reply, "%02d:%02d - %d/%d/%d UTC", dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year()); + sprintf(reply, "%02d:%02d:%02d - %d/%d/%d UTC", dt.hour(), dt.minute(), dt.second(), dt.day(), dt.month(), dt.year()); } else if (memcmp(command, "time ", 5) == 0) { // set time (to epoch seconds) uint32_t secs = _atoi(&command[5]); uint32_t curr = getRTCClock()->getCurrentTime(); diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index ee874ff0..fe986593 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -24,7 +24,7 @@ protected: public: void begin() { // for future use, sub-classes SHOULD call this from their begin() - startup_reason = BD_STARTUP_NORMAL; + startup_reason = BD_STARTUP_NORMAL; #ifdef ESP32_CPU_FREQ setCpuFrequencyMhz(ESP32_CPU_FREQ); @@ -47,7 +47,7 @@ public: #endif #else Wire.begin(); - #endif + #endif } // Temperature from ESP32 MCU @@ -66,50 +66,6 @@ public: return P_LORA_DIO_1; // default for SX1262 } - // 27 mins drift in 28 days and 40 mins = 669 ppm - const int64_t MICROSECONDS_PER_SECOND_DRIFT = 669; - const int64_t DRIFT_THRESHOLD_US = 60000000LL; // 1 minute in microseconds - - void applyTimeTrim() { - static int64_t last_trim_us = 0; - static int64_t accumulated_drift_us = 0; - - struct timeval tv; - gettimeofday(&tv, NULL); - int64_t now_us = (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec; - - if (last_trim_us == 0) { - last_trim_us = now_us; - return; - } - - // Calculate elapsed time - int64_t elapsed_us = now_us - last_trim_us; - - // Add this interval's drift to accumulated drift - accumulated_drift_us += (elapsed_us * MICROSECONDS_PER_SECOND_DRIFT) / 1000000LL; - - // Only trim when accumulated drift exceeds threshold - if (accumulated_drift_us >= DRIFT_THRESHOLD_US) { - // Calculate full seconds to trim - uint32_t seconds_to_trim = (uint32_t)(accumulated_drift_us / 1000000LL); - - // Set back the trimmed time to RTC - tv.tv_sec -= (time_t)seconds_to_trim; - settimeofday(&tv, NULL); - - // Keep the fractional remainder (anything less than a full second) - accumulated_drift_us %= 1000000LL; - - // Save for next trim - gettimeofday(&tv, NULL); - last_trim_us = (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec; - } else { - // Skip trimming - last_trim_us = now_us; - } - } - void sleep(uint32_t secs) override { // Skip if not allow to sleep if (inhibit_sleep) { @@ -117,23 +73,8 @@ public: return; } - // Use more accurate clock in sleep -#if SOC_RTC_SLOW_CLK_SUPPORT_RC_FAST_D256 - if (rtc_clk_slow_src_get() != SOC_RTC_SLOW_CLK_SRC_RC_FAST) { - - // Switch slow clock source to RC_FAST / 256 (~31.25 kHz) - rtc_clk_slow_src_set(SOC_RTC_SLOW_CLK_SRC_RC_FAST); - - // Calibrate slow clock - esp_clk_slow_boot_cal(1024); - } -#endif - - // Keep RTC 8M during sleep - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC8M, ESP_PD_OPTION_ON); - // Set GPIO wakeup - gpio_num_t wakeupPin = (gpio_num_t)getIRQGpio(); + gpio_num_t wakeupPin = (gpio_num_t)getIRQGpio(); // Configure timer wakeup if (secs > 0) { @@ -163,9 +104,6 @@ public: // Enable CPU interrupt servicing portEXIT_CRITICAL(&sleepMux); - - // Apply the software trim to correct for RC drift - applyTimeTrim(); } uint8_t getStartupReason() const override { return startup_reason; } From 674beb0e473f2954067d25d3ec309d70fd314ba5 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Thu, 7 May 2026 01:01:52 -0700 Subject: [PATCH 040/214] Improve repeater direct retry handling --- docs/cli_commands.md | 61 +++++-- examples/simple_repeater/MyMesh.cpp | 259 +++++++++++++++++----------- examples/simple_repeater/MyMesh.h | 8 +- src/Mesh.cpp | 113 +++++++----- src/Mesh.h | 6 +- src/helpers/CommonCLI.cpp | 178 +++++++++++++++++-- src/helpers/CommonCLI.h | 21 +++ src/helpers/SimpleMeshTables.h | 107 ++---------- 8 files changed, 491 insertions(+), 262 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index c771d379..f9c08259 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -128,8 +128,10 @@ This document provides an overview of CLI commands that can be sent to MeshCore - `page`: 1-based page number **Notes:** -- `set` is rejected when the prefix already exists in neighbors. -- Rows are shown newest-first. +- `set` stores or updates the prefix in the recent repeater table. +- Rows are sorted by prefix width (3-byte, 2-byte, 1-byte), then SNR descending. +- A full direct retry failure lowers the stored SNR by `0.25 dB`. +- If a full failure has no row yet, it first seeds the row at the active retry cutoff + `2.5 dB`, then applies the `0.25 dB` penalty. - Serial CLI prints all rows (no paging). - Over LoRa remote CLI, page size is fixed at `4` rows; choose page with `get recent.repeater `. @@ -534,11 +536,13 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Parameters:** - `value`: Direct transmit delay factor (0-2) -**Default:** `0.2` +**Default:** `0.3` + +**Note:** Direct retry waits include the same airtime-based randomized delay calculation as direct retransmits, so this factor also controls retry echo windows. --- -#### View or change whether direct retries can fall back to the recently-heard repeater list +#### View or change whether direct retries use the recent repeater blacklist **Usage:** - `get direct.retry.heard` - `set direct.retry.heard ` @@ -548,7 +552,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Default:** `on` -**Note:** When enabled, a repeater can use recently-heard non-duplicate repeater prefixes as a fallback for direct retry eligibility when no suitable neighbor entry is available. +**Note:** When enabled, the recent repeater table is the only direct retry eligibility gate. Prefixes missing from the table are assumed reachable; prefixes in the table below the active SNR gate are blocked. Neighbor data is not used. --- @@ -558,11 +562,30 @@ This document provides an overview of CLI commands that can be sent to MeshCore - `set direct.retry.margin ` **Parameters:** -- `value`: Margin in dB above the SF-specific receive floor (minimum `0`, maximum `40`, quarter-dB precision, default `2.5`) +- `value`: Rooftop preset margin in dB above the SF-specific receive floor (minimum `0`, maximum `40`, quarter-dB precision, default `5.0`) -**Default:** `2.5` +**Default:** `5.0` -**Note:** The retry gate uses the active SF floor of `SF5=-2.5`, `SF6=-5`, `SF7=-7.5`, `SF8=-10`, `SF9=-12.5`, `SF10=-15`, `SF11=-17.5`, `SF12=-20`, then adds this margin. +**Note:** `get direct.retry.margin` returns the active preset's effective margin. The retry gate uses the active SF floor of `SF5=-2.5`, `SF6=-5`, `SF7=-7.5`, `SF8=-10`, `SF9=-12.5`, `SF10=-15`, `SF11=-17.5`, `SF12=-20`, then adds this margin. + +--- + +#### View or change the direct retry timing preset +**Usage:** +- `get direct.retry.preset` +- `set direct.retry.preset ` + +**Parameters:** +- `value`: `infra`|`rooftop`|`mobile` or `0`|`1`|`2` + +**Default:** `rooftop` (`1`) + +**Presets:** +- `infra` (`0`): `275 ms` base wait, `4` retries, `150 ms` added per retry, SNR gate is SF floor + `15 dB` +- `rooftop` (`1`): `175 ms` base wait, `15` retries, `100 ms` added per retry, SNR gate is SF floor + `5 dB` +- `mobile` (`2`): `175 ms` base wait, `15` retries, `50 ms` added per retry, SNR gate is the SF floor + +**Note:** Selecting a preset copies those values into the retry settings. You can refine `direct.retry.margin`, `direct.retry.count`, `direct.retry.base`, or `direct.retry.step` afterward. Retry delay is `direct.txdelay` jitter + base wait + packet-length airtime wait + per-attempt step. --- @@ -572,10 +595,12 @@ This document provides an overview of CLI commands that can be sent to MeshCore - `set direct.retry.count ` **Parameters:** -- `value`: Retry attempts after initial TX (`1`-`15`) +- `value`: Maximum retry attempts after initial TX (`1`-`15`) **Default:** `15` +**Note:** The effective value is capped by total direct path length: paths of `3` hops or less use at most `8` retries, `4` hops use at most `12`, and `5+` hops use at most `15`. A queued resend is canceled early when the next-hop echo is heard. + --- #### View or change the base direct retry wait (milliseconds) @@ -586,9 +611,23 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Parameters:** - `value`: Base wait in milliseconds (`10`-`5000`) -**Default:** `200` +**Default:** `175` -**Note:** The actual first retry wait is `base + computed_echo_wait_from_live_phy`. +**Note:** The configured base is added to packet-length airtime and `direct.txdelay` jitter. Preset defaults are already reduced to account for the added `direct.txdelay` component. + +--- + +#### View or change the direct retry per-attempt add time (milliseconds) +**Usage:** +- `get direct.retry.step` +- `set direct.retry.step ` + +**Parameters:** +- `value`: Milliseconds added per retry attempt (`0`-`5000`) + +**Default:** `100` + +**Note:** This controls the linear add after the first retry wait. For example, `base=300` and `step=150` adds `0`, `150`, `300`, ... ms across retry attempts. --- diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 3e3a16ae..6b3e8d6a 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -63,29 +63,6 @@ #define LAZY_CONTACTS_WRITE_DELAY 5000 -const NeighbourInfo* MyMesh::findNeighbourByHash(const uint8_t* hash, uint8_t hash_len) const { -#if MAX_NEIGHBOURS - for (int i = 0; i < MAX_NEIGHBOURS; i++) { - if (neighbours[i].heard_timestamp > 0 && neighbours[i].id.isHashMatch(hash, hash_len)) { - return &neighbours[i]; - } - } -#else - (void)hash; - (void)hash_len; -#endif - return NULL; -} - -bool MyMesh::allowRecentRepeaterPrefixStore(const uint8_t* prefix, uint8_t prefix_len, void* ctx) { - if (ctx == NULL || prefix == NULL || prefix_len == 0) { - return true; - } - - const MyMesh* self = (const MyMesh*) ctx; - return self->findNeighbourByHash(prefix, prefix_len) == NULL; -} - static void formatRecentRepeaterPrefix(const SimpleMeshTables::RecentRepeaterInfo* info, char* out, size_t out_len) { if (out == NULL || out_len == 0) { return; @@ -124,6 +101,45 @@ static void formatRecentRepeaterSnrX4(int8_t snr_x4, char* out, size_t out_len) } } +static int buildSortedRecentRepeaterView(SimpleMeshTables* tables, + const SimpleMeshTables::RecentRepeaterInfo** out, + int out_cap) { + if (tables == NULL || out == NULL || out_cap <= 0) { + return 0; + } + + int total = tables->getRecentRepeaterCount(); + if (total > out_cap) { + total = out_cap; + } + + int count = 0; + for (int i = 0; i < total; i++) { + const auto* info = tables->getRecentRepeaterNewestByIdx(i); + if (info != NULL) { + out[count++] = info; + } + } + + std::stable_sort(out, out + count, [](const SimpleMeshTables::RecentRepeaterInfo* a, + const SimpleMeshTables::RecentRepeaterInfo* b) { + uint8_t a_len = a->prefix_len; + uint8_t b_len = b->prefix_len; + if (a_len > MAX_ROUTE_HASH_BYTES) a_len = MAX_ROUTE_HASH_BYTES; + if (b_len > MAX_ROUTE_HASH_BYTES) b_len = MAX_ROUTE_HASH_BYTES; + + if (a_len != b_len) { + return a_len > b_len; // 3-byte first, then 2-byte, then 1-byte + } + if (a->snr_x4 != b->snr_x4) { + return a->snr_x4 > b->snr_x4; // highest SNR first within each prefix size + } + return false; // keep original newest-first order for ties + }); + + return count; +} + static uint8_t decodeTraceHashSize(uint8_t flags, uint8_t route_bytes) { uint8_t code = flags & 0x03; uint8_t size_pow2 = (uint8_t)(1U << code); // legacy TRACE interpretation @@ -641,7 +657,7 @@ void MyMesh::logTxFail(mesh::Packet *pkt, int len) { } } -void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis) { +void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) { if (packet == NULL) { return; } @@ -656,92 +672,103 @@ void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, u mesh::Utils::toHex(next_hop_hex, prefix, prefix_len); } const char* next_hop = (has_prefix && prefix_len > 0) ? next_hop_hex : "unknown"; + char hop_text[24]; + if (packet->isRouteDirect() && packet->getPayloadType() == PAYLOAD_TYPE_TRACE && packet->payload_len >= 9) { + uint8_t route_bytes = packet->payload_len - 9; + uint8_t hash_size = decodeTraceHashSize(packet->payload[8], route_bytes); + uint8_t total_hops = (hash_size > 0) ? (route_bytes / hash_size) : 0; + if (total_hops > 0) { + snprintf(hop_text, sizeof(hop_text), "%u/%u", (unsigned int)packet->path_len, (unsigned int)total_hops); + } else { + snprintf(hop_text, sizeof(hop_text), "unknown"); + } + } else if (packet->isRouteDirect()) { + snprintf(hop_text, sizeof(hop_text), "remaining:%u", (unsigned int)packet->getPathHashCount()); + } else { + snprintf(hop_text, sizeof(hop_text), "unknown"); + } + // Direct-retry events are TX-side and usually have no trustworthy RX SNR. // Cap event SNR at fixed SF floor + 10 dB so trace-start retries can't inflate table SNR. uint8_t sf = constrain(active_sf, (uint8_t)5, (uint8_t)12); int16_t fallback_snr_x4_raw = direct_retry_floor_x4[sf - 5] + 40; int8_t fallback_snr_x4 = (int8_t)constrain(fallback_snr_x4_raw, -128, 127); - bool is_success_event = (strcmp(event, "good") == 0 || strcmp(event, "canceled_echo") == 0); + bool is_success_event = strcmp(event, "good") == 0; + bool updates_quality = strcmp(event, "good") == 0; int8_t retry_event_snr_x4; - const char* snr_src; - if (is_success_event && packet->_snr != 0) { - // On success, Mesh.cpp injects echo RX SNR for TRACE retries. + if (is_success_event) { + // On success, Mesh.cpp injects echo RX SNR for the downstream retry target. retry_event_snr_x4 = packet->_snr; - snr_src = "packet"; } else if (existing != NULL) { retry_event_snr_x4 = existing->snr_x4; - snr_src = "table"; } else { retry_event_snr_x4 = fallback_snr_x4; - snr_src = "fallback"; } - char snr_used_text[12]; char snr_pkt_text[12]; char snr_table_text[12]; - snprintf(snr_used_text, sizeof(snr_used_text), "%s", StrHelper::ftoa(((float)retry_event_snr_x4) / 4.0f)); + + if (has_prefix && updates_quality) { + // Refresh SNR only once per successful echo/progress event, not on queued/resent bookkeeping. + tables->setRecentRepeater(prefix, prefix_len, retry_event_snr_x4, false, true); + } + + if (strcmp(event, "failed_all_tries") == 0) { + if (has_prefix) { + if (existing == NULL) { + int16_t seed_snr_x4 = (int16_t)getDirectRetryMinSNRX4() + 10; // +2.5 dB over the active retry cutoff. + tables->setRecentRepeater(prefix, prefix_len, (int8_t)constrain(seed_snr_x4, -128, 127), false, true); + } + // SNR is stored in quarter-dB units, so 1 lowers quality by 0.25 dB. + tables->decrementRecentRepeaterSnrX4(prefix, prefix_len, 1); + } + } + snprintf(snr_pkt_text, sizeof(snr_pkt_text), "%s", StrHelper::ftoa(((float)packet->_snr) / 4.0f)); - if (existing != NULL) { - snprintf(snr_table_text, sizeof(snr_table_text), "%s", StrHelper::ftoa(((float)existing->snr_x4) / 4.0f)); + const auto* log_existing = has_prefix ? tables->findRecentRepeaterByHash(prefix, prefix_len) : NULL; + if (log_existing != NULL) { + snprintf(snr_table_text, sizeof(snr_table_text), "%s", StrHelper::ftoa(((float)log_existing->snr_x4) / 4.0f)); } else { snprintf(snr_table_text, sizeof(snr_table_text), "na"); } - if (has_prefix && is_success_event) { - // Refresh SNR only on successful echo/progress events, not on queued/resent bookkeeping. - tables->setRecentRepeater(prefix, prefix_len, retry_event_snr_x4, false, true); + const char* time_label = "time_ms"; + if (strcmp(event, "queued") == 0 || strcmp(event, "dropped_queue_full") == 0) { + time_label = "wait_ms"; + } else if (strcmp(event, "resent") == 0 || strcmp(event, "failed_all_tries") == 0 || strcmp(event, "failure") == 0) { + time_label = "elapsed_ms"; + } else if (strcmp(event, "good") == 0) { + time_label = "echo_ms"; } - if (strcmp(event, "resent") == 0) { - if (has_prefix) { - // Retry stats should be visible even when the prefix was never learned into recent.repeater. - tables->incrementRecentRepeaterRetryCount(prefix, prefix_len, true, retry_event_snr_x4, true); - } - } else if (strcmp(event, "failed_all_tries") == 0) { - if (has_prefix) { - // A failed_all_tries event means all retry attempts for this packet failed. - // Count failures by retry-attempts so fail% reflects failed retries, not just failed sessions. - uint8_t give_up_retries = getDirectRetryMaxAttempts(packet); - uint8_t failed_retries = give_up_retries; - if (failed_retries < 1) { - failed_retries = 1; - } - for (uint8_t i = 0; i < failed_retries; i++) { - tables->incrementRecentRepeaterFailCount(prefix, prefix_len, true, retry_event_snr_x4, true); - } - if (failed_retries >= give_up_retries && give_up_retries > 0) { - // If all configured retry attempts still fail, slightly degrade stored path quality. - tables->decrementRecentRepeaterSnrX4(prefix, prefix_len, 1); - } - } - } - - MESH_DEBUG_PRINTLN("%s direct retry %s (type=%d, route=%s, payload_len=%d, next_hop=%s, snr=%s, snr_src=%s, pkt_snr=%s, table_snr=%s, delay=%lu)", + MESH_DEBUG_PRINTLN("%s direct retry %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%s, next_hop=%s, pkt=%s, tbl=%s, %s=%lu)", getLogDateTime(), event, + (unsigned int)retry_attempt, (uint32_t)packet->getPayloadType(), packet->isRouteDirect() ? "D" : "F", (uint32_t)packet->payload_len, + hop_text, next_hop, - snr_used_text, - snr_src, snr_pkt_text, snr_table_text, + time_label, (unsigned long)delay_millis); if (_logging) { File f = openAppend(PACKET_LOG_FILE); if (f) { f.print(getLogDateTime()); - f.printf(": DIRECT RETRY %s (type=%d, route=%s, payload_len=%d, next_hop=%s, snr=%s, snr_src=%s, pkt_snr=%s, table_snr=%s, delay=%lu)\n", + f.printf(": DIRECT RETRY %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%s, next_hop=%s, pkt=%s, tbl=%s, %s=%lu)\n", event, + (unsigned int)retry_attempt, (uint32_t)packet->getPayloadType(), packet->isRouteDirect() ? "D" : "F", (uint32_t)packet->payload_len, + hop_text, next_hop, - snr_used_text, - snr_src, snr_pkt_text, snr_table_text, + time_label, (unsigned long)delay_millis); f.close(); } @@ -764,7 +791,8 @@ uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) { int8_t MyMesh::getDirectRetryMinSNRX4() const { // Use the live SF so `tempradio` changes immediately affect the retry threshold. uint8_t sf = constrain(active_sf, (uint8_t)5, (uint8_t)12); - int16_t threshold = direct_retry_floor_x4[sf - 5] + (int16_t)_prefs.direct_retry_snr_margin_db; + int16_t margin_x4 = (int16_t)_prefs.direct_retry_snr_margin_db; + int16_t threshold = direct_retry_floor_x4[sf - 5] + margin_x4; return (int8_t)constrain(threshold, -128, 127); } bool MyMesh::extractDirectRetryPrefix(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const { @@ -808,18 +836,30 @@ bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_ho int8_t min_snr_x4 = getDirectRetryMinSNRX4(); if (_prefs.direct_retry_recent_enabled) { - // Prefer the 64-entry recent-prefix cache first, then fall back to neighbours. const auto* recent = ((const SimpleMeshTables *)getTables())->findRecentRepeaterByHash(next_hop_hash, next_hop_hash_len); - if (recent != NULL && recent->snr_x4 >= min_snr_x4) { - return true; - } + return recent == NULL || recent->snr_x4 >= min_snr_x4; } - const NeighbourInfo* neighbour = findNeighbourByHash(next_hop_hash, next_hop_hash_len); - return neighbour != NULL && neighbour->snr >= min_snr_x4; + return true; +} +uint8_t MyMesh::getDirectRetryPreset() const { + if (_prefs.direct_retry_preset <= DIRECT_RETRY_PRESET_MOBILE) { + return _prefs.direct_retry_preset; + } + return DIRECT_RETRY_PRESET_ROOFTOP; +} +uint8_t MyMesh::getDirectRetryConfiguredMaxAttempts() const { + return constrain(_prefs.direct_retry_attempts, (uint8_t)1, (uint8_t)15); +} +uint32_t MyMesh::getDirectRetryAttemptStepMillis() const { + return constrain((uint32_t)_prefs.direct_retry_step_ms, (uint32_t)0, (uint32_t)5000); } uint32_t MyMesh::getDirectRetryEchoDelay(const mesh::Packet* packet) const { uint32_t base_wait_millis = constrain((uint32_t)_prefs.direct_retry_base_ms, (uint32_t)10, (uint32_t)5000); + if (packet == NULL) { + return base_wait_millis; + } + // Approximate LoRa line rate in kilobits/sec from the live radio params the repeater is using now. float kbps = (((float) active_sf) * active_bw * ((float) active_cr)) / ((float) (1UL << active_sf)); if (kbps <= 0.0f) { @@ -832,8 +872,36 @@ uint32_t MyMesh::getDirectRetryEchoDelay(const mesh::Packet* packet) const { return base_wait_millis + scaled_wait_millis; } uint8_t MyMesh::getDirectRetryMaxAttempts(const mesh::Packet* packet) const { - (void)packet; - return constrain(_prefs.direct_retry_attempts, (uint8_t)1, (uint8_t)15); + uint8_t configured_attempts = getDirectRetryConfiguredMaxAttempts(); + uint8_t total_hops = 0; + + if (packet != NULL) { + if (packet->isRouteDirect() && packet->getPayloadType() == PAYLOAD_TYPE_TRACE && packet->payload_len >= 9) { + uint8_t route_bytes = packet->payload_len - 9; + uint8_t hash_size = decodeTraceHashSize(packet->payload[8], route_bytes); + if (hash_size > 0) { + total_hops = (uint8_t)(route_bytes / hash_size); + } + } else { + total_hops = packet->getPathHashCount(); + } + } + + uint8_t path_cap = 15; + if (total_hops <= 3) { + path_cap = 8; + } else if (total_hops == 4) { + path_cap = 12; + } + + return configured_attempts < path_cap ? configured_attempts : path_cap; +} +uint32_t MyMesh::getDirectRetryAttemptDelay(const mesh::Packet* packet, uint8_t attempt_idx) { + uint32_t retry_delay = getDirectRetryEchoDelay(packet) + ((uint32_t)attempt_idx * getDirectRetryAttemptStepMillis()); + if (packet == NULL) { + return retry_delay; + } + return getDirectRetransmitDelay(packet) + retry_delay; } bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) { @@ -1166,9 +1234,11 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.tx_delay_factor = 0.5f; // was 0.25f _prefs.direct_tx_delay_factor = 0.3f; // was 0.2 _prefs.direct_retry_recent_enabled = 1; - _prefs.direct_retry_snr_margin_db = 10; // 2.5 dB stored in x4 units - _prefs.direct_retry_attempts = 15; - _prefs.direct_retry_base_ms = 200; + _prefs.direct_retry_snr_margin_db = DIRECT_RETRY_ROOFTOP_MARGIN_X4; + _prefs.direct_retry_attempts = DIRECT_RETRY_ROOFTOP_COUNT; + _prefs.direct_retry_base_ms = DIRECT_RETRY_ROOFTOP_BASE_MS; + _prefs.direct_retry_step_ms = DIRECT_RETRY_ROOFTOP_STEP_MS; + _prefs.direct_retry_preset = DIRECT_RETRY_PRESET_ROOFTOP; StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); _prefs.node_lat = ADVERT_LAT; _prefs.node_lon = ADVERT_LON; @@ -1213,8 +1283,6 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc active_bw = _prefs.bw; active_sf = _prefs.sf; active_cr = _prefs.cr; - - ((SimpleMeshTables *)getTables())->setRecentRepeaterAllowFilter(&MyMesh::allowRecentRepeaterPrefixStore, this); } void MyMesh::begin(FILESYSTEM *fs) { @@ -1612,21 +1680,22 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply if (tables->setRecentRepeater(prefix, MAX_ROUTE_HASH_BYTES, (int8_t)snr_x4, true)) { strcpy(reply, "OK"); } else { - strcpy(reply, "Err - prefix is already in neighbors"); + strcpy(reply, "Err - unable to store prefix"); } } } } } else { - int total = tables->getRecentRepeaterCount(); + const SimpleMeshTables::RecentRepeaterInfo* sorted_recent[MAX_RECENT_REPEATERS]; + int total = buildSortedRecentRepeaterView(tables, sorted_recent, MAX_RECENT_REPEATERS); if (total <= 0) { strcpy(reply, "> none"); } else { if (sender_timestamp == 0) { // Serial CLI: print all entries (no paging). - Serial.printf("Recent repeater table (newest first, total=%d):\n", total); + Serial.printf("Recent repeater table (3-byte,2-byte,1-byte; SNR desc, total=%d):\n", total); for (int i = 0; i < total; i++) { - const auto* info = tables->getRecentRepeaterNewestByIdx(i); + const auto* info = sorted_recent[i]; if (info == NULL) { continue; } @@ -1635,18 +1704,9 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply formatRecentRepeaterPrefix(info, hex, sizeof(hex)); char snr_text[12]; formatRecentRepeaterSnrX4(info->snr_x4, snr_text, sizeof(snr_text)); - uint32_t fail_pct_x10 = 0; - if (info->retry_count > 0) { - fail_pct_x10 = (((uint32_t)info->fail_count * 1000UL) + (info->retry_count / 2U)) / (uint32_t)info->retry_count; - } - Serial.printf("%03d: %s,%s,fp=%lu.%01lu%%,r=%u,f=%u%s\n", - i + 1, + Serial.printf("%s,%s%s\n", hex, snr_text, - (unsigned long)(fail_pct_x10 / 10U), - (unsigned long)(fail_pct_x10 % 10U), - (uint32_t)info->retry_count, - (uint32_t)info->fail_count, info->snr_locked ? ",l" : ""); } sprintf(reply, "> n=%d/%d", total, total); @@ -1692,7 +1752,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply for (int i = 0; i < limit; i++) { int idx = offset + i; - const auto* info = tables->getRecentRepeaterNewestByIdx(idx); + const auto* info = sorted_recent[idx]; if (info == NULL) { continue; } @@ -1707,12 +1767,9 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply formatRecentRepeaterSnrX4(info->snr_x4, snr_text, sizeof(snr_text)); int n = snprintf(reply + written, 160 - written, - "\n%03d:%s,%s,r=%u,f=%u%s", - idx + 1, + "\n%s,%s%s", hex, snr_text, - (uint32_t)info->retry_count, - (uint32_t)info->fail_count, info->snr_locked ? ",l" : ""); if (n < 0 || n >= (160 - written)) { truncated = true; diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index b2626e60..1a2f505c 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -122,10 +122,11 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { ESPNowBridge bridge; #endif - const NeighbourInfo* findNeighbourByHash(const uint8_t* hash, uint8_t hash_len) const; bool extractDirectRetryPrefix(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const; - static bool allowRecentRepeaterPrefixStore(const uint8_t* prefix, uint8_t prefix_len, void* ctx); int8_t getDirectRetryMinSNRX4() const; + uint8_t getDirectRetryPreset() const; + uint8_t getDirectRetryConfiguredMaxAttempts() const; + uint32_t getDirectRetryAttemptStepMillis() const; void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr); uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood); uint8_t handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data); @@ -156,7 +157,8 @@ protected: bool allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const override; uint32_t getDirectRetryEchoDelay(const mesh::Packet* packet) const override; uint8_t getDirectRetryMaxAttempts(const mesh::Packet* packet) const override; - void onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis) override; + uint32_t getDirectRetryAttemptDelay(const mesh::Packet* packet, uint8_t attempt_idx) override; + void onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) override; int getInterferenceThreshold() const override { return _prefs.interference_threshold; diff --git a/src/Mesh.cpp b/src/Mesh.cpp index f07484d6..6c9c8080 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -6,10 +6,32 @@ namespace mesh { static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT = 15; static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX = 15; +static uint8_t decodeTraceHashSize(uint8_t flags, uint8_t route_bytes) { + uint8_t code = flags & 0x03; + uint8_t size_pow2 = (uint8_t)(1U << code); // legacy TRACE interpretation + uint8_t size_linear = (uint8_t)(code + 1U); // packed-size interpretation (1..4) + + bool pow2_ok = size_pow2 > 0 && (route_bytes % size_pow2) == 0; + bool linear_ok = size_linear > 0 && (route_bytes % size_linear) == 0; + + if (pow2_ok && !linear_ok) { + return size_pow2; + } + if (linear_ok && !pow2_ok) { + return size_linear; + } + if (pow2_ok) { + return size_pow2; + } + return size_linear; +} + void Mesh::begin() { for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { _direct_retries[i].packet = NULL; _direct_retries[i].trigger_packet = NULL; + _direct_retries[i].retry_started_at = 0; + _direct_retries[i].echo_wait_started_at = 0; _direct_retries[i].retry_at = 0; _direct_retries[i].retry_delay = 0; _direct_retries[i].retry_attempts_sent = 0; @@ -60,7 +82,7 @@ uint32_t Mesh::getDirectRetryEchoDelay(const Packet* packet) const { uint8_t Mesh::getDirectRetryMaxAttempts(const Packet* packet) const { return DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT; } -uint32_t Mesh::getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx) const { +uint32_t Mesh::getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx) { uint32_t base = getDirectRetryEchoDelay(packet); // Keep the historical linear spacing while allowing the base wait to vary by platform/profile. return base + ((uint32_t)attempt_idx * 100UL); @@ -102,13 +124,14 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { uint32_t auth_code; memcpy(&auth_code, &pkt->payload[i], 4); i += 4; uint8_t flags = pkt->payload[i++]; - uint8_t path_sz = flags & 0x03; // NEW v1.11+: lower 2 bits is path hash size - uint8_t len = pkt->payload_len - i; - uint8_t offset = pkt->path_len << path_sz; + uint8_t hash_size = decodeTraceHashSize(flags, len); + uint16_t offset = (uint16_t)pkt->path_len * (uint16_t)hash_size; if (offset >= len) { // TRACE has reached end of given path onTraceRecv(pkt, trace_tag, auth_code, flags, pkt->path, &pkt->payload[i], len); - } else if (self_id.isHashMatch(&pkt->payload[i + offset], 1 << path_sz) && allowPacketForward(pkt) && !_tables->hasSeen(pkt)) { + } else if (hash_size > 0 && offset + hash_size <= len + && self_id.isHashMatch(&pkt->payload[i + offset], hash_size) + && allowPacketForward(pkt) && !_tables->hasSeen(pkt)) { // append SNR (Not hash!) pkt->path[pkt->path_len++] = (int8_t) (pkt->getSNR()*4); @@ -449,6 +472,8 @@ void Mesh::routeDirectRecvAcks(Packet* packet, uint32_t delay_millis) { void Mesh::clearDirectRetrySlot(int idx) { _direct_retries[idx].packet = NULL; _direct_retries[idx].trigger_packet = NULL; + _direct_retries[idx].retry_started_at = 0; + _direct_retries[idx].echo_wait_started_at = 0; _direct_retries[idx].retry_at = 0; _direct_retries[idx].retry_delay = 0; _direct_retries[idx].retry_attempts_sent = 0; @@ -490,13 +515,16 @@ bool Mesh::cancelDirectRetryOnEcho(const Packet* packet) { continue; } + int8_t echo_snr_x4 = packet->_snr; if (_direct_retries[i].queued) { - if (_direct_retries[i].expect_path_growth - && _direct_retries[i].packet != NULL - && _direct_retries[i].progress_marker < packet->path_len) { - // For retry-good quality, use the received echo packet SNR (return-link quality). - _direct_retries[i].packet->_snr = packet->_snr; + if (_direct_retries[i].packet != NULL) { + // Success quality comes from the received downstream echo, not the original upstream RX. + _direct_retries[i].packet->_snr = echo_snr_x4; } + uint32_t echo_millis = _direct_retries[i].echo_wait_started_at == 0 + ? 0 + : (uint32_t)(_ms->getMillis() - _direct_retries[i].echo_wait_started_at); + onDirectRetryEvent("good", _direct_retries[i].packet, echo_millis, _direct_retries[i].retry_attempts_sent + 1); for (int j = 0; j < _mgr->getOutboundTotal(); j++) { if (_mgr->getOutboundByIdx(j) == _direct_retries[i].packet) { Packet* pending = _mgr->removeOutboundByIdx(j); @@ -506,18 +534,15 @@ bool Mesh::cancelDirectRetryOnEcho(const Packet* packet) { break; } } - onDirectRetryEvent("canceled_echo", _direct_retries[i].packet, 0); - onDirectRetryEvent("good", _direct_retries[i].packet, 0); clearDirectRetrySlot(i); } else { - if (_direct_retries[i].expect_path_growth - && _direct_retries[i].trigger_packet != NULL - && _direct_retries[i].progress_marker < packet->path_len) { - // For retry-good quality, use the received echo packet SNR (return-link quality). - _direct_retries[i].trigger_packet->_snr = packet->_snr; + if (_direct_retries[i].trigger_packet != NULL) { + _direct_retries[i].trigger_packet->_snr = echo_snr_x4; } - onDirectRetryEvent("canceled_echo", _direct_retries[i].trigger_packet, 0); - onDirectRetryEvent("good", _direct_retries[i].trigger_packet, 0); + uint32_t echo_millis = _direct_retries[i].echo_wait_started_at == 0 + ? 0 + : (uint32_t)(_ms->getMillis() - _direct_retries[i].echo_wait_started_at); + onDirectRetryEvent("good", _direct_retries[i].trigger_packet, echo_millis, _direct_retries[i].retry_attempts_sent + 1); clearDirectRetrySlot(i); } cleared = true; @@ -535,7 +560,11 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { if (_direct_retries[i].queued) { if (_direct_retries[i].packet == packet) { // The retry packet itself just finished transmitting; Dispatcher will release it after this hook. - onDirectRetryEvent("resent", packet, 0); + uint32_t elapsed_millis = _direct_retries[i].retry_started_at == 0 + ? 0 + : (uint32_t)(_ms->getMillis() - _direct_retries[i].retry_started_at); + onDirectRetryEvent("resent", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent + 1); + _direct_retries[i].echo_wait_started_at = _ms->getMillis(); _direct_retries[i].retry_attempts_sent++; uint8_t max_attempts = getDirectRetryMaxAttempts(packet); if (max_attempts < 1) { @@ -544,16 +573,16 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { max_attempts = DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX; } if (_direct_retries[i].retry_attempts_sent >= max_attempts) { - onDirectRetryEvent("failed_all_tries", packet, 0); - onDirectRetryEvent("failure", packet, 0); + onDirectRetryEvent("failed_all_tries", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); + onDirectRetryEvent("failure", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); clearDirectRetrySlot(i); continue; } Packet* retry = obtainNewPacket(); if (retry == NULL) { - onDirectRetryEvent("dropped_no_packet", packet, 0); - onDirectRetryEvent("failure", packet, 0); + onDirectRetryEvent("dropped_no_packet", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent + 1); + onDirectRetryEvent("failure", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent + 1); clearDirectRetrySlot(i); continue; } @@ -565,10 +594,10 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { _direct_retries[i].packet = retry; _direct_retries[i].retry_delay = retry_delay; _direct_retries[i].retry_at = futureMillis(retry_delay); - onDirectRetryEvent("queued", retry, retry_delay); + onDirectRetryEvent("queued", retry, retry_delay, _direct_retries[i].retry_attempts_sent + 1); } else { - onDirectRetryEvent("dropped_queue_full", retry, retry_delay); - onDirectRetryEvent("failure", retry, 0); + onDirectRetryEvent("dropped_queue_full", retry, retry_delay, _direct_retries[i].retry_attempts_sent + 1); + onDirectRetryEvent("failure", retry, elapsed_millis, _direct_retries[i].retry_attempts_sent + 1); clearDirectRetrySlot(i); } } @@ -582,8 +611,8 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { // Allocate the retry packet only after TX-complete so busy repeaters do not reserve pool slots early. Packet* retry = obtainNewPacket(); if (retry == NULL) { - onDirectRetryEvent("dropped_no_packet", packet, _direct_retries[i].retry_delay); - onDirectRetryEvent("failure", packet, 0); + onDirectRetryEvent("dropped_no_packet", packet, _direct_retries[i].retry_delay, 1); + onDirectRetryEvent("failure", packet, 0, 1); clearDirectRetrySlot(i); continue; } @@ -593,14 +622,17 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { // Start the echo wait only after the initial direct transmission actually completed. sendPacket(retry, _direct_retries[i].priority, _direct_retries[i].retry_delay); if (isDirectRetryQueued(retry)) { + unsigned long now = _ms->getMillis(); _direct_retries[i].packet = retry; _direct_retries[i].trigger_packet = NULL; _direct_retries[i].queued = true; _direct_retries[i].retry_at = futureMillis(_direct_retries[i].retry_delay); - onDirectRetryEvent("queued", retry, _direct_retries[i].retry_delay); + _direct_retries[i].retry_started_at = now; + _direct_retries[i].echo_wait_started_at = now; + onDirectRetryEvent("queued", retry, _direct_retries[i].retry_delay, 1); } else { - onDirectRetryEvent("dropped_queue_full", retry, _direct_retries[i].retry_delay); - onDirectRetryEvent("failure", retry, 0); + onDirectRetryEvent("dropped_queue_full", retry, _direct_retries[i].retry_delay, 1); + onDirectRetryEvent("failure", retry, 0, 1); clearDirectRetrySlot(i); } } @@ -615,16 +647,16 @@ void Mesh::clearPendingDirectRetryOnSendFail(const Packet* packet) { if (_direct_retries[i].queued) { if (_direct_retries[i].packet == packet) { // The queued retry itself failed; Dispatcher will release it after this hook. - onDirectRetryEvent("dropped_send_fail", packet, 0); - onDirectRetryEvent("failure", packet, 0); + onDirectRetryEvent("dropped_send_fail", packet, 0, _direct_retries[i].retry_attempts_sent + 1); + onDirectRetryEvent("failure", packet, 0, _direct_retries[i].retry_attempts_sent + 1); clearDirectRetrySlot(i); } continue; } if (_direct_retries[i].trigger_packet == packet) { - onDirectRetryEvent("dropped_send_fail", packet, 0); - onDirectRetryEvent("failure", packet, 0); + onDirectRetryEvent("dropped_send_fail", packet, 0, 1); + onDirectRetryEvent("failure", packet, 0, 1); clearDirectRetrySlot(i); } } @@ -665,9 +697,9 @@ bool Mesh::getDirectRetryTarget(const Packet* packet, const uint8_t*& next_hop_h return false; } - uint8_t hash_size = 1 << (packet->payload[8] & 0x03); uint8_t route_bytes = packet->payload_len - 9; - uint8_t offset = packet->path_len * hash_size; + uint8_t hash_size = decodeTraceHashSize(packet->payload[8], route_bytes); + uint16_t offset = (uint16_t)packet->path_len * (uint16_t)hash_size; if (offset + hash_size > route_bytes) { return false; } @@ -705,6 +737,8 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { } } if (slot_idx < 0) { + onDirectRetryEvent("dropped_no_slot", packet, 0, 0); + onDirectRetryEvent("failure", packet, 0, 0); return; } @@ -713,6 +747,8 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { calculateDirectRetryKey(packet, _direct_retries[slot_idx].retry_key); _direct_retries[slot_idx].packet = NULL; _direct_retries[slot_idx].trigger_packet = const_cast(packet); + _direct_retries[slot_idx].retry_started_at = 0; + _direct_retries[slot_idx].echo_wait_started_at = 0; _direct_retries[slot_idx].retry_at = 0; _direct_retries[slot_idx].retry_delay = retry_delay; _direct_retries[slot_idx].retry_attempts_sent = 0; @@ -721,7 +757,6 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { _direct_retries[slot_idx].expect_path_growth = expect_path_growth; _direct_retries[slot_idx].queued = false; _direct_retries[slot_idx].active = true; - onDirectRetryEvent("armed", packet, retry_delay); } Packet* Mesh::createAdvert(const LocalIdentity& id, const uint8_t* app_data, size_t app_data_len) { diff --git a/src/Mesh.h b/src/Mesh.h index ad4d8a2f..422b79ab 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -32,6 +32,8 @@ class Mesh : public Dispatcher { struct DirectRetryEntry { Packet* packet; Packet* trigger_packet; + unsigned long retry_started_at; + unsigned long echo_wait_started_at; unsigned long retry_at; uint32_t retry_delay; uint8_t retry_attempts_sent; @@ -115,7 +117,7 @@ protected: /** * \returns delay before a specific retry attempt, where attempt_idx=0 is the first retry. */ - virtual uint32_t getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx) const; + virtual uint32_t getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx); /** * \returns number of extra (Direct) ACK transmissions wanted. @@ -125,7 +127,7 @@ protected: /** * \brief Optional hook for logging direct-retry lifecycle events. */ - virtual void onDirectRetryEvent(const char* event, const Packet* packet, uint32_t delay_millis) { } + virtual void onDirectRetryEvent(const char* event, const Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) { } /** * \brief Perform search of local DB of peers/contacts. diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 357da210..dc206516 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -3,6 +3,7 @@ #include "TxtDataHelpers.h" #include "AdvertDataHelpers.h" #include +#include #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) @@ -14,17 +15,21 @@ #define DIRECT_RETRY_PREFS_MAGIC_0 0xD4 #define DIRECT_RETRY_PREFS_MAGIC_1 0x52 #define DIRECT_RETRY_RECENT_DEFAULT 1 -#define DIRECT_RETRY_SNR_MARGIN_DB_DEFAULT_X4 10 +#define DIRECT_RETRY_SNR_MARGIN_DB_DEFAULT_X4 DIRECT_RETRY_ROOFTOP_MARGIN_X4 #define DIRECT_RETRY_SNR_MARGIN_DB_MAX 40 #define DIRECT_RETRY_SNR_MARGIN_X4_MAX (DIRECT_RETRY_SNR_MARGIN_DB_MAX * 4) #define DIRECT_RETRY_TIMING_MAGIC_0 0xD5 #define DIRECT_RETRY_TIMING_MAGIC_1 0x54 -#define DIRECT_RETRY_COUNT_DEFAULT 15 +#define DIRECT_RETRY_COUNT_DEFAULT DIRECT_RETRY_ROOFTOP_COUNT #define DIRECT_RETRY_COUNT_MIN 1 #define DIRECT_RETRY_COUNT_MAX 15 -#define DIRECT_RETRY_BASE_MS_DEFAULT 200 +#define DIRECT_RETRY_BASE_MS_DEFAULT DIRECT_RETRY_ROOFTOP_BASE_MS #define DIRECT_RETRY_BASE_MS_MIN 10 #define DIRECT_RETRY_BASE_MS_MAX 5000 +#define DIRECT_RETRY_STEP_MS_DEFAULT DIRECT_RETRY_ROOFTOP_STEP_MS +#define DIRECT_RETRY_STEP_MS_MIN 0 +#define DIRECT_RETRY_STEP_MS_MAX 5000 +#define DIRECT_RETRY_PRESET_DEFAULT DIRECT_RETRY_PRESET_ROOFTOP // Believe it or not, this std C function is busted on some platforms! static uint32_t _atoi(const char* sp) { @@ -45,6 +50,98 @@ static float directRetryMarginX4ToDb(uint8_t margin_x4) { return ((float)margin_x4) / 4.0f; } +static uint8_t directRetryPresetOrDefault(uint8_t preset) { + if (preset <= DIRECT_RETRY_PRESET_MOBILE) { + return preset; + } + return DIRECT_RETRY_PRESET_DEFAULT; +} + +static const char* directRetryPresetName(uint8_t preset) { + switch (directRetryPresetOrDefault(preset)) { + case DIRECT_RETRY_PRESET_INFRA: + return "infra"; + case DIRECT_RETRY_PRESET_MOBILE: + return "mobile"; + case DIRECT_RETRY_PRESET_ROOFTOP: + default: + return "rooftop"; + } +} + +static uint8_t directRetryEffectiveCount(const NodePrefs* prefs) { + return constrain(prefs->direct_retry_attempts, (uint8_t)DIRECT_RETRY_COUNT_MIN, (uint8_t)DIRECT_RETRY_COUNT_MAX); +} + +static uint16_t directRetryEffectiveBaseMs(const NodePrefs* prefs) { + return constrain(prefs->direct_retry_base_ms, (uint16_t)DIRECT_RETRY_BASE_MS_MIN, (uint16_t)DIRECT_RETRY_BASE_MS_MAX); +} + +static uint16_t directRetryEffectiveStepMs(const NodePrefs* prefs) { + return constrain(prefs->direct_retry_step_ms, (uint16_t)DIRECT_RETRY_STEP_MS_MIN, (uint16_t)DIRECT_RETRY_STEP_MS_MAX); +} + +static uint8_t directRetryEffectiveMarginX4(const NodePrefs* prefs) { + return constrain(prefs->direct_retry_snr_margin_db, (uint8_t)0, (uint8_t)DIRECT_RETRY_SNR_MARGIN_X4_MAX); +} + +static uint16_t directRetryPresetStepDefault(uint8_t preset) { + switch (directRetryPresetOrDefault(preset)) { + case DIRECT_RETRY_PRESET_INFRA: + return DIRECT_RETRY_INFRA_STEP_MS; + case DIRECT_RETRY_PRESET_MOBILE: + return DIRECT_RETRY_MOBILE_STEP_MS; + case DIRECT_RETRY_PRESET_ROOFTOP: + default: + return DIRECT_RETRY_ROOFTOP_STEP_MS; + } +} + +static void applyDirectRetryPreset(NodePrefs* prefs, uint8_t preset) { + prefs->direct_retry_preset = directRetryPresetOrDefault(preset); + switch (prefs->direct_retry_preset) { + case DIRECT_RETRY_PRESET_INFRA: + prefs->direct_retry_base_ms = DIRECT_RETRY_INFRA_BASE_MS; + prefs->direct_retry_attempts = DIRECT_RETRY_INFRA_COUNT; + prefs->direct_retry_step_ms = DIRECT_RETRY_INFRA_STEP_MS; + prefs->direct_retry_snr_margin_db = DIRECT_RETRY_INFRA_MARGIN_X4; + break; + case DIRECT_RETRY_PRESET_MOBILE: + prefs->direct_retry_base_ms = DIRECT_RETRY_MOBILE_BASE_MS; + prefs->direct_retry_attempts = DIRECT_RETRY_MOBILE_COUNT; + prefs->direct_retry_step_ms = DIRECT_RETRY_MOBILE_STEP_MS; + prefs->direct_retry_snr_margin_db = DIRECT_RETRY_MOBILE_MARGIN_X4; + break; + case DIRECT_RETRY_PRESET_ROOFTOP: + default: + prefs->direct_retry_base_ms = DIRECT_RETRY_ROOFTOP_BASE_MS; + prefs->direct_retry_attempts = DIRECT_RETRY_ROOFTOP_COUNT; + prefs->direct_retry_step_ms = DIRECT_RETRY_ROOFTOP_STEP_MS; + prefs->direct_retry_snr_margin_db = DIRECT_RETRY_ROOFTOP_MARGIN_X4; + break; + } +} + +static bool parseDirectRetryPreset(const char* value, uint8_t& preset) { + if (value == NULL) { + return false; + } + if (strcmp(value, "0") == 0 || strcmp(value, "infra") == 0 + || strcmp(value, "infa") == 0 || strcmp(value, "infrastructure") == 0) { + preset = DIRECT_RETRY_PRESET_INFRA; + return true; + } + if (strcmp(value, "1") == 0 || strcmp(value, "rooftop") == 0) { + preset = DIRECT_RETRY_PRESET_ROOFTOP; + return true; + } + if (strcmp(value, "2") == 0 || strcmp(value, "mobile") == 0) { + preset = DIRECT_RETRY_PRESET_MOBILE; + return true; + } + return false; +} + static bool isValidName(const char *n) { while (*n) { if (*n == '[' || *n == ']' || *n == '\\' || *n == ':' || *n == ',' || *n == '?' || *n == '*') return false; @@ -127,6 +224,9 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->direct_retry_timing_magic[0], sizeof(_prefs->direct_retry_timing_magic)); // 294 size_t radio_fem_rxgain_read = file.read((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 296 + file.read((uint8_t *)&_prefs->direct_retry_preset, sizeof(_prefs->direct_retry_preset)); // 297 + size_t retry_step_read = file.read((uint8_t *)&_prefs->direct_retry_step_ms, + sizeof(_prefs->direct_retry_step_ms)); // 298 // PowerSaving-only prefs stored radio_fem_rxgain at 291, before direct retry timing existed. if (radio_fem_rxgain_read != sizeof(_prefs->radio_fem_rxgain) && legacy_retry_attempts_read == sizeof(legacy_retry_attempts_or_radio_fem_rxgain) @@ -134,7 +234,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { || _prefs->direct_retry_timing_magic[1] != DIRECT_RETRY_TIMING_MAGIC_1)) { _prefs->radio_fem_rxgain = constrain(legacy_retry_attempts_or_radio_fem_rxgain, 0, 1); } - // next: 297 + // next: 298 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -182,6 +282,12 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { // sanitise settings _prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean _prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean + _prefs->direct_retry_preset = directRetryPresetOrDefault(_prefs->direct_retry_preset); + if (retry_step_read != sizeof(_prefs->direct_retry_step_ms)) { + _prefs->direct_retry_step_ms = directRetryPresetStepDefault(_prefs->direct_retry_preset); + } else { + _prefs->direct_retry_step_ms = constrain(_prefs->direct_retry_step_ms, DIRECT_RETRY_STEP_MS_MIN, DIRECT_RETRY_STEP_MS_MAX); + } file.close(); } @@ -252,7 +358,9 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { uint8_t retry_timing_magic[2] = { DIRECT_RETRY_TIMING_MAGIC_0, DIRECT_RETRY_TIMING_MAGIC_1 }; file.write(retry_timing_magic, sizeof(retry_timing_magic)); // 294 file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 296 - // next: 297 + file.write((uint8_t *)&_prefs->direct_retry_preset, sizeof(_prefs->direct_retry_preset)); // 297 + file.write((uint8_t *)&_prefs->direct_retry_step_ms, sizeof(_prefs->direct_retry_step_ms)); // 298 + // next: 300 file.close(); } @@ -413,11 +521,17 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else if (memcmp(config, "direct.retry.heard", 18) == 0) { sprintf(reply, "> %s", _prefs->direct_retry_recent_enabled ? "on" : "off"); } else if (memcmp(config, "direct.retry.margin", 19) == 0) { - sprintf(reply, "> %s", StrHelper::ftoa(directRetryMarginX4ToDb(_prefs->direct_retry_snr_margin_db))); + sprintf(reply, "> %s", StrHelper::ftoa(directRetryMarginX4ToDb(directRetryEffectiveMarginX4(_prefs)))); + } else if (memcmp(config, "direct.retry.preset", 19) == 0) { + sprintf(reply, "> %d,%s", + (uint32_t)directRetryPresetOrDefault(_prefs->direct_retry_preset), + directRetryPresetName(_prefs->direct_retry_preset)); } else if (memcmp(config, "direct.retry.count", 18) == 0) { - sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_attempts); + sprintf(reply, "> %d", (uint32_t)directRetryEffectiveCount(_prefs)); } else if (memcmp(config, "direct.retry.base", 17) == 0) { - sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_base_ms); + sprintf(reply, "> %d", (uint32_t)directRetryEffectiveBaseMs(_prefs)); + } else if (memcmp(config, "direct.retry.step", 17) == 0) { + sprintf(reply, "> %d", (uint32_t)directRetryEffectiveStepMs(_prefs)); } else if (memcmp(config, "owner.info", 10) == 0) { *reply++ = '>'; *reply++ = ' '; @@ -688,6 +802,15 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else { sprintf(reply, "Error, min 0 and max %d", DIRECT_RETRY_SNR_MARGIN_DB_MAX); } + } else if (memcmp(config, "direct.retry.preset ", 20) == 0) { + uint8_t preset; + if (parseDirectRetryPreset(&config[20], preset)) { + applyDirectRetryPreset(_prefs, preset); + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be infra, rooftop, mobile, 0, 1, or 2"); + } } else if (memcmp(config, "direct.retry.count ", 19) == 0) { int count = atoi(&config[19]); if (count >= DIRECT_RETRY_COUNT_MIN && count <= DIRECT_RETRY_COUNT_MAX) { @@ -706,6 +829,15 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else { sprintf(reply, "Error, min %d and max %d", DIRECT_RETRY_BASE_MS_MIN, DIRECT_RETRY_BASE_MS_MAX); } + } else if (memcmp(config, "direct.retry.step ", 18) == 0) { + int delay_ms = atoi(&config[18]); + if (delay_ms >= DIRECT_RETRY_STEP_MS_MIN && delay_ms <= DIRECT_RETRY_STEP_MS_MAX) { + _prefs->direct_retry_step_ms = (uint16_t)delay_ms; + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error, min %d and max %d", DIRECT_RETRY_STEP_MS_MIN, DIRECT_RETRY_STEP_MS_MAX); + } } else if (memcmp(config, "owner.info ", 11) == 0) { config += 11; char *dp = _prefs->owner_info; @@ -1224,6 +1356,15 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { sprintf(reply, "Error, min 0 and max %d", DIRECT_RETRY_SNR_MARGIN_DB_MAX); } + } else if (memcmp(config, "direct.retry.preset ", 20) == 0) { + uint8_t preset; + if (parseDirectRetryPreset(&config[20], preset)) { + applyDirectRetryPreset(_prefs, preset); + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be infra, rooftop, mobile, 0, 1, or 2"); + } } else if (memcmp(config, "direct.retry.count ", 19) == 0) { int count = atoi(&config[19]); if (count >= DIRECT_RETRY_COUNT_MIN && count <= DIRECT_RETRY_COUNT_MAX) { @@ -1242,6 +1383,15 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { sprintf(reply, "Error, min %d and max %d", DIRECT_RETRY_BASE_MS_MIN, DIRECT_RETRY_BASE_MS_MAX); } + } else if (memcmp(config, "direct.retry.step ", 18) == 0) { + int delay_ms = atoi(&config[18]); + if (delay_ms >= DIRECT_RETRY_STEP_MS_MIN && delay_ms <= DIRECT_RETRY_STEP_MS_MAX) { + _prefs->direct_retry_step_ms = (uint16_t)delay_ms; + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error, min %d and max %d", DIRECT_RETRY_STEP_MS_MIN, DIRECT_RETRY_STEP_MS_MAX); + } } else if (memcmp(config, "owner.info ", 11) == 0) { config += 11; char *dp = _prefs->owner_info; @@ -1420,11 +1570,17 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep } else if (memcmp(config, "direct.retry.heard", 18) == 0) { sprintf(reply, "> %s", _prefs->direct_retry_recent_enabled ? "on" : "off"); } else if (memcmp(config, "direct.retry.margin", 19) == 0) { - sprintf(reply, "> %s", StrHelper::ftoa(directRetryMarginX4ToDb(_prefs->direct_retry_snr_margin_db))); + sprintf(reply, "> %s", StrHelper::ftoa(directRetryMarginX4ToDb(directRetryEffectiveMarginX4(_prefs)))); + } else if (memcmp(config, "direct.retry.preset", 19) == 0) { + sprintf(reply, "> %d,%s", + (uint32_t)directRetryPresetOrDefault(_prefs->direct_retry_preset), + directRetryPresetName(_prefs->direct_retry_preset)); } else if (memcmp(config, "direct.retry.count", 18) == 0) { - sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_attempts); + sprintf(reply, "> %d", (uint32_t)directRetryEffectiveCount(_prefs)); } else if (memcmp(config, "direct.retry.base", 17) == 0) { - sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_base_ms); + sprintf(reply, "> %d", (uint32_t)directRetryEffectiveBaseMs(_prefs)); + } else if (memcmp(config, "direct.retry.step", 17) == 0) { + sprintf(reply, "> %d", (uint32_t)directRetryEffectiveStepMs(_prefs)); } else if (memcmp(config, "owner.info", 10) == 0) { *reply++ = '>'; *reply++ = ' '; diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 3ca19357..ea30777a 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -19,6 +19,25 @@ #define LOOP_DETECT_MODERATE 2 #define LOOP_DETECT_STRICT 3 +#define DIRECT_RETRY_PRESET_INFRA 0 +#define DIRECT_RETRY_PRESET_ROOFTOP 1 +#define DIRECT_RETRY_PRESET_MOBILE 2 + +#define DIRECT_RETRY_INFRA_BASE_MS 275 +#define DIRECT_RETRY_INFRA_COUNT 4 +#define DIRECT_RETRY_INFRA_STEP_MS 150 +#define DIRECT_RETRY_INFRA_MARGIN_X4 60 + +#define DIRECT_RETRY_ROOFTOP_BASE_MS 175 +#define DIRECT_RETRY_ROOFTOP_COUNT 15 +#define DIRECT_RETRY_ROOFTOP_STEP_MS 100 +#define DIRECT_RETRY_ROOFTOP_MARGIN_X4 20 + +#define DIRECT_RETRY_MOBILE_BASE_MS 175 +#define DIRECT_RETRY_MOBILE_COUNT 15 +#define DIRECT_RETRY_MOBILE_STEP_MS 50 +#define DIRECT_RETRY_MOBILE_MARGIN_X4 0 + struct NodePrefs { // persisted to file float airtime_factor; char node_name[32]; @@ -67,6 +86,8 @@ struct NodePrefs { // persisted to file uint8_t direct_retry_attempts; uint16_t direct_retry_base_ms; uint8_t direct_retry_timing_magic[2]; + uint8_t direct_retry_preset; + uint16_t direct_retry_step_ms; }; class CommonCLICallbacks { diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index ac6d01b5..ae28acc8 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -9,8 +9,10 @@ #define MAX_PACKET_HASHES 128 #define MAX_PACKET_ACKS 64 #ifndef MAX_RECENT_REPEATERS - // Two defaults. Can be overridden with -D MAX_RECENT_REPEATERS=. - #if defined(ESP32) + // Platform defaults. Can be overridden with -D MAX_RECENT_REPEATERS=. + #if defined(ESP32) || defined(ESP32_PLATFORM) + #define MAX_RECENT_REPEATERS 2048 + #elif defined(NRF52_PLATFORM) #define MAX_RECENT_REPEATERS 512 #else #define MAX_RECENT_REPEATERS 64 @@ -23,9 +25,7 @@ public: typedef bool (*RecentRepeaterAllowFn)(const uint8_t* prefix, uint8_t prefix_len, void* ctx); struct RecentRepeaterInfo { - // Just enough identity to match a next-hop path prefix plus the SNR that heard it. - uint16_t retry_count; - uint16_t fail_count; + // Identity and link quality for a next-hop path prefix. uint8_t prefix[MAX_ROUTE_HASH_BYTES]; uint8_t prefix_len; int8_t snr_x4; @@ -80,8 +80,8 @@ private: int8_t weightedSnrX4RoundUp(int8_t curr_snr_x4, int8_t new_snr_x4) const { // Keep existing SNR heavier than a single new sample: 75% existing + 25% new. - int16_t weighted_sum = ((int16_t)curr_snr_x4 * 3) + (int16_t)new_snr_x4; - int16_t blended = weighted_sum / 4; // truncates toward zero + int32_t weighted_sum = ((int32_t)curr_snr_x4 * 3) + (int32_t)new_snr_x4; + int32_t blended = weighted_sum / 4; // truncates toward zero // "Round up" means ceil(), which only differs from truncation for positive remainders. if (weighted_sum > 0 && (weighted_sum % 4) != 0) { blended++; @@ -160,8 +160,11 @@ public: f.read((uint8_t *) &_next_idx, sizeof(_next_idx)); f.read((uint8_t *) &_acks[0], sizeof(_acks)); f.read((uint8_t *) &_next_ack_idx, sizeof(_next_ack_idx)); - f.read((uint8_t *) &_recent_repeaters[0], sizeof(_recent_repeaters)); - f.read((uint8_t *) &_next_recent_repeater_idx, sizeof(_next_recent_repeater_idx)); + // Recent repeater entries are intentionally not restored across boots. + // This avoids struct-layout migration issues and keeps stale path quality + // stats from persisting indefinitely. + memset(_recent_repeaters, 0, sizeof(_recent_repeaters)); + _next_recent_repeater_idx = 0; } void saveTo(File f) { f.write(_hashes, sizeof(_hashes)); @@ -321,96 +324,10 @@ public: memcpy(slot.prefix, prefix, prefix_len); slot.prefix_len = prefix_len; slot.snr_x4 = snr_x4; - slot.retry_count = 0; - slot.fail_count = 0; slot.snr_locked = snr_locked ? 1 : 0; _next_recent_repeater_idx = (slot_idx + 1) % MAX_RECENT_REPEATERS; return true; } - bool incrementRecentRepeaterRetryCount(const uint8_t* prefix, uint8_t prefix_len, - bool create_if_missing = false, int8_t seed_snr_x4 = 0, - bool bypass_allow_filter = false) { - if (prefix == NULL || prefix_len == 0) { - return false; - } - if (prefix_len > MAX_ROUTE_HASH_BYTES) { - prefix_len = MAX_ROUTE_HASH_BYTES; - } - - for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { - RecentRepeaterInfo& existing = _recent_repeaters[i]; - if (existing.prefix_len == 0 || !prefixesOverlap(existing.prefix, existing.prefix_len, prefix, prefix_len)) { - continue; - } - if (prefix_len > existing.prefix_len) { - memset(existing.prefix, 0, sizeof(existing.prefix)); - memcpy(existing.prefix, prefix, prefix_len); - existing.prefix_len = prefix_len; - } - if (existing.retry_count < 0xFFFF) { - existing.retry_count++; - } - return true; - } - - if (!create_if_missing || !setRecentRepeater(prefix, prefix_len, seed_snr_x4, false, bypass_allow_filter)) { - return false; - } - - for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { - RecentRepeaterInfo& existing = _recent_repeaters[i]; - if (existing.prefix_len == 0 || !prefixesOverlap(existing.prefix, existing.prefix_len, prefix, prefix_len)) { - continue; - } - if (existing.retry_count < 0xFFFF) { - existing.retry_count++; - } - return true; - } - return false; - } - bool incrementRecentRepeaterFailCount(const uint8_t* prefix, uint8_t prefix_len, - bool create_if_missing = false, int8_t seed_snr_x4 = 0, - bool bypass_allow_filter = false) { - if (prefix == NULL || prefix_len == 0) { - return false; - } - if (prefix_len > MAX_ROUTE_HASH_BYTES) { - prefix_len = MAX_ROUTE_HASH_BYTES; - } - - for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { - RecentRepeaterInfo& existing = _recent_repeaters[i]; - if (existing.prefix_len == 0 || !prefixesOverlap(existing.prefix, existing.prefix_len, prefix, prefix_len)) { - continue; - } - if (prefix_len > existing.prefix_len) { - memset(existing.prefix, 0, sizeof(existing.prefix)); - memcpy(existing.prefix, prefix, prefix_len); - existing.prefix_len = prefix_len; - } - if (existing.fail_count < 0xFFFF) { - existing.fail_count++; - } - return true; - } - - if (!create_if_missing || !setRecentRepeater(prefix, prefix_len, seed_snr_x4, false, bypass_allow_filter)) { - return false; - } - - for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { - RecentRepeaterInfo& existing = _recent_repeaters[i]; - if (existing.prefix_len == 0 || !prefixesOverlap(existing.prefix, existing.prefix_len, prefix, prefix_len)) { - continue; - } - if (existing.fail_count < 0xFFFF) { - existing.fail_count++; - } - return true; - } - return false; - } bool decrementRecentRepeaterSnrX4(const uint8_t* prefix, uint8_t prefix_len, uint8_t amount_x4 = 1) { if (prefix == NULL || prefix_len == 0 || amount_x4 == 0) { return false; From 4b49449ff6e0cad9c9e5c606794574beae83a667 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Thu, 7 May 2026 20:43:36 +0700 Subject: [PATCH 041/214] fix CustomLFS version pinning --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 864e5e1f..2a6c23df 100644 --- a/platformio.ini +++ b/platformio.ini @@ -91,7 +91,7 @@ build_flags = ${arduino_base.build_flags} -D EXTRAFS=1 lib_deps = ${arduino_base.lib_deps} - https://github.com/oltaco/CustomLFS @ 0.2.1 + https://github.com/oltaco/CustomLFS#0.2.1 ; ----------------- RP2040 --------------------- [rp2040_base] From 041f103b019701eb4d02609693edd66ab878fa06 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Thu, 7 May 2026 21:09:38 +0700 Subject: [PATCH 042/214] Reduced MAX_CONTACTS to 100 to compile and add PS BLE --- variants/lilygo_tlora_v2_1/platformio.ini | 25 ++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index 3641f127..1b0cb6ec 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -92,7 +92,30 @@ extends = LilyGo_TLora_V2_1_1_6 build_flags = ${LilyGo_TLora_V2_1_1_6.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=160 + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 + -D OFFLINE_QUEUE_SIZE=256 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${LilyGo_TLora_V2_1_1_6.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps] +extends = LilyGo_TLora_V2_1_1_6 +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${LilyGo_TLora_V2_1_1_6.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 -D OFFLINE_QUEUE_SIZE=256 From cc1759aa42a7e1a45cc4477017e4d1ae094be893 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Thu, 7 May 2026 21:11:56 +0700 Subject: [PATCH 043/214] Added PS BLE --- variants/heltec_ct62/platformio.ini | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/variants/heltec_ct62/platformio.ini b/variants/heltec_ct62/platformio.ini index 910385ec..0031c8ef 100644 --- a/variants/heltec_ct62/platformio.ini +++ b/variants/heltec_ct62/platformio.ini @@ -130,6 +130,28 @@ lib_deps = ${esp32_ota.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_ct62_companion_radio_ble_ps] +extends = Heltec_ct62 +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${Heltec_ct62.build_flags} +; -D ARDUINO_USB_MODE=1 +; -D ARDUINO_USB_CDC_ON_BOOT=1 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D OFFLINE_QUEUE_SIZE=256 + -D BLE_PIN_CODE=123456 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_ct62.build_src_filter} + +<../examples/companion_radio/*.cpp> + + +lib_deps = + ${Heltec_ct62.lib_deps} + ${esp32_ota.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:Heltec_ct62_sensor] extends = Heltec_ct62 build_flags = From 1610c0a19b08feb982078155aadbf62b7b104b85 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Thu, 7 May 2026 21:13:50 +0700 Subject: [PATCH 044/214] Added more boards to PowerSaving --- build-iotthinks.sh | 70 ++++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/build-iotthinks.sh b/build-iotthinks.sh index 7c654482..9bff2eff 100644 --- a/build-iotthinks.sh +++ b/build-iotthinks.sh @@ -1,9 +1,9 @@ # sh ./build-repeaters-iotthinks.sh -export FIRMWARE_VERSION="PowerSaving15" +export FIRMWARE_VERSION="PowerSaving15.0.2" ############# Repeaters ############# # Commonly-used boards -## ESP32 - 12 boards +## ESP32 - 17 boards sh build.sh build-firmware \ Heltec_v3_repeater \ Heltec_WSL3_repeater \ @@ -16,9 +16,14 @@ Xiao_S3_WIO_repeater \ Xiao_C3_repeater \ Xiao_C6_repeater_ \ Heltec_E290_repeater \ -Heltec_Wireless_Tracker_repeater +Heltec_Wireless_Tracker_repeater \ +LilyGo_TBeam_1W_repeater \ +Xiao_S3_repeater \ +heltec_tracker_v2_repeater \ +Heltec_Wireless_Paper_repeater \ +Heltec_ct62_repeater -## NRF52 - 13 boards +## NRF52 - 17 boards sh build.sh build-firmware \ RAK_4631_repeater \ Heltec_t114_repeater \ @@ -32,7 +37,12 @@ WioTrackerL1_repeater \ RAK_3401_repeater \ RAK_WisMesh_Tag_repeater \ GAT562_30S_Mesh_Kit_repeater \ -GAT562_Mesh_Tracker_Pro_repeater +GAT562_Mesh_Tracker_Pro_repeater \ +ikoka_nano_nrf_22dbm_repeater \ +ikoka_nano_nrf_30dbm_repeater \ +ikoka_nano_nrf_33dbm_repeater \ +ThinkNode_M1_repeater \ +Heltec_t096_repeater ## ESP32, SX1276 - 3 boards sh build.sh build-firmware \ @@ -40,37 +50,29 @@ Heltec_v2_repeater \ LilyGo_TLora_V2_1_1_6_repeater \ Tbeam_SX1276_repeater -## Ikoka - 3 boards -sh build.sh build-firmware \ -ikoka_nano_nrf_22dbm_repeater \ -ikoka_nano_nrf_30dbm_repeater \ -ikoka_nano_nrf_33dbm_repeater - ############# Room Server ############# -# ESP32 +# ESP32 - 7 boards sh build.sh build-firmware \ Heltec_v3_room_server \ -heltec_v4_room_server +heltec_v4_room_server \ +LilyGo_TBeam_1W_room_server \ +Heltec_WSL3_room_server \ +Xiao_S3_room_server \ +heltec_tracker_v2_room_server \ +Heltec_Wireless_Paper_room_server -# NRF52 +# NRF52 - 6 boards sh build.sh build-firmware \ RAK_4631_room_server \ Heltec_t114_room_server \ Xiao_nrf52_room_server \ t1000e_room_server \ WioTrackerL1_room_server \ -RAK_3401_room_server +RAK_3401_room_server \ +Heltec_t096_room_server ############# Companions BLE ############# -# ESP32 -sh build.sh build-firmware \ -Heltec_v3_companion_radio_ble_ps \ -heltec_v4_companion_radio_ble_ps \ -heltec_v4_companion_radio_ble_ps_femoff \ -Xiao_S3_WIO_companion_radio_ble \ -Heltec_Wireless_Paper_companion_radio_ble - -# NRF52 +# NRF52 - 12 boards sh build.sh build-firmware \ RAK_4631_companion_radio_ble \ Heltec_t114_companion_radio_ble \ @@ -79,13 +81,14 @@ t1000e_companion_radio_ble \ LilyGo_T-Echo_companion_radio_ble \ WioTrackerL1_companion_radio_ble \ RAK_3401_companion_radio_ble \ -RAK_WisMesh_Tag_companion_radio_ble - -############# Companions USB ############# -sh build.sh build-firmware \ -Heltec_v3_companion_radio_usb +RAK_WisMesh_Tag_companion_radio_ble \ +SenseCap_Solar_companion_radio_ble \ +ThinkNode_M1_companion_radio_ble \ +Heltec_t096_companion_radio_ble \ +Heltec_t096_companion_radio_ble_femoff ############# Companions BLE PS ############# +# ESP32 - 13 boards sh build.sh build-firmware \ Heltec_v3_companion_radio_ble_ps \ heltec_v4_companion_radio_ble_ps \ @@ -93,4 +96,11 @@ heltec_v4_3_companion_radio_ble_ps_femoff \ Xiao_C3_companion_radio_ble_ps \ Xiao_S3_companion_radio_ble_ps \ Xiao_S3_WIO_companion_radio_ble_ps \ -Heltec_v2_companion_radio_ble_ps +Heltec_v2_companion_radio_ble_ps \ +LilyGo_TBeam_1W_companion_radio_ble_ps \ +Heltec_WSL3_companion_radio_ble_ps \ +Heltec_Wireless_Tracker_companion_radio_ble_ps \ +heltec_tracker_v2_companion_radio_ble_ps \ +Heltec_Wireless_Paper_companion_radio_ble_ps \ +LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps \ +Heltec_ct62_companion_radio_ble_ps From d4febe7867938fc7a38898db4fecd5fe1be55edc Mon Sep 17 00:00:00 2001 From: mikecarper Date: Thu, 7 May 2026 11:48:29 -0700 Subject: [PATCH 045/214] Add flood retry controls --- docs/cli_commands.md | 88 +++++- examples/simple_repeater/MyMesh.cpp | 293 +++++++++++++++++ examples/simple_repeater/MyMesh.h | 24 ++ src/Dispatcher.cpp | 25 +- src/Dispatcher.h | 5 +- src/Mesh.cpp | 280 ++++++++++++++++- src/Mesh.h | 64 ++++ src/helpers/CommonCLI.cpp | 401 +++++++++++++++++++++++- src/helpers/CommonCLI.h | 19 ++ src/helpers/SimpleMeshTables.h | 16 + src/helpers/StaticPoolPacketManager.cpp | 5 +- src/helpers/StaticPoolPacketManager.h | 4 +- 12 files changed, 1204 insertions(+), 20 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index f9c08259..79d319ad 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -585,7 +585,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore - `rooftop` (`1`): `175 ms` base wait, `15` retries, `100 ms` added per retry, SNR gate is SF floor + `5 dB` - `mobile` (`2`): `175 ms` base wait, `15` retries, `50 ms` added per retry, SNR gate is the SF floor -**Note:** Selecting a preset copies those values into the retry settings. You can refine `direct.retry.margin`, `direct.retry.count`, `direct.retry.base`, or `direct.retry.step` afterward. Retry delay is `direct.txdelay` jitter + base wait + packet-length airtime wait + per-attempt step. +**Note:** Selecting a preset copies those values into the direct retry settings and also resets flood retry defaults. You can refine `direct.retry.margin`, `direct.retry.count`, `direct.retry.base`, `direct.retry.step`, `flood.retry.count`, or `flood.retry.path` afterward. Retry delay is `direct.txdelay` jitter + base wait + packet-length airtime wait + per-attempt step. --- @@ -754,6 +754,92 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### View or change the flood retry preset +**Usage:** +- `get flood.retry.preset` +- `set flood.retry.preset ` + +**Parameters:** +- `value`: `infra`|`rooftop`|`mobile` or `0`|`1`|`2` + +**Presets:** +- `infra` (`0`): `1` retry, path gate `1` +- `rooftop` (`1`): `3` retries, path gate `2` +- `mobile` (`2`): `3` retries, path gate `1` + +**Note:** This applies only the flood retry defaults. `set direct.retry.preset` also resets these flood retry defaults. + +--- + +#### View or change the number of flood retry attempts +**Usage:** +- `get flood.retry.count` +- `set flood.retry.count ` + +**Parameters:** +- `value`: Maximum retry attempts after initial flood TX (`0`-`3`) + +**Default:** `3` for `rooftop` and `mobile`, `1` for `infra` + +**Note:** `0` disables flood retry. + +--- + +#### View or change the flood retry path gate +**Usage:** +- `get flood.retry.path` +- `set flood.retry.path ` + +**Parameters:** +- `value`: Maximum flood path length eligible for retry (`0`-`63`), or `off` to disable the gate + +**Default:** `2` for `rooftop`, `1` for `infra` and `mobile` + +--- + +#### View or change flood retry target prefixes +**Usage:** +- `get flood.retry.prefixes` +- `set flood.retry.prefixes ` + +**Parameters:** +- `prefixes`: Comma-separated 3-byte hex prefixes, such as `A1B2C3,D4E5F6`; use `none` or `off` to clear + +**Default:** `none` + +**Note:** Prefixes are stored as 3 bytes. Flood retry skips packets whose path already contains a matching target prefix. When prefixes are configured, only a downstream echo from one of those target prefixes cancels a queued retry; when no prefixes are configured, any downstream echo cancels it. Matching works with 3-byte, 2-byte, or 1-byte flood paths by comparing the matching leading bytes. + +--- + +#### View or change flood retry bridge mode +**Usage:** +- `get flood.retry.bridge` +- `set flood.retry.bridge ` + +**Parameters:** +- `state`: `on` or `off` + +**Default:** `off` + +**Note:** Bridge mode uses bucket definitions instead of the single `flood.retry.prefixes` target list. If a flood comes from one fresh bucket, retry continues until every other fresh configured bucket has been heard or `flood.retry.count` is exhausted. + +--- + +#### View or change flood retry bridge buckets +**Usage:** +- `get flood.retry.bucket.` +- `set flood.retry.bucket ` + +**Parameters:** +- `bucket`: Bucket number (`1`-`6`) +- `prefixes`: Up to 8 comma-separated 3-byte hex prefixes, such as `AABBCC,223344`; use `none` or `off` to clear + +**Default:** all buckets empty + +**Note:** Prefixes are stored as 3 bytes but match 3-byte, 2-byte, and 1-byte flood paths by comparing leading bytes. Bucket prefixes are included in bridge retry logic only if they were heard in the recent repeater table within the last hour. + +--- + ### ACL #### Add, update or remove permissions for a companion diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 6b3e8d6a..404a26d6 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -854,6 +854,180 @@ uint8_t MyMesh::getDirectRetryConfiguredMaxAttempts() const { uint32_t MyMesh::getDirectRetryAttemptStepMillis() const { return constrain((uint32_t)_prefs.direct_retry_step_ms, (uint32_t)0, (uint32_t)5000); } +bool MyMesh::hasFloodRetryPrefixes() const { + for (int i = 0; i < FLOOD_RETRY_PREFIX_SLOTS; i++) { + const uint8_t* configured = _prefs.flood_retry_prefixes[i]; + if (configured[0] != 0 || configured[1] != 0 || configured[2] != 0) { + return true; + } + } + return false; +} +bool MyMesh::floodRetryLastHopMatches(const mesh::Packet* packet) const { + if (packet == NULL || packet->getPathHashCount() == 0) { + return false; + } + + uint8_t hash_size = packet->getPathHashSize(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + return false; + } + + const uint8_t* heard_prefix = &packet->path[(packet->getPathHashCount() - 1) * hash_size]; + for (int i = 0; i < FLOOD_RETRY_PREFIX_SLOTS; i++) { + const uint8_t* configured = _prefs.flood_retry_prefixes[i]; + if ((configured[0] != 0 || configured[1] != 0 || configured[2] != 0) + && memcmp(configured, heard_prefix, hash_size) == 0) { + return true; + } + } + + return false; +} +bool MyMesh::floodRetryPrefixMatches(const mesh::Packet* packet) const { + if (packet == NULL || packet->getPathHashCount() == 0) { + return false; + } + + uint8_t hash_size = packet->getPathHashSize(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + return false; + } + + const uint8_t* path = packet->path; + for (int hop = 0; hop < packet->getPathHashCount(); hop++) { + for (int i = 0; i < FLOOD_RETRY_PREFIX_SLOTS; i++) { + const uint8_t* configured = _prefs.flood_retry_prefixes[i]; + if ((configured[0] != 0 || configured[1] != 0 || configured[2] != 0) + && memcmp(configured, path, hash_size) == 0) { + return true; + } + } + path += hash_size; + } + + return false; +} +bool MyMesh::floodRetryPrefixFresh(const uint8_t* prefix, uint8_t prefix_len) const { + const auto* recent = ((const SimpleMeshTables *)getTables())->findRecentRepeaterByHash(prefix, prefix_len); + if (recent == NULL || recent->last_heard_millis == 0) { + return false; + } + return (uint32_t)(millis() - recent->last_heard_millis) <= 3600000UL; +} +int MyMesh::floodRetryBucketForPrefix(const uint8_t* prefix, uint8_t prefix_len, bool require_fresh) const { + if (prefix == NULL || prefix_len == 0 || prefix_len > MAX_ROUTE_HASH_BYTES) { + return -1; + } + if (require_fresh && !floodRetryPrefixFresh(prefix, prefix_len)) { + return -1; + } + for (int bucket = 0; bucket < FLOOD_RETRY_BRIDGE_BUCKETS; bucket++) { + for (int i = 0; i < FLOOD_RETRY_BUCKET_PREFIXES; i++) { + const uint8_t* configured = _prefs.flood_retry_bridge_buckets[bucket][i]; + if ((configured[0] != 0 || configured[1] != 0 || configured[2] != 0) + && memcmp(configured, prefix, prefix_len) == 0) { + return bucket; + } + } + } + return -1; +} +int MyMesh::floodRetrySourceBucket(const mesh::Packet* packet) const { + if (packet == NULL || packet->getPathHashCount() < 2) { + return -1; + } + uint8_t hash_size = packet->getPathHashSize(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + return -1; + } + const uint8_t* source_prefix = &packet->path[(packet->getPathHashCount() - 2) * hash_size]; + return floodRetryBucketForPrefix(source_prefix, hash_size, true); +} +uint8_t MyMesh::floodRetryBridgeTargetMask(uint8_t source_bucket) const { + uint8_t mask = 0; + for (int bucket = 0; bucket < FLOOD_RETRY_BRIDGE_BUCKETS; bucket++) { + if (bucket == source_bucket) { + continue; + } + for (int i = 0; i < FLOOD_RETRY_BUCKET_PREFIXES; i++) { + const uint8_t* configured = _prefs.flood_retry_bridge_buckets[bucket][i]; + if ((configured[0] != 0 || configured[1] != 0 || configured[2] != 0) + && floodRetryPrefixFresh(configured, FLOOD_RETRY_PREFIX_LEN)) { + mask |= (uint8_t)(1U << bucket); + break; + } + } + } + return mask; +} +uint8_t MyMesh::floodRetryBridgeHeardMask(const mesh::Packet* packet, uint8_t source_bucket) const { + if (packet == NULL || packet->getPathHashCount() == 0) { + return 0; + } + uint8_t hash_size = packet->getPathHashSize(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + return 0; + } + + uint8_t mask = 0; + const uint8_t* path = packet->path; + for (int hop = 0; hop < packet->getPathHashCount(); hop++) { + int bucket = floodRetryBucketForPrefix(path, hash_size, true); + if (bucket >= 0 && bucket != source_bucket) { + mask |= (uint8_t)(1U << bucket); + } + path += hash_size; + } + return mask; +} +MyMesh::FloodRetryBridgeState* MyMesh::floodRetryBridgeStateFor(const mesh::Packet* packet, bool create) const { + if (packet == NULL) { + return NULL; + } + + uint8_t key[MAX_HASH_SIZE]; + packet->calculatePacketHash(key); + FloodRetryBridgeState* free_slot = NULL; + for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) { + if (flood_retry_bridge_states[i].active + && memcmp(flood_retry_bridge_states[i].key, key, MAX_HASH_SIZE) == 0) { + return &flood_retry_bridge_states[i]; + } + if (!flood_retry_bridge_states[i].active && free_slot == NULL) { + free_slot = &flood_retry_bridge_states[i]; + } + } + if (!create) { + return NULL; + } + if (free_slot == NULL) { + return NULL; + } + + int source_bucket = floodRetrySourceBucket(packet); + if (source_bucket < 0) { + return NULL; + } + + uint8_t target_mask = floodRetryBridgeTargetMask((uint8_t)source_bucket); + if (target_mask == 0) { + return NULL; + } + + uint8_t heard_mask = floodRetryBridgeHeardMask(packet, (uint8_t)source_bucket) & target_mask; + if ((heard_mask & target_mask) == target_mask) { + return NULL; + } + + memset(free_slot, 0, sizeof(*free_slot)); + memcpy(free_slot->key, key, sizeof(free_slot->key)); + free_slot->source_bucket = (uint8_t)source_bucket; + free_slot->target_mask = target_mask; + free_slot->heard_mask = heard_mask; + free_slot->active = true; + return free_slot; +} uint32_t MyMesh::getDirectRetryEchoDelay(const mesh::Packet* packet) const { uint32_t base_wait_millis = constrain((uint32_t)_prefs.direct_retry_base_ms, (uint32_t)10, (uint32_t)5000); if (packet == NULL) { @@ -871,6 +1045,121 @@ uint32_t MyMesh::getDirectRetryEchoDelay(const mesh::Packet* packet) const { uint32_t scaled_wait_millis = (uint32_t) ((((float) bits) * 4.0f) / kbps); return base_wait_millis + scaled_wait_millis; } +bool MyMesh::allowFloodRetry(const mesh::Packet* packet) const { + if (_prefs.disable_fwd || constrain(_prefs.flood_retry_attempts, (uint8_t)0, (uint8_t)3) == 0) { + return false; + } + if (!_prefs.flood_retry_bridge_enabled) { + return true; + } + FloodRetryBridgeState* state = floodRetryBridgeStateFor(packet, true); + if (state == NULL) { + return false; + } + if ((state->heard_mask & state->target_mask) == state->target_mask) { + state->active = false; + return false; + } + return true; +} +void MyMesh::clearFloodRetryBridgeState(const mesh::Packet* packet) { + FloodRetryBridgeState* state = floodRetryBridgeStateFor(packet, false); + if (state != NULL) { + state->active = false; + } +} +void MyMesh::onFloodRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) { + if (event == NULL || packet == NULL) { + return; + } + + const char* time_label = "time_ms"; + if (strcmp(event, "queued") == 0 || strcmp(event, "dropped_queue_full") == 0) { + time_label = "wait_ms"; + } else if (strcmp(event, "resent") == 0 || strcmp(event, "failed_all_tries") == 0 + || strcmp(event, "failure") == 0 || strncmp(event, "dropped_", 8) == 0) { + time_label = "elapsed_ms"; + } else if (strcmp(event, "good") == 0) { + time_label = "echo_ms"; + } + + MESH_DEBUG_PRINTLN("%s flood retry %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%u, %s=%lu)", + getLogDateTime(), + event, + (unsigned int)retry_attempt, + (uint32_t)packet->getPayloadType(), + packet->isRouteDirect() ? "D" : "F", + (uint32_t)packet->payload_len, + (unsigned int)packet->getPathHashCount(), + time_label, + (unsigned long)delay_millis); + + if (_logging) { + File f = openAppend(PACKET_LOG_FILE); + if (f) { + f.print(getLogDateTime()); + f.printf(": FLOOD RETRY %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%u, %s=%lu)\n", + event, + (unsigned int)retry_attempt, + (uint32_t)packet->getPayloadType(), + packet->isRouteDirect() ? "D" : "F", + (uint32_t)packet->payload_len, + (unsigned int)packet->getPathHashCount(), + time_label, + (unsigned long)delay_millis); + f.close(); + } + } + + if (_prefs.flood_retry_bridge_enabled + && (strcmp(event, "failure") == 0 || strcmp(event, "failed_all_tries") == 0 + || strncmp(event, "dropped_", 8) == 0)) { + clearFloodRetryBridgeState(packet); + } +} +bool MyMesh::hasFloodRetryTargetPrefix(const mesh::Packet* packet) const { + if (_prefs.flood_retry_bridge_enabled) { + return false; + } + return floodRetryPrefixMatches(packet); +} +uint8_t MyMesh::getFloodRetryMaxPathLength(const mesh::Packet* packet) const { + uint8_t gate = _prefs.flood_retry_path_gate; + if (gate == FLOOD_RETRY_PATH_GATE_DISABLED) { + return FLOOD_RETRY_PATH_GATE_DISABLED; + } + return gate <= 63 ? gate : 2; +} +uint8_t MyMesh::getFloodRetryMaxAttempts(const mesh::Packet* packet) const { + if (_prefs.disable_fwd) { + return 0; + } + return constrain(_prefs.flood_retry_attempts, (uint8_t)0, (uint8_t)3); +} +bool MyMesh::isFloodRetryEchoTarget(const mesh::Packet* packet, uint8_t progress_marker) const { + if (packet == NULL || !packet->isRouteFlood()) { + return false; + } + if (_prefs.flood_retry_bridge_enabled) { + FloodRetryBridgeState* state = floodRetryBridgeStateFor(packet, false); + if (state == NULL) { + return false; + } + state->heard_mask |= floodRetryBridgeHeardMask(packet, state->source_bucket) & state->target_mask; + bool complete = (state->heard_mask & state->target_mask) == state->target_mask; + if (complete) { + state->active = false; + } + return complete; + } + if (hasFloodRetryPrefixes()) { + return floodRetryLastHopMatches(packet); + } + if (packet->getPathHashCount() <= progress_marker) { + return false; + } + return true; +} uint8_t MyMesh::getDirectRetryMaxAttempts(const mesh::Packet* packet) const { uint8_t configured_attempts = getDirectRetryConfiguredMaxAttempts(); uint8_t total_hops = 0; @@ -1222,6 +1511,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc set_radio_at = revert_radio_at = 0; _logging = false; region_load_active = false; + memset(flood_retry_bridge_states, 0, sizeof(flood_retry_bridge_states)); #if MAX_NEIGHBOURS memset(neighbours, 0, sizeof(neighbours)); @@ -1239,6 +1529,9 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.direct_retry_base_ms = DIRECT_RETRY_ROOFTOP_BASE_MS; _prefs.direct_retry_step_ms = DIRECT_RETRY_ROOFTOP_STEP_MS; _prefs.direct_retry_preset = DIRECT_RETRY_PRESET_ROOFTOP; + _prefs.flood_retry_attempts = 3; + _prefs.flood_retry_path_gate = 2; + _prefs.flood_retry_bridge_enabled = 0; StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); _prefs.node_lat = ADVERT_LAT; _prefs.node_lon = ADVERT_LON; diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 1a2f505c..80c41b02 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -99,6 +99,14 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { RegionEntry* recv_pkt_region; TransportKey default_scope; RateLimiter discover_limiter, anon_limiter; + struct FloodRetryBridgeState { + uint8_t key[MAX_HASH_SIZE]; + uint8_t source_bucket; + uint8_t target_mask; + uint8_t heard_mask; + bool active; + }; + mutable FloodRetryBridgeState flood_retry_bridge_states[MAX_FLOOD_RETRY_SLOTS]; uint32_t pending_discover_tag; unsigned long pending_discover_until; bool region_load_active; @@ -127,6 +135,16 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { uint8_t getDirectRetryPreset() const; uint8_t getDirectRetryConfiguredMaxAttempts() const; uint32_t getDirectRetryAttemptStepMillis() const; + bool hasFloodRetryPrefixes() const; + bool floodRetryPrefixMatches(const mesh::Packet* packet) const; + bool floodRetryLastHopMatches(const mesh::Packet* packet) const; + bool floodRetryPrefixFresh(const uint8_t* prefix, uint8_t prefix_len) const; + int floodRetryBucketForPrefix(const uint8_t* prefix, uint8_t prefix_len, bool require_fresh) const; + int floodRetrySourceBucket(const mesh::Packet* packet) const; + uint8_t floodRetryBridgeTargetMask(uint8_t source_bucket) const; + uint8_t floodRetryBridgeHeardMask(const mesh::Packet* packet, uint8_t source_bucket) const; + FloodRetryBridgeState* floodRetryBridgeStateFor(const mesh::Packet* packet, bool create) const; + void clearFloodRetryBridgeState(const mesh::Packet* packet); void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr); uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood); uint8_t handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data); @@ -159,6 +177,12 @@ protected: uint8_t getDirectRetryMaxAttempts(const mesh::Packet* packet) const override; uint32_t getDirectRetryAttemptDelay(const mesh::Packet* packet, uint8_t attempt_idx) override; void onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) override; + bool allowFloodRetry(const mesh::Packet* packet) const override; + void onFloodRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) override; + bool hasFloodRetryTargetPrefix(const mesh::Packet* packet) const override; + uint8_t getFloodRetryMaxPathLength(const mesh::Packet* packet) const override; + uint8_t getFloodRetryMaxAttempts(const mesh::Packet* packet) const override; + bool isFloodRetryEchoTarget(const mesh::Packet* packet, uint8_t progress_marker) const override; int getInterferenceThreshold() const override { return _prefs.interference_threshold; diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index cccbd36c..a59f0be6 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -269,7 +269,10 @@ void Dispatcher::processRecvPacket(Packet* pkt) { uint8_t priority = (action >> 24) - 1; uint32_t _delay = action & 0xFFFFFF; - _mgr->queueOutbound(pkt, priority, futureMillis(_delay)); + if (!queueOutboundPacket(pkt, priority, _delay)) { + onSendFail(pkt); + releasePacket(pkt); + } } } @@ -320,7 +323,8 @@ void Dispatcher::checkSend() { if (len + outbound->payload_len > MAX_TRANS_UNIT) { MESH_DEBUG_PRINTLN("%s Dispatcher::checkSend(): FATAL: Invalid packet queued... too long, len=%d", getLogDateTime(), len + outbound->payload_len); - _mgr->free(outbound); + onSendFail(outbound); + releasePacket(outbound); outbound = NULL; } else { memcpy(&raw[len], outbound->payload, outbound->payload_len); len += outbound->payload_len; @@ -332,6 +336,7 @@ void Dispatcher::checkSend() { MESH_DEBUG_PRINTLN("%s Dispatcher::loop(): ERROR: send start failed!", getLogDateTime()); logTxFail(outbound, outbound->getRawLength()); + onSendFail(outbound); releasePacket(outbound); // return to pool outbound = NULL; @@ -369,13 +374,21 @@ void Dispatcher::releasePacket(Packet* packet) { _mgr->free(packet); } -void Dispatcher::sendPacket(Packet* packet, uint8_t priority, uint32_t delay_millis) { +bool Dispatcher::queueOutboundPacket(Packet* packet, uint8_t priority, uint32_t delay_millis) { if (!Packet::isValidPathLen(packet->path_len) || packet->payload_len > MAX_PACKET_PAYLOAD) { MESH_DEBUG_PRINTLN("%s Dispatcher::sendPacket(): ERROR: invalid packet... path_len=%d, payload_len=%d", getLogDateTime(), (uint32_t) packet->path_len, (uint32_t) packet->payload_len); - _mgr->free(packet); - } else { - _mgr->queueOutbound(packet, priority, futureMillis(delay_millis)); + return false; } + return _mgr->queueOutbound(packet, priority, futureMillis(delay_millis)); +} + +bool Dispatcher::sendPacket(Packet* packet, uint8_t priority, uint32_t delay_millis) { + if (!queueOutboundPacket(packet, priority, delay_millis)) { + onSendFail(packet); + releasePacket(packet); + return false; + } + return true; } // Utility function -- handles the case where millis() wraps around back to zero diff --git a/src/Dispatcher.h b/src/Dispatcher.h index 90ee5cdb..16a1a964 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -87,7 +87,7 @@ public: virtual Packet* allocNew() = 0; virtual void free(Packet* packet) = 0; - virtual void queueOutbound(Packet* packet, uint8_t priority, uint32_t scheduled_for) = 0; + virtual bool queueOutbound(Packet* packet, uint8_t priority, uint32_t scheduled_for) = 0; virtual Packet* getNextOutbound(uint32_t now) = 0; // by priority virtual int getOutboundCount(uint32_t now) const = 0; virtual int getOutboundTotal() const = 0; @@ -171,6 +171,7 @@ protected: virtual int getAGCResetInterval() const { return 0; } // disabled by default virtual unsigned long getDutyCycleWindowMs() const { return 3600000; } const Packet* getOutboundInFlight() const { return outbound; } + bool queueOutboundPacket(Packet* packet, uint8_t priority, uint32_t delay_millis); public: void begin(); @@ -178,7 +179,7 @@ public: Packet* obtainNewPacket(); void releasePacket(Packet* packet); - void sendPacket(Packet* packet, uint8_t priority, uint32_t delay_millis=0); + bool sendPacket(Packet* packet, uint8_t priority, uint32_t delay_millis=0); unsigned long getTotalAirTime() const { return total_air_time; } unsigned long getReceiveAirTime() const {return rx_air_time; } diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 6c9c8080..254523bc 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -5,6 +5,7 @@ namespace mesh { static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT = 15; static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX = 15; +static const uint8_t FLOOD_RETRY_MAX_ATTEMPTS_DEFAULT = 3; static uint8_t decodeTraceHashSize(uint8_t flags, uint8_t route_bytes) { uint8_t code = flags & 0x03; @@ -41,6 +42,18 @@ void Mesh::begin() { _direct_retries[i].queued = false; _direct_retries[i].active = false; } + for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) { + _flood_retries[i].packet = NULL; + _flood_retries[i].trigger_packet = NULL; + _flood_retries[i].retry_started_at = 0; + _flood_retries[i].retry_at = 0; + _flood_retries[i].retry_delay = 0; + _flood_retries[i].retry_attempts_sent = 0; + _flood_retries[i].priority = 0; + _flood_retries[i].progress_marker = 0; + _flood_retries[i].queued = false; + _flood_retries[i].active = false; + } Dispatcher::begin(); } @@ -59,6 +72,19 @@ void Mesh::loop() { clearDirectRetrySlot(i); } } + + for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) { + if (!_flood_retries[i].active || !_flood_retries[i].queued || !millisHasNowPassed(_flood_retries[i].retry_at)) { + continue; + } + + if (!isFloodRetryQueued(_flood_retries[i].packet)) { + if (_flood_retries[i].packet == getOutboundInFlight()) { + continue; + } + clearFloodRetrySlot(i); + } + } } bool Mesh::allowPacketForward(const mesh::Packet* packet) { @@ -87,16 +113,39 @@ uint32_t Mesh::getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_ // Keep the historical linear spacing while allowing the base wait to vary by platform/profile. return base + ((uint32_t)attempt_idx * 100UL); } +bool Mesh::allowFloodRetry(const Packet* packet) const { + return true; +} +bool Mesh::hasFloodRetryTargetPrefix(const Packet* packet) const { + return false; +} +uint8_t Mesh::getFloodRetryMaxPathLength(const Packet* packet) const { + return 2; +} +uint8_t Mesh::getFloodRetryMaxAttempts(const Packet* packet) const { + return FLOOD_RETRY_MAX_ATTEMPTS_DEFAULT; +} +uint32_t Mesh::getFloodRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx) { + if (packet == NULL) { + return _radio->getEstAirtimeFor(MAX_TRANS_UNIT); + } + + uint32_t max_packet_airtime = _radio->getEstAirtimeFor(MAX_TRANS_UNIT); + uint32_t packet_airtime = _radio->getEstAirtimeFor(packet->getRawLength()); + return max_packet_airtime + (20UL * packet_airtime); +} uint8_t Mesh::getExtraAckTransmitCount() const { return 0; } void Mesh::onSendComplete(Packet* packet) { armDirectRetryOnSendComplete(packet); + armFloodRetryOnSendComplete(packet); } void Mesh::onSendFail(Packet* packet) { clearPendingDirectRetryOnSendFail(packet); + clearPendingFloodRetryOnSendFail(packet); } uint32_t Mesh::getCADFailRetryDelay() const { @@ -114,6 +163,8 @@ int Mesh::searchChannelsByHash(const uint8_t* hash, GroupChannel channels[], int DispatcherAction Mesh::onRecvPacket(Packet* pkt) { if (pkt->isRouteDirect()) { cancelDirectRetryOnEcho(pkt); + } else if (pkt->isRouteFlood()) { + cancelFloodRetryOnEcho(pkt); } if (pkt->isRouteDirect() && pkt->getPayloadType() == PAYLOAD_TYPE_TRACE) { @@ -414,8 +465,10 @@ DispatcherAction Mesh::routeRecvPacket(Packet* packet) { packet->setPathHashCount(n + 1); uint32_t d = getRetransmitDelay(packet); + uint8_t priority = packet->getPathHashCount(); + maybeScheduleFloodRetry(packet, priority); // as this propagates outwards, give it lower and lower priority - return ACTION_RETRANSMIT_DELAYED(packet->getPathHashCount(), d); // give priority to closer sources, than ones further away + return ACTION_RETRANSMIT_DELAYED(priority, d); // give priority to closer sources, than ones further away } return ACTION_RELEASE; } @@ -589,8 +642,7 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { *retry = *packet; uint32_t retry_delay = getDirectRetryAttemptDelay(packet, _direct_retries[i].retry_attempts_sent); - sendPacket(retry, _direct_retries[i].priority, retry_delay); - if (isDirectRetryQueued(retry)) { + if (queueOutboundPacket(retry, _direct_retries[i].priority, retry_delay)) { _direct_retries[i].packet = retry; _direct_retries[i].retry_delay = retry_delay; _direct_retries[i].retry_at = futureMillis(retry_delay); @@ -598,6 +650,7 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { } else { onDirectRetryEvent("dropped_queue_full", retry, retry_delay, _direct_retries[i].retry_attempts_sent + 1); onDirectRetryEvent("failure", retry, elapsed_millis, _direct_retries[i].retry_attempts_sent + 1); + releasePacket(retry); clearDirectRetrySlot(i); } } @@ -620,8 +673,7 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { *retry = *packet; // Start the echo wait only after the initial direct transmission actually completed. - sendPacket(retry, _direct_retries[i].priority, _direct_retries[i].retry_delay); - if (isDirectRetryQueued(retry)) { + if (queueOutboundPacket(retry, _direct_retries[i].priority, _direct_retries[i].retry_delay)) { unsigned long now = _ms->getMillis(); _direct_retries[i].packet = retry; _direct_retries[i].trigger_packet = NULL; @@ -633,6 +685,7 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { } else { onDirectRetryEvent("dropped_queue_full", retry, _direct_retries[i].retry_delay, 1); onDirectRetryEvent("failure", retry, 0, 1); + releasePacket(retry); clearDirectRetrySlot(i); } } @@ -759,6 +812,223 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { _direct_retries[slot_idx].active = true; } +void Mesh::clearFloodRetrySlot(int idx) { + _flood_retries[idx].packet = NULL; + _flood_retries[idx].trigger_packet = NULL; + _flood_retries[idx].retry_started_at = 0; + _flood_retries[idx].retry_at = 0; + _flood_retries[idx].retry_delay = 0; + _flood_retries[idx].retry_attempts_sent = 0; + _flood_retries[idx].priority = 0; + _flood_retries[idx].progress_marker = 0; + _flood_retries[idx].queued = false; + _flood_retries[idx].active = false; +} + +bool Mesh::isFloodRetryQueued(const Packet* packet) const { + for (int i = 0; i < _mgr->getOutboundTotal(); i++) { + if (_mgr->getOutboundByIdx(i) == packet) { + return true; + } + } + return false; +} + +bool Mesh::isFloodRetryEchoTarget(const Packet* packet, uint8_t progress_marker) const { + return packet->isRouteFlood() && packet->getPathHashCount() > progress_marker; +} + +bool Mesh::cancelFloodRetryOnEcho(const Packet* packet) { + uint8_t recv_key[MAX_HASH_SIZE]; + packet->calculatePacketHash(recv_key); + + bool cleared = false; + for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) { + if (!_flood_retries[i].active || memcmp(recv_key, _flood_retries[i].retry_key, MAX_HASH_SIZE) != 0) { + continue; + } + if (!isFloodRetryEchoTarget(packet, _flood_retries[i].progress_marker)) { + continue; + } + + uint32_t echo_millis = _flood_retries[i].retry_started_at == 0 + ? 0 + : (uint32_t)(_ms->getMillis() - _flood_retries[i].retry_started_at); + const Packet* event_packet = _flood_retries[i].queued ? _flood_retries[i].packet : _flood_retries[i].trigger_packet; + onFloodRetryEvent("good", event_packet, echo_millis, _flood_retries[i].retry_attempts_sent + 1); + + if (_flood_retries[i].queued) { + for (int j = 0; j < _mgr->getOutboundTotal(); j++) { + if (_mgr->getOutboundByIdx(j) == _flood_retries[i].packet) { + Packet* pending = _mgr->removeOutboundByIdx(j); + if (pending) { + releasePacket(pending); + } + break; + } + } + } + clearFloodRetrySlot(i); + cleared = true; + } + + return cleared; +} + +void Mesh::armFloodRetryOnSendComplete(const Packet* packet) { + for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) { + if (!_flood_retries[i].active) { + continue; + } + + if (_flood_retries[i].queued) { + if (_flood_retries[i].packet != packet) { + continue; + } + + uint32_t elapsed_millis = _flood_retries[i].retry_started_at == 0 + ? 0 + : (uint32_t)(_ms->getMillis() - _flood_retries[i].retry_started_at); + onFloodRetryEvent("resent", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent + 1); + _flood_retries[i].retry_attempts_sent++; + + uint8_t max_attempts = getFloodRetryMaxAttempts(packet); + if (max_attempts < 1) { + max_attempts = 1; + } else if (max_attempts > FLOOD_RETRY_MAX_ATTEMPTS_DEFAULT) { + max_attempts = FLOOD_RETRY_MAX_ATTEMPTS_DEFAULT; + } + if (_flood_retries[i].retry_attempts_sent >= max_attempts) { + onFloodRetryEvent("failed_all_tries", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent); + onFloodRetryEvent("failure", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent); + clearFloodRetrySlot(i); + continue; + } + + Packet* retry = obtainNewPacket(); + if (retry == NULL) { + onFloodRetryEvent("dropped_no_packet", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent + 1); + onFloodRetryEvent("failure", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent + 1); + clearFloodRetrySlot(i); + continue; + } + + *retry = *packet; + uint32_t retry_delay = getFloodRetryAttemptDelay(packet, _flood_retries[i].retry_attempts_sent); + if (queueOutboundPacket(retry, _flood_retries[i].priority, retry_delay)) { + _flood_retries[i].packet = retry; + _flood_retries[i].retry_delay = retry_delay; + _flood_retries[i].retry_at = futureMillis(retry_delay); + _flood_retries[i].retry_started_at = _ms->getMillis(); + onFloodRetryEvent("queued", retry, retry_delay, _flood_retries[i].retry_attempts_sent + 1); + } else { + onFloodRetryEvent("dropped_queue_full", retry, retry_delay, _flood_retries[i].retry_attempts_sent + 1); + onFloodRetryEvent("failure", retry, elapsed_millis, _flood_retries[i].retry_attempts_sent + 1); + releasePacket(retry); + clearFloodRetrySlot(i); + } + continue; + } + + if (_flood_retries[i].trigger_packet != packet) { + continue; + } + + Packet* retry = obtainNewPacket(); + if (retry == NULL) { + onFloodRetryEvent("dropped_no_packet", packet, _flood_retries[i].retry_delay, 1); + onFloodRetryEvent("failure", packet, 0, 1); + clearFloodRetrySlot(i); + continue; + } + + *retry = *packet; + if (queueOutboundPacket(retry, _flood_retries[i].priority, _flood_retries[i].retry_delay)) { + unsigned long now = _ms->getMillis(); + _flood_retries[i].packet = retry; + _flood_retries[i].trigger_packet = NULL; + _flood_retries[i].queued = true; + _flood_retries[i].retry_at = futureMillis(_flood_retries[i].retry_delay); + _flood_retries[i].retry_started_at = now; + onFloodRetryEvent("queued", retry, _flood_retries[i].retry_delay, 1); + } else { + onFloodRetryEvent("dropped_queue_full", retry, _flood_retries[i].retry_delay, 1); + onFloodRetryEvent("failure", retry, 0, 1); + releasePacket(retry); + clearFloodRetrySlot(i); + } + } +} + +void Mesh::clearPendingFloodRetryOnSendFail(const Packet* packet) { + for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) { + if (!_flood_retries[i].active) { + continue; + } + + if (_flood_retries[i].queued) { + if (_flood_retries[i].packet == packet) { + onFloodRetryEvent("dropped_send_fail", packet, 0, _flood_retries[i].retry_attempts_sent + 1); + onFloodRetryEvent("failure", packet, 0, _flood_retries[i].retry_attempts_sent + 1); + clearFloodRetrySlot(i); + } + continue; + } + + if (_flood_retries[i].trigger_packet == packet) { + onFloodRetryEvent("dropped_send_fail", packet, 0, 1); + onFloodRetryEvent("failure", packet, 0, 1); + clearFloodRetrySlot(i); + } + } +} + +void Mesh::maybeScheduleFloodRetry(const Packet* packet, uint8_t priority) { + if (packet == NULL || !packet->isRouteFlood() || hasFloodRetryTargetPrefix(packet)) { + return; + } + + uint8_t max_path_len = getFloodRetryMaxPathLength(packet); + if (max_path_len != FLOOD_RETRY_PATH_GATE_DISABLED && packet->getPathHashCount() > max_path_len) { + return; + } + + uint8_t max_attempts = getFloodRetryMaxAttempts(packet); + if (max_attempts == 0) { + return; + } + + int slot_idx = -1; + for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) { + if (!_flood_retries[i].active) { + slot_idx = i; + break; + } + } + if (slot_idx < 0) { + onFloodRetryEvent("dropped_no_slot", packet, 0, 0); + onFloodRetryEvent("failure", packet, 0, 0); + return; + } + + if (!allowFloodRetry(packet)) { + return; + } + + uint32_t retry_delay = getFloodRetryAttemptDelay(packet, 0); + packet->calculatePacketHash(_flood_retries[slot_idx].retry_key); + _flood_retries[slot_idx].packet = NULL; + _flood_retries[slot_idx].trigger_packet = const_cast(packet); + _flood_retries[slot_idx].retry_started_at = 0; + _flood_retries[slot_idx].retry_at = 0; + _flood_retries[slot_idx].retry_delay = retry_delay; + _flood_retries[slot_idx].retry_attempts_sent = 0; + _flood_retries[slot_idx].priority = priority; + _flood_retries[slot_idx].progress_marker = packet->getPathHashCount(); + _flood_retries[slot_idx].queued = false; + _flood_retries[slot_idx].active = true; +} + Packet* Mesh::createAdvert(const LocalIdentity& id, const uint8_t* app_data, size_t app_data_len) { if (app_data_len > MAX_ADVERT_DATA_SIZE) return NULL; diff --git a/src/Mesh.h b/src/Mesh.h index 422b79ab..69f05195 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -8,6 +8,14 @@ namespace mesh { #define MAX_DIRECT_RETRY_SLOTS 6 #endif +#ifndef MAX_FLOOD_RETRY_SLOTS + #define MAX_FLOOD_RETRY_SLOTS 6 +#endif + +#ifndef FLOOD_RETRY_PATH_GATE_DISABLED + #define FLOOD_RETRY_PATH_GATE_DISABLED 0xFF +#endif + class GroupChannel { public: uint8_t hash[PATH_HASH_SIZE]; @@ -45,10 +53,25 @@ class Mesh : public Dispatcher { bool active; }; + struct FloodRetryEntry { + Packet* packet; + Packet* trigger_packet; + unsigned long retry_started_at; + unsigned long retry_at; + uint32_t retry_delay; + uint8_t retry_attempts_sent; + uint8_t retry_key[MAX_HASH_SIZE]; + uint8_t priority; + uint8_t progress_marker; + bool queued; + bool active; + }; + RTCClock* _rtc; RNG* _rng; MeshTables* _tables; DirectRetryEntry _direct_retries[MAX_DIRECT_RETRY_SLOTS]; + FloodRetryEntry _flood_retries[MAX_FLOOD_RETRY_SLOTS]; void removeSelfFromPath(Packet* packet); void routeDirectRecvAcks(Packet* packet, uint32_t delay_millis); @@ -61,6 +84,12 @@ class Mesh : public Dispatcher { bool getDirectRetryTarget(const Packet* packet, const uint8_t*& next_hop_hash, uint8_t& next_hop_hash_len, uint8_t& progress_marker, bool& expect_path_growth) const; void maybeScheduleDirectRetry(const Packet* packet, uint8_t priority); + void clearFloodRetrySlot(int idx); + bool isFloodRetryQueued(const Packet* packet) const; + bool cancelFloodRetryOnEcho(const Packet* packet); + void armFloodRetryOnSendComplete(const Packet* packet); + void clearPendingFloodRetryOnSendFail(const Packet* packet); + void maybeScheduleFloodRetry(const Packet* packet, uint8_t priority); //void routeRecvAcks(Packet* packet, uint32_t delay_millis); DispatcherAction forwardMultipartDirect(Packet* pkt); @@ -119,6 +148,41 @@ protected: */ virtual uint32_t getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx); + /** + * \brief Decide whether a FLOOD packet should retry when no downstream echo is overheard. + */ + virtual bool allowFloodRetry(const Packet* packet) const; + + /** + * \brief Return true when this FLOOD packet already carries an application-defined target prefix. + */ + virtual bool hasFloodRetryTargetPrefix(const Packet* packet) const; + + /** + * \returns maximum flood path hash count eligible for retry, or FLOOD_RETRY_PATH_GATE_DISABLED. + */ + virtual uint8_t getFloodRetryMaxPathLength(const Packet* packet) const; + + /** + * \returns maximum number of FLOOD retry transmissions after the initial TX. + */ + virtual uint8_t getFloodRetryMaxAttempts(const Packet* packet) const; + + /** + * \brief Return true when a received FLOOD echo is enough to cancel a pending retry. + */ + virtual bool isFloodRetryEchoTarget(const Packet* packet, uint8_t progress_marker) const; + + /** + * \returns delay before a specific flood retry attempt, where attempt_idx=0 is the first retry. + */ + virtual uint32_t getFloodRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx); + + /** + * \brief Optional hook for logging flood-retry lifecycle events. + */ + virtual void onFloodRetryEvent(const char* event, const Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) { } + /** * \returns number of extra (Direct) ACK transmissions wanted. */ diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index dc206516..27b2eeb4 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -30,6 +30,10 @@ #define DIRECT_RETRY_STEP_MS_MIN 0 #define DIRECT_RETRY_STEP_MS_MAX 5000 #define DIRECT_RETRY_PRESET_DEFAULT DIRECT_RETRY_PRESET_ROOFTOP +#define FLOOD_RETRY_PREFS_MAGIC_0 0xF4 +#define FLOOD_RETRY_PREFS_MAGIC_1 0x52 +#define FLOOD_RETRY_COUNT_MIN 0 +#define FLOOD_RETRY_COUNT_MAX 3 // Believe it or not, this std C function is busted on some platforms! static uint32_t _atoi(const char* sp) { @@ -41,6 +45,30 @@ static uint32_t _atoi(const char* sp) { return n; } +static bool parseUint8Strict(const char* value, uint8_t min_value, uint8_t max_value, uint8_t& result) { + if (value == NULL || *value == 0) { + return false; + } + + uint16_t parsed = 0; + const char* sp = value; + while (*sp) { + if (*sp < '0' || *sp > '9') { + return false; + } + parsed = (uint16_t)((parsed * 10) + (*sp - '0')); + if (parsed > max_value) { + return false; + } + sp++; + } + if (parsed < min_value) { + return false; + } + result = (uint8_t)parsed; + return true; +} + static uint8_t directRetryMarginDbToX4(float margin_db) { int32_t scaled_x4 = (int32_t)((margin_db * 4.0f) + 0.5f); // nearest 0.25 dB return (uint8_t)constrain(scaled_x4, 0, DIRECT_RETRY_SNR_MARGIN_X4_MAX); @@ -97,6 +125,49 @@ static uint16_t directRetryPresetStepDefault(uint8_t preset) { } } +static uint8_t floodRetryPresetCountDefault(uint8_t preset) { + switch (directRetryPresetOrDefault(preset)) { + case DIRECT_RETRY_PRESET_INFRA: + return 1; + case DIRECT_RETRY_PRESET_MOBILE: + case DIRECT_RETRY_PRESET_ROOFTOP: + default: + return 3; + } +} + +static uint8_t floodRetryPresetPathDefault(uint8_t preset) { + switch (directRetryPresetOrDefault(preset)) { + case DIRECT_RETRY_PRESET_INFRA: + return 1; + case DIRECT_RETRY_PRESET_MOBILE: + return 1; + case DIRECT_RETRY_PRESET_ROOFTOP: + default: + return 2; + } +} + +static void applyFloodRetryPreset(NodePrefs* prefs, uint8_t preset) { + prefs->flood_retry_attempts = floodRetryPresetCountDefault(preset); + prefs->flood_retry_path_gate = floodRetryPresetPathDefault(preset); +} + +static uint8_t floodRetryEffectiveCount(const NodePrefs* prefs) { + return constrain(prefs->flood_retry_attempts, (uint8_t)FLOOD_RETRY_COUNT_MIN, (uint8_t)FLOOD_RETRY_COUNT_MAX); +} + +static uint8_t floodRetryPresetForPrefs(const NodePrefs* prefs) { + uint8_t count = floodRetryEffectiveCount(prefs); + uint8_t path_gate = prefs->flood_retry_path_gate; + for (uint8_t preset = DIRECT_RETRY_PRESET_INFRA; preset <= DIRECT_RETRY_PRESET_MOBILE; preset++) { + if (count == floodRetryPresetCountDefault(preset) && path_gate == floodRetryPresetPathDefault(preset)) { + return preset; + } + } + return directRetryPresetOrDefault(prefs->direct_retry_preset); +} + static void applyDirectRetryPreset(NodePrefs* prefs, uint8_t preset) { prefs->direct_retry_preset = directRetryPresetOrDefault(preset); switch (prefs->direct_retry_preset) { @@ -120,6 +191,138 @@ static void applyDirectRetryPreset(NodePrefs* prefs, uint8_t preset) { prefs->direct_retry_snr_margin_db = DIRECT_RETRY_ROOFTOP_MARGIN_X4; break; } + applyFloodRetryPreset(prefs, prefs->direct_retry_preset); +} + +static bool parseFloodRetryPathGate(const char* value, uint8_t& path_gate) { + if (value == NULL) { + return false; + } + if (strcmp(value, "off") == 0 || strcmp(value, "disabled") == 0 || strcmp(value, "disable") == 0) { + path_gate = FLOOD_RETRY_PATH_GATE_DISABLED; + return true; + } + return parseUint8Strict(value, 0, 63, path_gate); +} + +static void formatFloodRetryPathGate(char* dest, uint8_t path_gate) { + if (path_gate == FLOOD_RETRY_PATH_GATE_DISABLED) { + strcpy(dest, "off"); + } else { + sprintf(dest, "%u", (unsigned int)path_gate); + } +} + +static void formatFloodRetryPrefixes(char* dest, const NodePrefs* prefs) { + char* out = dest; + bool first = true; + for (int i = 0; i < FLOOD_RETRY_PREFIX_SLOTS; i++) { + const uint8_t* prefix = prefs->flood_retry_prefixes[i]; + if (prefix[0] == 0 && prefix[1] == 0 && prefix[2] == 0) { + continue; + } + if (!first) { + *out++ = ','; + } + mesh::Utils::toHex(out, prefix, FLOOD_RETRY_PREFIX_LEN); + out += FLOOD_RETRY_PREFIX_LEN * 2; + first = false; + } + *out = 0; +} + +static void formatFloodRetryBridgeBucket(char* dest, const NodePrefs* prefs, uint8_t bucket) { + char* out = dest; + bool first = true; + if (bucket >= FLOOD_RETRY_BRIDGE_BUCKETS) { + *out = 0; + return; + } + for (int i = 0; i < FLOOD_RETRY_BUCKET_PREFIXES; i++) { + const uint8_t* prefix = prefs->flood_retry_bridge_buckets[bucket][i]; + if (prefix[0] == 0 && prefix[1] == 0 && prefix[2] == 0) { + continue; + } + if (!first) { + *out++ = ','; + } + mesh::Utils::toHex(out, prefix, FLOOD_RETRY_PREFIX_LEN); + out += FLOOD_RETRY_PREFIX_LEN * 2; + first = false; + } + *out = 0; +} + +static bool parseFloodRetryPrefixes(NodePrefs* prefs, const char* value) { + uint8_t parsed[FLOOD_RETRY_PREFIX_SLOTS][FLOOD_RETRY_PREFIX_LEN]; + memset(parsed, 0, sizeof(parsed)); + if (value == NULL || value[0] == 0 || strcmp(value, "none") == 0 || strcmp(value, "off") == 0) { + memcpy(prefs->flood_retry_prefixes, parsed, sizeof(prefs->flood_retry_prefixes)); + return true; + } + + char tmp[96]; + StrHelper::strncpy(tmp, value, sizeof(tmp)); + const char* parts[FLOOD_RETRY_PREFIX_SLOTS + 1]; + int num = mesh::Utils::parseTextParts(tmp, parts, FLOOD_RETRY_PREFIX_SLOTS + 1); + if (num > FLOOD_RETRY_PREFIX_SLOTS) { + return false; + } + for (int i = 0; i < num; i++) { + if (strlen(parts[i]) != FLOOD_RETRY_PREFIX_LEN * 2) { + return false; + } + for (int j = 0; j < FLOOD_RETRY_PREFIX_LEN * 2; j++) { + if (!mesh::Utils::isHexChar(parts[i][j])) { + return false; + } + } + if (!mesh::Utils::fromHex(parsed[i], FLOOD_RETRY_PREFIX_LEN, parts[i])) { + return false; + } + const uint8_t* prefix = parsed[i]; + if (prefix[0] == 0 && prefix[1] == 0 && prefix[2] == 0) { + return false; + } + } + memcpy(prefs->flood_retry_prefixes, parsed, sizeof(prefs->flood_retry_prefixes)); + return true; +} + +static bool parseFloodRetryPrefixList(uint8_t dest[][FLOOD_RETRY_PREFIX_LEN], uint8_t max_prefixes, const char* value) { + if (max_prefixes > FLOOD_RETRY_BUCKET_PREFIXES) { + return false; + } + uint8_t parsed[FLOOD_RETRY_BUCKET_PREFIXES][FLOOD_RETRY_PREFIX_LEN]; + memset(parsed, 0, sizeof(parsed)); + if (value == NULL || value[0] == 0 || strcmp(value, "none") == 0 || strcmp(value, "off") == 0) { + memcpy(dest, parsed, max_prefixes * FLOOD_RETRY_PREFIX_LEN); + return true; + } + + char tmp[96]; + StrHelper::strncpy(tmp, value, sizeof(tmp)); + const char* parts[FLOOD_RETRY_BUCKET_PREFIXES + 1]; + int num = mesh::Utils::parseTextParts(tmp, parts, FLOOD_RETRY_BUCKET_PREFIXES + 1); + if (num > max_prefixes) { + return false; + } + for (int i = 0; i < num; i++) { + if (strlen(parts[i]) != FLOOD_RETRY_PREFIX_LEN * 2) { + return false; + } + for (int j = 0; j < FLOOD_RETRY_PREFIX_LEN * 2; j++) { + if (!mesh::Utils::isHexChar(parts[i][j])) { + return false; + } + } + if (!mesh::Utils::fromHex(parsed[i], FLOOD_RETRY_PREFIX_LEN, parts[i]) + || (parsed[i][0] == 0 && parsed[i][1] == 0 && parsed[i][2] == 0)) { + return false; + } + } + memcpy(dest, parsed, max_prefixes * FLOOD_RETRY_PREFIX_LEN); + return true; } static bool parseDirectRetryPreset(const char* value, uint8_t& preset) { @@ -227,6 +430,13 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->direct_retry_preset, sizeof(_prefs->direct_retry_preset)); // 297 size_t retry_step_read = file.read((uint8_t *)&_prefs->direct_retry_step_ms, sizeof(_prefs->direct_retry_step_ms)); // 298 + size_t flood_retry_attempts_read = file.read((uint8_t *)&_prefs->flood_retry_attempts, + sizeof(_prefs->flood_retry_attempts)); // 300 + file.read((uint8_t *)&_prefs->flood_retry_path_gate, sizeof(_prefs->flood_retry_path_gate)); // 301 + file.read((uint8_t *)&_prefs->flood_retry_prefs_magic[0], sizeof(_prefs->flood_retry_prefs_magic)); // 302 + file.read((uint8_t *)&_prefs->flood_retry_prefixes[0][0], sizeof(_prefs->flood_retry_prefixes)); // 304 + file.read((uint8_t *)&_prefs->flood_retry_bridge_enabled, sizeof(_prefs->flood_retry_bridge_enabled)); // 328 + file.read((uint8_t *)&_prefs->flood_retry_bridge_buckets[0][0][0], sizeof(_prefs->flood_retry_bridge_buckets)); // 329 // PowerSaving-only prefs stored radio_fem_rxgain at 291, before direct retry timing existed. if (radio_fem_rxgain_read != sizeof(_prefs->radio_fem_rxgain) && legacy_retry_attempts_read == sizeof(legacy_retry_attempts_or_radio_fem_rxgain) @@ -234,7 +444,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { || _prefs->direct_retry_timing_magic[1] != DIRECT_RETRY_TIMING_MAGIC_1)) { _prefs->radio_fem_rxgain = constrain(legacy_retry_attempts_or_radio_fem_rxgain, 0, 1); } - // next: 298 + // next: 473 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -288,6 +498,20 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { } else { _prefs->direct_retry_step_ms = constrain(_prefs->direct_retry_step_ms, DIRECT_RETRY_STEP_MS_MIN, DIRECT_RETRY_STEP_MS_MAX); } + if (flood_retry_attempts_read != sizeof(_prefs->flood_retry_attempts) + || _prefs->flood_retry_prefs_magic[0] != FLOOD_RETRY_PREFS_MAGIC_0 + || _prefs->flood_retry_prefs_magic[1] != FLOOD_RETRY_PREFS_MAGIC_1) { + applyFloodRetryPreset(_prefs, _prefs->direct_retry_preset); + memset(_prefs->flood_retry_prefixes, 0, sizeof(_prefs->flood_retry_prefixes)); + _prefs->flood_retry_bridge_enabled = 0; + memset(_prefs->flood_retry_bridge_buckets, 0, sizeof(_prefs->flood_retry_bridge_buckets)); + } else { + _prefs->flood_retry_attempts = constrain(_prefs->flood_retry_attempts, FLOOD_RETRY_COUNT_MIN, FLOOD_RETRY_COUNT_MAX); + if (_prefs->flood_retry_path_gate > 63 && _prefs->flood_retry_path_gate != FLOOD_RETRY_PATH_GATE_DISABLED) { + _prefs->flood_retry_path_gate = floodRetryPresetPathDefault(_prefs->direct_retry_preset); + } + _prefs->flood_retry_bridge_enabled = constrain(_prefs->flood_retry_bridge_enabled, 0, 1); + } file.close(); } @@ -360,7 +584,14 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 296 file.write((uint8_t *)&_prefs->direct_retry_preset, sizeof(_prefs->direct_retry_preset)); // 297 file.write((uint8_t *)&_prefs->direct_retry_step_ms, sizeof(_prefs->direct_retry_step_ms)); // 298 - // next: 300 + file.write((uint8_t *)&_prefs->flood_retry_attempts, sizeof(_prefs->flood_retry_attempts)); // 300 + file.write((uint8_t *)&_prefs->flood_retry_path_gate, sizeof(_prefs->flood_retry_path_gate)); // 301 + uint8_t flood_retry_magic[2] = { FLOOD_RETRY_PREFS_MAGIC_0, FLOOD_RETRY_PREFS_MAGIC_1 }; + file.write(flood_retry_magic, sizeof(flood_retry_magic)); // 302 + file.write((uint8_t *)&_prefs->flood_retry_prefixes[0][0], sizeof(_prefs->flood_retry_prefixes)); // 304 + file.write((uint8_t *)&_prefs->flood_retry_bridge_enabled, sizeof(_prefs->flood_retry_bridge_enabled)); // 328 + file.write((uint8_t *)&_prefs->flood_retry_bridge_buckets[0][0][0], sizeof(_prefs->flood_retry_bridge_buckets)); // 329 + // next: 473 file.close(); } @@ -516,6 +747,30 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor)); } else if (memcmp(config, "flood.max", 9) == 0) { sprintf(reply, "> %d", (uint32_t)_prefs->flood_max); + } else if (memcmp(config, "flood.retry.count", 17) == 0) { + sprintf(reply, "> %d", (uint32_t)floodRetryEffectiveCount(_prefs)); + } else if (memcmp(config, "flood.retry.path", 16) == 0) { + char path_gate[8]; + formatFloodRetryPathGate(path_gate, _prefs->flood_retry_path_gate); + sprintf(reply, "> %s", path_gate); + } else if (memcmp(config, "flood.retry.prefixes", 20) == 0) { + formatFloodRetryPrefixes(tmp, _prefs); + sprintf(reply, "> %s", tmp[0] ? tmp : "none"); + } else if (memcmp(config, "flood.retry.bridge", 18) == 0) { + sprintf(reply, "> %s", _prefs->flood_retry_bridge_enabled ? "on" : "off"); + } else if (memcmp(config, "flood.retry.bucket.", 19) == 0) { + uint8_t bucket = atoi(&config[19]); + if (bucket >= 1 && bucket <= FLOOD_RETRY_BRIDGE_BUCKETS) { + formatFloodRetryBridgeBucket(tmp, _prefs, bucket - 1); + sprintf(reply, "> %s", tmp[0] ? tmp : "none"); + } else { + sprintf(reply, "Error, bucket 1-%d", FLOOD_RETRY_BRIDGE_BUCKETS); + } + } else if (memcmp(config, "flood.retry.preset", 18) == 0) { + uint8_t preset = floodRetryPresetForPrefs(_prefs); + sprintf(reply, "> %d,%s", + (uint32_t)preset, + directRetryPresetName(preset)); } else if (memcmp(config, "direct.txdelay", 14) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor)); } else if (memcmp(config, "direct.retry.heard", 18) == 0) { @@ -772,6 +1027,65 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else { strcpy(reply, "Error, max 64"); } + } else if (memcmp(config, "flood.retry.preset ", 19) == 0) { + uint8_t preset; + if (parseDirectRetryPreset(&config[19], preset)) { + applyFloodRetryPreset(_prefs, preset); + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be infra, rooftop, mobile, 0, 1, or 2"); + } + } else if (memcmp(config, "flood.retry.count ", 18) == 0) { + uint8_t count; + if (parseUint8Strict(&config[18], FLOOD_RETRY_COUNT_MIN, FLOOD_RETRY_COUNT_MAX, count)) { + _prefs->flood_retry_attempts = count; + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error, min %d and max %d", FLOOD_RETRY_COUNT_MIN, FLOOD_RETRY_COUNT_MAX); + } + } else if (memcmp(config, "flood.retry.path ", 17) == 0) { + uint8_t path_gate; + if (parseFloodRetryPathGate(&config[17], path_gate)) { + _prefs->flood_retry_path_gate = path_gate; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be 0-63 or off"); + } + } else if (memcmp(config, "flood.retry.prefixes ", 21) == 0) { + if (parseFloodRetryPrefixes(_prefs, &config[21])) { + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, use comma-separated 3-byte hex prefixes"); + } + } else if (memcmp(config, "flood.retry.bridge ", 19) == 0) { + if (memcmp(&config[19], "on", 2) == 0) { + _prefs->flood_retry_bridge_enabled = 1; + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(&config[19], "off", 3) == 0) { + _prefs->flood_retry_bridge_enabled = 0; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be on or off"); + } + } else if (memcmp(config, "flood.retry.bucket ", 19) == 0) { + const char* params = &config[19]; + uint8_t bucket = atoi(params); + const char* list = strchr(params, ' '); + if (bucket < 1 || bucket > FLOOD_RETRY_BRIDGE_BUCKETS || list == NULL || *(list + 1) == 0) { + sprintf(reply, "Error, usage: set flood.retry.bucket <1-%d> ", FLOOD_RETRY_BRIDGE_BUCKETS); + } else if (parseFloodRetryPrefixList(_prefs->flood_retry_bridge_buckets[bucket - 1], + FLOOD_RETRY_BUCKET_PREFIXES, list + 1)) { + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, use up to 8 comma-separated 3-byte hex prefixes"); + } } else if (memcmp(config, "direct.txdelay ", 15) == 0) { float f = atof(&config[15]); if (f >= 0) { @@ -1326,6 +1640,65 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { strcpy(reply, "Error, max 64"); } + } else if (memcmp(config, "flood.retry.preset ", 19) == 0) { + uint8_t preset; + if (parseDirectRetryPreset(&config[19], preset)) { + applyFloodRetryPreset(_prefs, preset); + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be infra, rooftop, mobile, 0, 1, or 2"); + } + } else if (memcmp(config, "flood.retry.count ", 18) == 0) { + uint8_t count; + if (parseUint8Strict(&config[18], FLOOD_RETRY_COUNT_MIN, FLOOD_RETRY_COUNT_MAX, count)) { + _prefs->flood_retry_attempts = count; + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error, min %d and max %d", FLOOD_RETRY_COUNT_MIN, FLOOD_RETRY_COUNT_MAX); + } + } else if (memcmp(config, "flood.retry.path ", 17) == 0) { + uint8_t path_gate; + if (parseFloodRetryPathGate(&config[17], path_gate)) { + _prefs->flood_retry_path_gate = path_gate; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be 0-63 or off"); + } + } else if (memcmp(config, "flood.retry.prefixes ", 21) == 0) { + if (parseFloodRetryPrefixes(_prefs, &config[21])) { + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, use comma-separated 3-byte hex prefixes"); + } + } else if (memcmp(config, "flood.retry.bridge ", 19) == 0) { + if (memcmp(&config[19], "on", 2) == 0) { + _prefs->flood_retry_bridge_enabled = 1; + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(&config[19], "off", 3) == 0) { + _prefs->flood_retry_bridge_enabled = 0; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be on or off"); + } + } else if (memcmp(config, "flood.retry.bucket ", 19) == 0) { + const char* params = &config[19]; + uint8_t bucket = atoi(params); + const char* list = strchr(params, ' '); + if (bucket < 1 || bucket > FLOOD_RETRY_BRIDGE_BUCKETS || list == NULL || *(list + 1) == 0) { + sprintf(reply, "Error, usage: set flood.retry.bucket <1-%d> ", FLOOD_RETRY_BRIDGE_BUCKETS); + } else if (parseFloodRetryPrefixList(_prefs->flood_retry_bridge_buckets[bucket - 1], + FLOOD_RETRY_BUCKET_PREFIXES, list + 1)) { + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, use up to 8 comma-separated 3-byte hex prefixes"); + } } else if (memcmp(config, "direct.txdelay ", 15) == 0) { float f = atof(&config[15]); if (f >= 0) { @@ -1565,6 +1938,30 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor)); } else if (memcmp(config, "flood.max", 9) == 0) { sprintf(reply, "> %d", (uint32_t)_prefs->flood_max); + } else if (memcmp(config, "flood.retry.count", 17) == 0) { + sprintf(reply, "> %d", (uint32_t)floodRetryEffectiveCount(_prefs)); + } else if (memcmp(config, "flood.retry.path", 16) == 0) { + char path_gate[8]; + formatFloodRetryPathGate(path_gate, _prefs->flood_retry_path_gate); + sprintf(reply, "> %s", path_gate); + } else if (memcmp(config, "flood.retry.prefixes", 20) == 0) { + formatFloodRetryPrefixes(tmp, _prefs); + sprintf(reply, "> %s", tmp[0] ? tmp : "none"); + } else if (memcmp(config, "flood.retry.bridge", 18) == 0) { + sprintf(reply, "> %s", _prefs->flood_retry_bridge_enabled ? "on" : "off"); + } else if (memcmp(config, "flood.retry.bucket.", 19) == 0) { + uint8_t bucket = atoi(&config[19]); + if (bucket >= 1 && bucket <= FLOOD_RETRY_BRIDGE_BUCKETS) { + formatFloodRetryBridgeBucket(tmp, _prefs, bucket - 1); + sprintf(reply, "> %s", tmp[0] ? tmp : "none"); + } else { + sprintf(reply, "Error, bucket 1-%d", FLOOD_RETRY_BRIDGE_BUCKETS); + } + } else if (memcmp(config, "flood.retry.preset", 18) == 0) { + uint8_t preset = floodRetryPresetForPrefs(_prefs); + sprintf(reply, "> %d,%s", + (uint32_t)preset, + directRetryPresetName(preset)); } else if (memcmp(config, "direct.txdelay", 14) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor)); } else if (memcmp(config, "direct.retry.heard", 18) == 0) { diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index ea30777a..321c3c8f 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -38,6 +38,19 @@ #define DIRECT_RETRY_MOBILE_STEP_MS 50 #define DIRECT_RETRY_MOBILE_MARGIN_X4 0 +#ifndef FLOOD_RETRY_PREFIX_SLOTS + #define FLOOD_RETRY_PREFIX_SLOTS 8 +#endif +#ifndef FLOOD_RETRY_PREFIX_LEN + #define FLOOD_RETRY_PREFIX_LEN 3 +#endif +#ifndef FLOOD_RETRY_BRIDGE_BUCKETS + #define FLOOD_RETRY_BRIDGE_BUCKETS 6 +#endif +#ifndef FLOOD_RETRY_BUCKET_PREFIXES + #define FLOOD_RETRY_BUCKET_PREFIXES 8 +#endif + struct NodePrefs { // persisted to file float airtime_factor; char node_name[32]; @@ -88,6 +101,12 @@ struct NodePrefs { // persisted to file uint8_t direct_retry_timing_magic[2]; uint8_t direct_retry_preset; uint16_t direct_retry_step_ms; + uint8_t flood_retry_attempts; + uint8_t flood_retry_path_gate; + uint8_t flood_retry_prefs_magic[2]; + uint8_t flood_retry_prefixes[FLOOD_RETRY_PREFIX_SLOTS][FLOOD_RETRY_PREFIX_LEN]; + uint8_t flood_retry_bridge_enabled; + uint8_t flood_retry_bridge_buckets[FLOOD_RETRY_BRIDGE_BUCKETS][FLOOD_RETRY_BUCKET_PREFIXES][FLOOD_RETRY_PREFIX_LEN]; }; class CommonCLICallbacks { diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index ae28acc8..ca81b144 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -1,6 +1,9 @@ #pragma once #include +#if ARDUINO + #include +#endif #ifdef ESP32 #include @@ -30,6 +33,7 @@ public: uint8_t prefix_len; int8_t snr_x4; uint8_t snr_locked; + uint32_t last_heard_millis; }; private: @@ -178,6 +182,8 @@ public: bool hasSeen(const mesh::Packet* packet) override { if (packet->getPayloadType() == PAYLOAD_TYPE_ACK) { + recordRecentRepeater(packet); + uint32_t ack; memcpy(&ack, packet->payload, 4); @@ -295,6 +301,11 @@ public: } else if (!existing.snr_locked) { existing.snr_x4 = weightedSnrX4RoundUp(existing.snr_x4, snr_x4); } +#if ARDUINO + existing.last_heard_millis = millis(); +#else + existing.last_heard_millis = 0; +#endif return true; } @@ -325,6 +336,11 @@ public: slot.prefix_len = prefix_len; slot.snr_x4 = snr_x4; slot.snr_locked = snr_locked ? 1 : 0; +#if ARDUINO + slot.last_heard_millis = millis(); +#else + slot.last_heard_millis = 0; +#endif _next_recent_repeater_idx = (slot_idx + 1) % MAX_RECENT_REPEATERS; return true; } diff --git a/src/helpers/StaticPoolPacketManager.cpp b/src/helpers/StaticPoolPacketManager.cpp index b8926df0..fc2bb059 100644 --- a/src/helpers/StaticPoolPacketManager.cpp +++ b/src/helpers/StaticPoolPacketManager.cpp @@ -83,11 +83,12 @@ void StaticPoolPacketManager::free(mesh::Packet* packet) { unused.add(packet, 0, 0); } -void StaticPoolPacketManager::queueOutbound(mesh::Packet* packet, uint8_t priority, uint32_t scheduled_for) { +bool StaticPoolPacketManager::queueOutbound(mesh::Packet* packet, uint8_t priority, uint32_t scheduled_for) { if (!send_queue.add(packet, priority, scheduled_for)) { MESH_DEBUG_PRINTLN("queueOutbound: send queue full, dropping packet"); - free(packet); + return false; } + return true; } mesh::Packet* StaticPoolPacketManager::getNextOutbound(uint32_t now) { diff --git a/src/helpers/StaticPoolPacketManager.h b/src/helpers/StaticPoolPacketManager.h index 59715b4e..350e85d2 100644 --- a/src/helpers/StaticPoolPacketManager.h +++ b/src/helpers/StaticPoolPacketManager.h @@ -26,7 +26,7 @@ public: mesh::Packet* allocNew() override; void free(mesh::Packet* packet) override; - void queueOutbound(mesh::Packet* packet, uint8_t priority, uint32_t scheduled_for) override; + bool queueOutbound(mesh::Packet* packet, uint8_t priority, uint32_t scheduled_for) override; mesh::Packet* getNextOutbound(uint32_t now) override; int getOutboundCount(uint32_t now) const override; int getOutboundTotal() const override; @@ -35,4 +35,4 @@ public: mesh::Packet* removeOutboundByIdx(int i) override; void queueInbound(mesh::Packet* packet, uint32_t scheduled_for) override; mesh::Packet* getNextInbound(uint32_t now) override; -}; \ No newline at end of file +}; From 93bd0f086947b951b206fcfec5f0c45c2616fc0d Mon Sep 17 00:00:00 2001 From: mikecarper Date: Thu, 7 May 2026 16:40:31 -0700 Subject: [PATCH 046/214] Fix flood retry diagnostics --- docs/cli_commands.md | 4 +- examples/simple_repeater/MyMesh.cpp | 253 ++++++++++++++++++++++------ examples/simple_repeater/MyMesh.h | 3 + src/Mesh.cpp | 50 +++++- src/Mesh.h | 1 + 5 files changed, 255 insertions(+), 56 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 79d319ad..c1041042 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -132,8 +132,8 @@ This document provides an overview of CLI commands that can be sent to MeshCore - Rows are sorted by prefix width (3-byte, 2-byte, 1-byte), then SNR descending. - A full direct retry failure lowers the stored SNR by `0.25 dB`. - If a full failure has no row yet, it first seeds the row at the active retry cutoff + `2.5 dB`, then applies the `0.25 dB` penalty. -- Serial CLI prints all rows (no paging). -- Over LoRa remote CLI, page size is fixed at `4` rows; choose page with `get recent.repeater `. +- Serial CLI page size is fixed at `128` rows; choose page with `get recent.repeater `. +- Over LoRa remote CLI, page size is fixed at `7` rows; choose page with `get recent.repeater `. --- diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 404a26d6..ce848ef2 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1,5 +1,6 @@ #include "MyMesh.h" #include +#include /* ------------------------------ Config -------------------------------- */ @@ -1068,11 +1069,148 @@ void MyMesh::clearFloodRetryBridgeState(const mesh::Packet* packet) { state->active = false; } } +void MyMesh::refreshFloodRetryHeardRecent(const mesh::Packet* packet) { + if (packet == NULL || !packet->isRouteFlood() || packet->getPathHashCount() == 0) { + return; + } + + uint8_t hash_size = packet->getPathHashSize(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + return; + } + + auto* tables = (SimpleMeshTables*)getTables(); + const uint8_t* path = packet->path; + if (_prefs.flood_retry_bridge_enabled) { + FloodRetryBridgeState* state = floodRetryBridgeStateFor(packet, false); + if (state != NULL) { + for (int hop = 0; hop < packet->getPathHashCount(); hop++) { + int bucket = floodRetryBucketForPrefix(path, hash_size, true); + if (bucket >= 0 && bucket != state->source_bucket && (state->target_mask & (uint8_t)(1U << bucket))) { + tables->setRecentRepeater(path, hash_size, packet->_snr, false, true); + } + path += hash_size; + } + return; + } + } + + const uint8_t* heard_prefix = &packet->path[(packet->getPathHashCount() - 1) * hash_size]; + tables->setRecentRepeater(heard_prefix, hash_size, packet->_snr, false, true); +} +void MyMesh::formatFloodRetryPath(char* dest, size_t dest_len, const mesh::Packet* packet) const { + if (dest == NULL || dest_len == 0) { + return; + } + dest[0] = 0; + + if (packet == NULL || packet->getPathHashCount() == 0) { + StrHelper::strncpy(dest, "-", dest_len); + return; + } + + uint8_t hash_size = packet->getPathHashSize(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + StrHelper::strncpy(dest, "invalid", dest_len); + return; + } + + char* out = dest; + size_t remaining = dest_len; + const uint8_t* path = packet->path; + for (int hop = 0; hop < packet->getPathHashCount(); hop++) { + size_t needed = (hop > 0 ? 1 : 0) + ((size_t)hash_size * 2) + 1; + if (remaining < needed) { + if (remaining > 4) { + strcpy(out, "..."); + } + return; + } + if (hop > 0) { + *out++ = '>'; + remaining--; + } + mesh::Utils::toHex(out, path, hash_size); + out += (size_t)hash_size * 2; + remaining -= (size_t)hash_size * 2; + path += hash_size; + } +} +bool MyMesh::formatFloodRetryHeard(char* dest, size_t dest_len, const mesh::Packet* packet) const { + if (dest == NULL || dest_len == 0 || packet == NULL || packet->getPathHashCount() == 0) { + return false; + } + dest[0] = 0; + + uint8_t hash_size = packet->getPathHashSize(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + return false; + } + + char* out = dest; + size_t remaining = dest_len; + bool first = true; + + if (_prefs.flood_retry_bridge_enabled) { + FloodRetryBridgeState* state = floodRetryBridgeStateFor(packet, false); + if (state == NULL) { + return false; + } + const uint8_t* path = packet->path; + for (int hop = 0; hop < packet->getPathHashCount(); hop++) { + int bucket = floodRetryBucketForPrefix(path, hash_size, true); + if (bucket >= 0 && bucket != state->source_bucket && (state->target_mask & (uint8_t)(1U << bucket))) { + size_t needed = (first ? 0 : 1) + 2 + 1 + ((size_t)hash_size * 2) + 1; + if (remaining < needed) { + if (remaining > 4) { + strcpy(out, "..."); + } + return dest[0] != 0; + } + if (!first) { + *out++ = ','; + remaining--; + } + int n = snprintf(out, remaining, "b%d:", bucket + 1); + if (n < 0 || (size_t)n >= remaining) { + return dest[0] != 0; + } + out += n; + remaining -= n; + mesh::Utils::toHex(out, path, hash_size); + out += (size_t)hash_size * 2; + remaining -= (size_t)hash_size * 2; + first = false; + } + path += hash_size; + } + return dest[0] != 0; + } + + const uint8_t* heard_prefix = &packet->path[(packet->getPathHashCount() - 1) * hash_size]; + if (remaining < ((size_t)hash_size * 2) + 1) { + return false; + } + mesh::Utils::toHex(out, heard_prefix, hash_size); + return true; +} void MyMesh::onFloodRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) { if (event == NULL || packet == NULL) { return; } + bool clear_bridge_state = _prefs.flood_retry_bridge_enabled + && (strcmp(event, "good") == 0 || strcmp(event, "failure") == 0 || strcmp(event, "failed_all_tries") == 0 + || strncmp(event, "dropped_", 8) == 0); + + if (clear_bridge_state && strcmp(event, "failure") == 0) { + clearFloodRetryBridgeState(packet); + } + + if (strcmp(event, "failure") == 0) { + return; + } + const char* time_label = "time_ms"; if (strcmp(event, "queued") == 0 || strcmp(event, "dropped_queue_full") == 0) { time_label = "wait_ms"; @@ -1083,7 +1221,17 @@ void MyMesh::onFloodRetryEvent(const char* event, const mesh::Packet* packet, ui time_label = "echo_ms"; } - MESH_DEBUG_PRINTLN("%s flood retry %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%u, %s=%lu)", + char path_log[208]; + char heard_log[96]; + char heard_suffix[112]; + formatFloodRetryPath(path_log, sizeof(path_log), packet); + heard_suffix[0] = 0; + if (strcmp(event, "good") == 0 && formatFloodRetryHeard(heard_log, sizeof(heard_log), packet)) { + refreshFloodRetryHeardRecent(packet); + snprintf(heard_suffix, sizeof(heard_suffix), ", heard=%s", heard_log); + } + + MESH_DEBUG_PRINTLN("%s flood retry %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%u, path=%s%s, %s=%lu)", getLogDateTime(), event, (unsigned int)retry_attempt, @@ -1091,6 +1239,8 @@ void MyMesh::onFloodRetryEvent(const char* event, const mesh::Packet* packet, ui packet->isRouteDirect() ? "D" : "F", (uint32_t)packet->payload_len, (unsigned int)packet->getPathHashCount(), + path_log, + heard_suffix, time_label, (unsigned long)delay_millis); @@ -1098,22 +1248,22 @@ void MyMesh::onFloodRetryEvent(const char* event, const mesh::Packet* packet, ui File f = openAppend(PACKET_LOG_FILE); if (f) { f.print(getLogDateTime()); - f.printf(": FLOOD RETRY %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%u, %s=%lu)\n", + f.printf(": FLOOD RETRY %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%u, path=%s%s, %s=%lu)\n", event, (unsigned int)retry_attempt, (uint32_t)packet->getPayloadType(), packet->isRouteDirect() ? "D" : "F", (uint32_t)packet->payload_len, (unsigned int)packet->getPathHashCount(), + path_log, + heard_suffix, time_label, (unsigned long)delay_millis); f.close(); } } - if (_prefs.flood_retry_bridge_enabled - && (strcmp(event, "failure") == 0 || strcmp(event, "failed_all_tries") == 0 - || strncmp(event, "dropped_", 8) == 0)) { + if (clear_bridge_state) { clearFloodRetryBridgeState(packet); } } @@ -1146,11 +1296,7 @@ bool MyMesh::isFloodRetryEchoTarget(const mesh::Packet* packet, uint8_t progress return false; } state->heard_mask |= floodRetryBridgeHeardMask(packet, state->source_bucket) & state->target_mask; - bool complete = (state->heard_mask & state->target_mask) == state->target_mask; - if (complete) { - state->active = false; - } - return complete; + return (state->heard_mask & state->target_mask) == state->target_mask; } if (hasFloodRetryPrefixes()) { return floodRetryLastHopMatches(packet); @@ -1979,16 +2125,58 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } } } else { - const SimpleMeshTables::RecentRepeaterInfo* sorted_recent[MAX_RECENT_REPEATERS]; + const long page_size = sender_timestamp == 0 ? 128 : 7; + long page_num = 1; + const char* arg = sub; + + if (strncmp(arg, "page ", 5) == 0) { + arg += 5; + while (*arg == ' ') arg++; + } + + if (*arg != 0) { + char* end_ptr = NULL; + page_num = strtol(arg, &end_ptr, 10); + while (end_ptr != NULL && *end_ptr == ' ') end_ptr++; + if (end_ptr == NULL || page_num <= 0 || (end_ptr != NULL && *end_ptr != 0)) { + strcpy(reply, "Err - usage: get recent.repeater [page]"); + return; + } + } + + size_t sorted_size = sizeof(SimpleMeshTables::RecentRepeaterInfo*) * MAX_RECENT_REPEATERS; + const SimpleMeshTables::RecentRepeaterInfo** sorted_recent = + (const SimpleMeshTables::RecentRepeaterInfo**)malloc(sorted_size); + if (sorted_recent == NULL) { + strcpy(reply, "Err - unable to allocate recent repeater view"); + return; + } + int total = buildSortedRecentRepeaterView(tables, sorted_recent, MAX_RECENT_REPEATERS); if (total <= 0) { strcpy(reply, "> none"); } else { + int total_pages = (total + (int)page_size - 1) / (int)page_size; + if (page_num > total_pages) { + sprintf(reply, "> none (page=%ld/%d)", page_num, total_pages); + free(sorted_recent); + return; + } + + int offset = ((int)page_num - 1) * (int)page_size; + int limit = total - offset; + if (limit > (int)page_size) { + limit = (int)page_size; + } + if (sender_timestamp == 0) { - // Serial CLI: print all entries (no paging). - Serial.printf("Recent repeater table (3-byte,2-byte,1-byte; SNR desc, total=%d):\n", total); - for (int i = 0; i < total; i++) { - const auto* info = sorted_recent[i]; + Serial.printf("Recent repeater table (3-byte,2-byte,1-byte; SNR desc, page=%ld/%d, n=%d/%d):\n", + page_num, + total_pages, + limit, + total); + for (int i = 0; i < limit; i++) { + const auto* info = sorted_recent[offset + i]; if (info == NULL) { continue; } @@ -2002,40 +2190,8 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply snr_text, info->snr_locked ? ",l" : ""); } - sprintf(reply, "> n=%d/%d", total, total); + sprintf(reply, "> page=%ld/%d n=%d/%d", page_num, total_pages, limit, total); } else { - // Remote CLI: page by fixed size to fit packet-limited reply payload. - long page_num = 1; - const long page_size = 4; - const char* arg = sub; - - if (strncmp(arg, "page ", 5) == 0) { - arg += 5; - while (*arg == ' ') arg++; - } - - if (*arg != 0) { - char* end_ptr = NULL; - page_num = strtol(arg, &end_ptr, 10); - while (end_ptr != NULL && *end_ptr == ' ') end_ptr++; - if (end_ptr == NULL || page_num <= 0 || (end_ptr != NULL && *end_ptr != 0)) { - strcpy(reply, "Err - usage: get recent.repeater [page]"); - return; - } - } - - int total_pages = (total + (int)page_size - 1) / (int)page_size; - if (page_num > total_pages) { - sprintf(reply, "> none (page=%ld/%d)", page_num, total_pages); - return; - } - - int offset = ((int)page_num - 1) * (int)page_size; - int limit = total - offset; - if (limit > (int)page_size) { - limit = (int)page_size; - } - int written = snprintf(reply, 160, "> page=%ld/%d n=%d/%d", page_num, total_pages, limit, total); bool truncated = false; if (written < 0) { @@ -2075,6 +2231,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } } } + free(sorted_recent); } } else if (memcmp(command, "discover.neighbors", 18) == 0) { const char* sub = command + 18; diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 80c41b02..c189bda9 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -145,6 +145,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { uint8_t floodRetryBridgeHeardMask(const mesh::Packet* packet, uint8_t source_bucket) const; FloodRetryBridgeState* floodRetryBridgeStateFor(const mesh::Packet* packet, bool create) const; void clearFloodRetryBridgeState(const mesh::Packet* packet); + void refreshFloodRetryHeardRecent(const mesh::Packet* packet); + void formatFloodRetryPath(char* dest, size_t dest_len, const mesh::Packet* packet) const; + bool formatFloodRetryHeard(char* dest, size_t dest_len, const mesh::Packet* packet) const; void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr); uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood); uint8_t handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data); diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 254523bc..4671be44 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -51,6 +51,7 @@ void Mesh::begin() { _flood_retries[i].retry_attempts_sent = 0; _flood_retries[i].priority = 0; _flood_retries[i].progress_marker = 0; + _flood_retries[i].waiting_final_echo = false; _flood_retries[i].queued = false; _flood_retries[i].active = false; } @@ -74,7 +75,25 @@ void Mesh::loop() { } for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) { - if (!_flood_retries[i].active || !_flood_retries[i].queued || !millisHasNowPassed(_flood_retries[i].retry_at)) { + if (!_flood_retries[i].active) { + continue; + } + + if (_flood_retries[i].waiting_final_echo) { + if (!millisHasNowPassed(_flood_retries[i].retry_at)) { + continue; + } + + uint32_t elapsed_millis = _flood_retries[i].retry_started_at == 0 + ? 0 + : (uint32_t)(_ms->getMillis() - _flood_retries[i].retry_started_at); + onFloodRetryEvent("failed_all_tries", _flood_retries[i].packet, elapsed_millis, _flood_retries[i].retry_attempts_sent); + onFloodRetryEvent("failure", _flood_retries[i].packet, elapsed_millis, _flood_retries[i].retry_attempts_sent); + clearFloodRetrySlot(i); + continue; + } + + if (!_flood_retries[i].queued || !millisHasNowPassed(_flood_retries[i].retry_at)) { continue; } @@ -813,6 +832,9 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { } void Mesh::clearFloodRetrySlot(int idx) { + if (_flood_retries[idx].waiting_final_echo && _flood_retries[idx].packet != NULL) { + releasePacket(_flood_retries[idx].packet); + } _flood_retries[idx].packet = NULL; _flood_retries[idx].trigger_packet = NULL; _flood_retries[idx].retry_started_at = 0; @@ -821,6 +843,7 @@ void Mesh::clearFloodRetrySlot(int idx) { _flood_retries[idx].retry_attempts_sent = 0; _flood_retries[idx].priority = 0; _flood_retries[idx].progress_marker = 0; + _flood_retries[idx].waiting_final_echo = false; _flood_retries[idx].queued = false; _flood_retries[idx].active = false; } @@ -854,8 +877,10 @@ bool Mesh::cancelFloodRetryOnEcho(const Packet* packet) { uint32_t echo_millis = _flood_retries[i].retry_started_at == 0 ? 0 : (uint32_t)(_ms->getMillis() - _flood_retries[i].retry_started_at); - const Packet* event_packet = _flood_retries[i].queued ? _flood_retries[i].packet : _flood_retries[i].trigger_packet; - onFloodRetryEvent("good", event_packet, echo_millis, _flood_retries[i].retry_attempts_sent + 1); + uint8_t retry_attempt = _flood_retries[i].waiting_final_echo + ? _flood_retries[i].retry_attempts_sent + : _flood_retries[i].retry_attempts_sent + 1; + onFloodRetryEvent("good", packet, echo_millis, retry_attempt); if (_flood_retries[i].queued) { for (int j = 0; j < _mgr->getOutboundTotal(); j++) { @@ -899,9 +924,19 @@ void Mesh::armFloodRetryOnSendComplete(const Packet* packet) { max_attempts = FLOOD_RETRY_MAX_ATTEMPTS_DEFAULT; } if (_flood_retries[i].retry_attempts_sent >= max_attempts) { - onFloodRetryEvent("failed_all_tries", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent); - onFloodRetryEvent("failure", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent); - clearFloodRetrySlot(i); + Packet* final_wait = obtainNewPacket(); + if (final_wait == NULL) { + onFloodRetryEvent("dropped_no_packet", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent); + onFloodRetryEvent("failure", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent); + clearFloodRetrySlot(i); + continue; + } + + *final_wait = *packet; + _flood_retries[i].packet = final_wait; + _flood_retries[i].retry_at = futureMillis(_flood_retries[i].retry_delay); + _flood_retries[i].waiting_final_echo = true; + _flood_retries[i].queued = false; continue; } @@ -920,6 +955,7 @@ void Mesh::armFloodRetryOnSendComplete(const Packet* packet) { _flood_retries[i].retry_delay = retry_delay; _flood_retries[i].retry_at = futureMillis(retry_delay); _flood_retries[i].retry_started_at = _ms->getMillis(); + _flood_retries[i].waiting_final_echo = false; onFloodRetryEvent("queued", retry, retry_delay, _flood_retries[i].retry_attempts_sent + 1); } else { onFloodRetryEvent("dropped_queue_full", retry, retry_delay, _flood_retries[i].retry_attempts_sent + 1); @@ -948,6 +984,7 @@ void Mesh::armFloodRetryOnSendComplete(const Packet* packet) { _flood_retries[i].packet = retry; _flood_retries[i].trigger_packet = NULL; _flood_retries[i].queued = true; + _flood_retries[i].waiting_final_echo = false; _flood_retries[i].retry_at = futureMillis(_flood_retries[i].retry_delay); _flood_retries[i].retry_started_at = now; onFloodRetryEvent("queued", retry, _flood_retries[i].retry_delay, 1); @@ -1025,6 +1062,7 @@ void Mesh::maybeScheduleFloodRetry(const Packet* packet, uint8_t priority) { _flood_retries[slot_idx].retry_attempts_sent = 0; _flood_retries[slot_idx].priority = priority; _flood_retries[slot_idx].progress_marker = packet->getPathHashCount(); + _flood_retries[slot_idx].waiting_final_echo = false; _flood_retries[slot_idx].queued = false; _flood_retries[slot_idx].active = true; } diff --git a/src/Mesh.h b/src/Mesh.h index 69f05195..33adf918 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -63,6 +63,7 @@ class Mesh : public Dispatcher { uint8_t retry_key[MAX_HASH_SIZE]; uint8_t priority; uint8_t progress_marker; + bool waiting_final_echo; bool queued; bool active; }; From e0b19a1f60b0be43a1cbc33a639568aad1a6a483 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Thu, 7 May 2026 16:51:49 -0700 Subject: [PATCH 047/214] Fix direct retry diagnostics --- docs/cli_commands.md | 4 +- examples/simple_repeater/MyMesh.cpp | 91 +++++++++++++++++------------ src/Mesh.cpp | 65 +++++++++++++++++---- src/Mesh.h | 1 + 4 files changed, 109 insertions(+), 52 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index f9c08259..bf77872f 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -132,8 +132,8 @@ This document provides an overview of CLI commands that can be sent to MeshCore - Rows are sorted by prefix width (3-byte, 2-byte, 1-byte), then SNR descending. - A full direct retry failure lowers the stored SNR by `0.25 dB`. - If a full failure has no row yet, it first seeds the row at the active retry cutoff + `2.5 dB`, then applies the `0.25 dB` penalty. -- Serial CLI prints all rows (no paging). -- Over LoRa remote CLI, page size is fixed at `4` rows; choose page with `get recent.repeater `. +- Serial CLI page size is fixed at `128` rows; choose page with `get recent.repeater `. +- Over LoRa remote CLI, page size is fixed at `7` rows; choose page with `get recent.repeater `. --- diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 6b3e8d6a..f9a961b9 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1,5 +1,6 @@ #include "MyMesh.h" #include +#include /* ------------------------------ Config -------------------------------- */ @@ -661,6 +662,9 @@ void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, u if (packet == NULL) { return; } + if (strcmp(event, "failure") == 0) { + return; + } uint8_t prefix[MAX_ROUTE_HASH_BYTES] = {0}; uint8_t prefix_len = 0; @@ -1686,16 +1690,58 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } } } else { - const SimpleMeshTables::RecentRepeaterInfo* sorted_recent[MAX_RECENT_REPEATERS]; + const long page_size = sender_timestamp == 0 ? 128 : 7; + long page_num = 1; + const char* arg = sub; + + if (strncmp(arg, "page ", 5) == 0) { + arg += 5; + while (*arg == ' ') arg++; + } + + if (*arg != 0) { + char* end_ptr = NULL; + page_num = strtol(arg, &end_ptr, 10); + while (end_ptr != NULL && *end_ptr == ' ') end_ptr++; + if (end_ptr == NULL || page_num <= 0 || (end_ptr != NULL && *end_ptr != 0)) { + strcpy(reply, "Err - usage: get recent.repeater [page]"); + return; + } + } + + size_t sorted_size = sizeof(SimpleMeshTables::RecentRepeaterInfo*) * MAX_RECENT_REPEATERS; + const SimpleMeshTables::RecentRepeaterInfo** sorted_recent = + (const SimpleMeshTables::RecentRepeaterInfo**)malloc(sorted_size); + if (sorted_recent == NULL) { + strcpy(reply, "Err - unable to allocate recent repeater view"); + return; + } + int total = buildSortedRecentRepeaterView(tables, sorted_recent, MAX_RECENT_REPEATERS); if (total <= 0) { strcpy(reply, "> none"); } else { + int total_pages = (total + (int)page_size - 1) / (int)page_size; + if (page_num > total_pages) { + sprintf(reply, "> none (page=%ld/%d)", page_num, total_pages); + free(sorted_recent); + return; + } + + int offset = ((int)page_num - 1) * (int)page_size; + int limit = total - offset; + if (limit > (int)page_size) { + limit = (int)page_size; + } + if (sender_timestamp == 0) { - // Serial CLI: print all entries (no paging). - Serial.printf("Recent repeater table (3-byte,2-byte,1-byte; SNR desc, total=%d):\n", total); - for (int i = 0; i < total; i++) { - const auto* info = sorted_recent[i]; + Serial.printf("Recent repeater table (3-byte,2-byte,1-byte; SNR desc, page=%ld/%d, n=%d/%d):\n", + page_num, + total_pages, + limit, + total); + for (int i = 0; i < limit; i++) { + const auto* info = sorted_recent[offset + i]; if (info == NULL) { continue; } @@ -1709,40 +1755,8 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply snr_text, info->snr_locked ? ",l" : ""); } - sprintf(reply, "> n=%d/%d", total, total); + sprintf(reply, "> page=%ld/%d n=%d/%d", page_num, total_pages, limit, total); } else { - // Remote CLI: page by fixed size to fit packet-limited reply payload. - long page_num = 1; - const long page_size = 4; - const char* arg = sub; - - if (strncmp(arg, "page ", 5) == 0) { - arg += 5; - while (*arg == ' ') arg++; - } - - if (*arg != 0) { - char* end_ptr = NULL; - page_num = strtol(arg, &end_ptr, 10); - while (end_ptr != NULL && *end_ptr == ' ') end_ptr++; - if (end_ptr == NULL || page_num <= 0 || (end_ptr != NULL && *end_ptr != 0)) { - strcpy(reply, "Err - usage: get recent.repeater [page]"); - return; - } - } - - int total_pages = (total + (int)page_size - 1) / (int)page_size; - if (page_num > total_pages) { - sprintf(reply, "> none (page=%ld/%d)", page_num, total_pages); - return; - } - - int offset = ((int)page_num - 1) * (int)page_size; - int limit = total - offset; - if (limit > (int)page_size) { - limit = (int)page_size; - } - int written = snprintf(reply, 160, "> page=%ld/%d n=%d/%d", page_num, total_pages, limit, total); bool truncated = false; if (written < 0) { @@ -1782,6 +1796,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } } } + free(sorted_recent); } } else if (memcmp(command, "discover.neighbors", 18) == 0) { const char* sub = command + 18; diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 6c9c8080..42bcf5b1 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -38,6 +38,7 @@ void Mesh::begin() { _direct_retries[i].priority = 0; _direct_retries[i].progress_marker = 0; _direct_retries[i].expect_path_growth = false; + _direct_retries[i].waiting_final_echo = false; _direct_retries[i].queued = false; _direct_retries[i].active = false; } @@ -48,7 +49,25 @@ void Mesh::loop() { Dispatcher::loop(); for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { - if (!_direct_retries[i].active || !_direct_retries[i].queued || !millisHasNowPassed(_direct_retries[i].retry_at)) { + if (!_direct_retries[i].active) { + continue; + } + + if (_direct_retries[i].waiting_final_echo) { + if (!millisHasNowPassed(_direct_retries[i].retry_at)) { + continue; + } + + uint32_t elapsed_millis = _direct_retries[i].retry_started_at == 0 + ? 0 + : (uint32_t)(_ms->getMillis() - _direct_retries[i].retry_started_at); + onDirectRetryEvent("failed_all_tries", _direct_retries[i].packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); + onDirectRetryEvent("failure", _direct_retries[i].packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); + clearDirectRetrySlot(i); + continue; + } + + if (!_direct_retries[i].queued || !millisHasNowPassed(_direct_retries[i].retry_at)) { continue; } @@ -470,6 +489,9 @@ void Mesh::routeDirectRecvAcks(Packet* packet, uint32_t delay_millis) { } void Mesh::clearDirectRetrySlot(int idx) { + if (_direct_retries[idx].waiting_final_echo && _direct_retries[idx].packet != NULL) { + releasePacket(_direct_retries[idx].packet); + } _direct_retries[idx].packet = NULL; _direct_retries[idx].trigger_packet = NULL; _direct_retries[idx].retry_started_at = 0; @@ -480,6 +502,7 @@ void Mesh::clearDirectRetrySlot(int idx) { _direct_retries[idx].priority = 0; _direct_retries[idx].progress_marker = 0; _direct_retries[idx].expect_path_growth = false; + _direct_retries[idx].waiting_final_echo = false; _direct_retries[idx].queued = false; _direct_retries[idx].active = false; } @@ -516,7 +539,7 @@ bool Mesh::cancelDirectRetryOnEcho(const Packet* packet) { } int8_t echo_snr_x4 = packet->_snr; - if (_direct_retries[i].queued) { + if (_direct_retries[i].queued || _direct_retries[i].waiting_final_echo) { if (_direct_retries[i].packet != NULL) { // Success quality comes from the received downstream echo, not the original upstream RX. _direct_retries[i].packet->_snr = echo_snr_x4; @@ -524,14 +547,19 @@ bool Mesh::cancelDirectRetryOnEcho(const Packet* packet) { uint32_t echo_millis = _direct_retries[i].echo_wait_started_at == 0 ? 0 : (uint32_t)(_ms->getMillis() - _direct_retries[i].echo_wait_started_at); - onDirectRetryEvent("good", _direct_retries[i].packet, echo_millis, _direct_retries[i].retry_attempts_sent + 1); - for (int j = 0; j < _mgr->getOutboundTotal(); j++) { - if (_mgr->getOutboundByIdx(j) == _direct_retries[i].packet) { - Packet* pending = _mgr->removeOutboundByIdx(j); - if (pending) { - releasePacket(pending); + uint8_t retry_attempt = _direct_retries[i].waiting_final_echo + ? _direct_retries[i].retry_attempts_sent + : _direct_retries[i].retry_attempts_sent + 1; + onDirectRetryEvent("good", _direct_retries[i].packet, echo_millis, retry_attempt); + if (_direct_retries[i].queued) { + for (int j = 0; j < _mgr->getOutboundTotal(); j++) { + if (_mgr->getOutboundByIdx(j) == _direct_retries[i].packet) { + Packet* pending = _mgr->removeOutboundByIdx(j); + if (pending) { + releasePacket(pending); + } + break; } - break; } } clearDirectRetrySlot(i); @@ -573,9 +601,19 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { max_attempts = DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX; } if (_direct_retries[i].retry_attempts_sent >= max_attempts) { - onDirectRetryEvent("failed_all_tries", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); - onDirectRetryEvent("failure", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); - clearDirectRetrySlot(i); + Packet* final_wait = obtainNewPacket(); + if (final_wait == NULL) { + onDirectRetryEvent("dropped_no_packet", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); + onDirectRetryEvent("failure", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); + clearDirectRetrySlot(i); + continue; + } + + *final_wait = *packet; + _direct_retries[i].packet = final_wait; + _direct_retries[i].retry_at = futureMillis(_direct_retries[i].retry_delay); + _direct_retries[i].waiting_final_echo = true; + _direct_retries[i].queued = false; continue; } @@ -594,6 +632,7 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { _direct_retries[i].packet = retry; _direct_retries[i].retry_delay = retry_delay; _direct_retries[i].retry_at = futureMillis(retry_delay); + _direct_retries[i].waiting_final_echo = false; onDirectRetryEvent("queued", retry, retry_delay, _direct_retries[i].retry_attempts_sent + 1); } else { onDirectRetryEvent("dropped_queue_full", retry, retry_delay, _direct_retries[i].retry_attempts_sent + 1); @@ -626,6 +665,7 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { _direct_retries[i].packet = retry; _direct_retries[i].trigger_packet = NULL; _direct_retries[i].queued = true; + _direct_retries[i].waiting_final_echo = false; _direct_retries[i].retry_at = futureMillis(_direct_retries[i].retry_delay); _direct_retries[i].retry_started_at = now; _direct_retries[i].echo_wait_started_at = now; @@ -755,6 +795,7 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { _direct_retries[slot_idx].priority = priority; _direct_retries[slot_idx].progress_marker = progress_marker; _direct_retries[slot_idx].expect_path_growth = expect_path_growth; + _direct_retries[slot_idx].waiting_final_echo = false; _direct_retries[slot_idx].queued = false; _direct_retries[slot_idx].active = true; } diff --git a/src/Mesh.h b/src/Mesh.h index 422b79ab..91aec9a6 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -41,6 +41,7 @@ class Mesh : public Dispatcher { uint8_t priority; uint8_t progress_marker; bool expect_path_growth; + bool waiting_final_echo; bool queued; bool active; }; From 8dcd4975d5d4e3b6aa05f7d5bc6c67b5b0b85d3d Mon Sep 17 00:00:00 2001 From: mikecarper Date: Thu, 7 May 2026 16:55:37 -0700 Subject: [PATCH 048/214] Delay direct retry final failure --- examples/simple_repeater/MyMesh.cpp | 3 ++ src/Mesh.cpp | 65 +++++++++++++++++++++++------ src/Mesh.h | 1 + 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index ce848ef2..ca86d27e 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -662,6 +662,9 @@ void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, u if (packet == NULL) { return; } + if (strcmp(event, "failure") == 0) { + return; + } uint8_t prefix[MAX_ROUTE_HASH_BYTES] = {0}; uint8_t prefix_len = 0; diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 4671be44..109b1384 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -39,6 +39,7 @@ void Mesh::begin() { _direct_retries[i].priority = 0; _direct_retries[i].progress_marker = 0; _direct_retries[i].expect_path_growth = false; + _direct_retries[i].waiting_final_echo = false; _direct_retries[i].queued = false; _direct_retries[i].active = false; } @@ -62,7 +63,25 @@ void Mesh::loop() { Dispatcher::loop(); for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { - if (!_direct_retries[i].active || !_direct_retries[i].queued || !millisHasNowPassed(_direct_retries[i].retry_at)) { + if (!_direct_retries[i].active) { + continue; + } + + if (_direct_retries[i].waiting_final_echo) { + if (!millisHasNowPassed(_direct_retries[i].retry_at)) { + continue; + } + + uint32_t elapsed_millis = _direct_retries[i].retry_started_at == 0 + ? 0 + : (uint32_t)(_ms->getMillis() - _direct_retries[i].retry_started_at); + onDirectRetryEvent("failed_all_tries", _direct_retries[i].packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); + onDirectRetryEvent("failure", _direct_retries[i].packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); + clearDirectRetrySlot(i); + continue; + } + + if (!_direct_retries[i].queued || !millisHasNowPassed(_direct_retries[i].retry_at)) { continue; } @@ -542,6 +561,9 @@ void Mesh::routeDirectRecvAcks(Packet* packet, uint32_t delay_millis) { } void Mesh::clearDirectRetrySlot(int idx) { + if (_direct_retries[idx].waiting_final_echo && _direct_retries[idx].packet != NULL) { + releasePacket(_direct_retries[idx].packet); + } _direct_retries[idx].packet = NULL; _direct_retries[idx].trigger_packet = NULL; _direct_retries[idx].retry_started_at = 0; @@ -552,6 +574,7 @@ void Mesh::clearDirectRetrySlot(int idx) { _direct_retries[idx].priority = 0; _direct_retries[idx].progress_marker = 0; _direct_retries[idx].expect_path_growth = false; + _direct_retries[idx].waiting_final_echo = false; _direct_retries[idx].queued = false; _direct_retries[idx].active = false; } @@ -588,7 +611,7 @@ bool Mesh::cancelDirectRetryOnEcho(const Packet* packet) { } int8_t echo_snr_x4 = packet->_snr; - if (_direct_retries[i].queued) { + if (_direct_retries[i].queued || _direct_retries[i].waiting_final_echo) { if (_direct_retries[i].packet != NULL) { // Success quality comes from the received downstream echo, not the original upstream RX. _direct_retries[i].packet->_snr = echo_snr_x4; @@ -596,14 +619,19 @@ bool Mesh::cancelDirectRetryOnEcho(const Packet* packet) { uint32_t echo_millis = _direct_retries[i].echo_wait_started_at == 0 ? 0 : (uint32_t)(_ms->getMillis() - _direct_retries[i].echo_wait_started_at); - onDirectRetryEvent("good", _direct_retries[i].packet, echo_millis, _direct_retries[i].retry_attempts_sent + 1); - for (int j = 0; j < _mgr->getOutboundTotal(); j++) { - if (_mgr->getOutboundByIdx(j) == _direct_retries[i].packet) { - Packet* pending = _mgr->removeOutboundByIdx(j); - if (pending) { - releasePacket(pending); + uint8_t retry_attempt = _direct_retries[i].waiting_final_echo + ? _direct_retries[i].retry_attempts_sent + : _direct_retries[i].retry_attempts_sent + 1; + onDirectRetryEvent("good", _direct_retries[i].packet, echo_millis, retry_attempt); + if (_direct_retries[i].queued) { + for (int j = 0; j < _mgr->getOutboundTotal(); j++) { + if (_mgr->getOutboundByIdx(j) == _direct_retries[i].packet) { + Packet* pending = _mgr->removeOutboundByIdx(j); + if (pending) { + releasePacket(pending); + } + break; } - break; } } clearDirectRetrySlot(i); @@ -645,9 +673,19 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { max_attempts = DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX; } if (_direct_retries[i].retry_attempts_sent >= max_attempts) { - onDirectRetryEvent("failed_all_tries", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); - onDirectRetryEvent("failure", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); - clearDirectRetrySlot(i); + Packet* final_wait = obtainNewPacket(); + if (final_wait == NULL) { + onDirectRetryEvent("dropped_no_packet", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); + onDirectRetryEvent("failure", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); + clearDirectRetrySlot(i); + continue; + } + + *final_wait = *packet; + _direct_retries[i].packet = final_wait; + _direct_retries[i].retry_at = futureMillis(_direct_retries[i].retry_delay); + _direct_retries[i].waiting_final_echo = true; + _direct_retries[i].queued = false; continue; } @@ -665,6 +703,7 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { _direct_retries[i].packet = retry; _direct_retries[i].retry_delay = retry_delay; _direct_retries[i].retry_at = futureMillis(retry_delay); + _direct_retries[i].waiting_final_echo = false; onDirectRetryEvent("queued", retry, retry_delay, _direct_retries[i].retry_attempts_sent + 1); } else { onDirectRetryEvent("dropped_queue_full", retry, retry_delay, _direct_retries[i].retry_attempts_sent + 1); @@ -697,6 +736,7 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { _direct_retries[i].packet = retry; _direct_retries[i].trigger_packet = NULL; _direct_retries[i].queued = true; + _direct_retries[i].waiting_final_echo = false; _direct_retries[i].retry_at = futureMillis(_direct_retries[i].retry_delay); _direct_retries[i].retry_started_at = now; _direct_retries[i].echo_wait_started_at = now; @@ -827,6 +867,7 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { _direct_retries[slot_idx].priority = priority; _direct_retries[slot_idx].progress_marker = progress_marker; _direct_retries[slot_idx].expect_path_growth = expect_path_growth; + _direct_retries[slot_idx].waiting_final_echo = false; _direct_retries[slot_idx].queued = false; _direct_retries[slot_idx].active = true; } diff --git a/src/Mesh.h b/src/Mesh.h index 33adf918..f76334a6 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -49,6 +49,7 @@ class Mesh : public Dispatcher { uint8_t priority; uint8_t progress_marker; bool expect_path_growth; + bool waiting_final_echo; bool queued; bool active; }; From 3a6766b0fddb6f29c105a3f08a1009ed2cdcda12 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 8 May 2026 00:49:33 -0700 Subject: [PATCH 049/214] Improve flood retry bridge buckets --- build-iotthinks.sh | 23 ++++---- docs/cli_commands.md | 18 +++++- examples/simple_repeater/MyMesh.cpp | 92 ++++++++++++++++++++++++----- examples/simple_repeater/MyMesh.h | 10 +++- src/helpers/CommonCLI.cpp | 84 +++++++++++++++++--------- src/helpers/CommonCLI.h | 17 +++++- src/helpers/SimpleMeshTables.h | 2 - 7 files changed, 183 insertions(+), 63 deletions(-) mode change 100644 => 100755 build-iotthinks.sh diff --git a/build-iotthinks.sh b/build-iotthinks.sh old mode 100644 new mode 100755 index 7c654482..3e41b4d6 --- a/build-iotthinks.sh +++ b/build-iotthinks.sh @@ -1,10 +1,11 @@ -# sh ./build-repeaters-iotthinks.sh +#!/usr/bin/env bash +# ./build-iotthinks.sh export FIRMWARE_VERSION="PowerSaving15" ############# Repeaters ############# # Commonly-used boards ## ESP32 - 12 boards -sh build.sh build-firmware \ +./build.sh build-firmware \ Heltec_v3_repeater \ Heltec_WSL3_repeater \ heltec_v4_repeater \ @@ -19,7 +20,7 @@ Heltec_E290_repeater \ Heltec_Wireless_Tracker_repeater ## NRF52 - 13 boards -sh build.sh build-firmware \ +./build.sh build-firmware \ RAK_4631_repeater \ Heltec_t114_repeater \ Xiao_nrf52_repeater \ @@ -35,25 +36,25 @@ GAT562_30S_Mesh_Kit_repeater \ GAT562_Mesh_Tracker_Pro_repeater ## ESP32, SX1276 - 3 boards -sh build.sh build-firmware \ +./build.sh build-firmware \ Heltec_v2_repeater \ LilyGo_TLora_V2_1_1_6_repeater \ Tbeam_SX1276_repeater ## Ikoka - 3 boards -sh build.sh build-firmware \ +./build.sh build-firmware \ ikoka_nano_nrf_22dbm_repeater \ ikoka_nano_nrf_30dbm_repeater \ ikoka_nano_nrf_33dbm_repeater ############# Room Server ############# # ESP32 -sh build.sh build-firmware \ +./build.sh build-firmware \ Heltec_v3_room_server \ heltec_v4_room_server # NRF52 -sh build.sh build-firmware \ +./build.sh build-firmware \ RAK_4631_room_server \ Heltec_t114_room_server \ Xiao_nrf52_room_server \ @@ -63,7 +64,7 @@ RAK_3401_room_server ############# Companions BLE ############# # ESP32 -sh build.sh build-firmware \ +./build.sh build-firmware \ Heltec_v3_companion_radio_ble_ps \ heltec_v4_companion_radio_ble_ps \ heltec_v4_companion_radio_ble_ps_femoff \ @@ -71,7 +72,7 @@ Xiao_S3_WIO_companion_radio_ble \ Heltec_Wireless_Paper_companion_radio_ble # NRF52 -sh build.sh build-firmware \ +./build.sh build-firmware \ RAK_4631_companion_radio_ble \ Heltec_t114_companion_radio_ble \ Xiao_nrf52_companion_radio_ble \ @@ -82,11 +83,11 @@ RAK_3401_companion_radio_ble \ RAK_WisMesh_Tag_companion_radio_ble ############# Companions USB ############# -sh build.sh build-firmware \ +./build.sh build-firmware \ Heltec_v3_companion_radio_usb ############# Companions BLE PS ############# -sh build.sh build-firmware \ +./build.sh build-firmware \ Heltec_v3_companion_radio_ble_ps \ heltec_v4_companion_radio_ble_ps \ heltec_v4_3_companion_radio_ble_ps_femoff \ diff --git a/docs/cli_commands.md b/docs/cli_commands.md index c1041042..1a6e4233 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -821,7 +821,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Default:** `off` -**Note:** Bridge mode uses bucket definitions instead of the single `flood.retry.prefixes` target list. If a flood comes from one fresh bucket, retry continues until every other fresh configured bucket has been heard or `flood.retry.count` is exhausted. +**Note:** Bridge mode uses bucket definitions instead of the single `flood.retry.prefixes` target list. It also has an implicit unconfigured catch-all bucket. If a flood comes from one fresh configured bucket, retry continues until every other fresh configured bucket plus the catch-all bucket has been heard or `flood.retry.count` is exhausted. If a flood comes from an unconfigured or pathless source, retry targets every fresh configured bucket. This means one configured bucket bridges between that bucket and everything else. Prefixes in `flood.retry.ignore` never count as heard bridge targets. --- @@ -832,7 +832,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Parameters:** - `bucket`: Bucket number (`1`-`6`) -- `prefixes`: Up to 8 comma-separated 3-byte hex prefixes, such as `AABBCC,223344`; use `none` or `off` to clear +- `prefixes`: Up to 17 comma-separated 3-byte hex prefixes, such as `AABBCC,223344`; use `none` or `off` to clear **Default:** all buckets empty @@ -840,6 +840,20 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### View or change flood retry ignored prefixes +**Usage:** +- `get flood.retry.ignore` +- `set flood.retry.ignore ` + +**Parameters:** +- `prefixes`: Up to 8 comma-separated 3-byte hex prefixes, such as `AABBCC,223344`; use `none` or `off` to clear + +**Default:** empty + +**Note:** Ignored prefixes do not count as a heard bridge bucket or as the implicit catch-all bucket when bridge retry decides whether every target has repeated the flood. + +--- + ### ACL #### Add, update or remove permissions for a companion diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index ca86d27e..3fa770e9 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -912,6 +912,19 @@ bool MyMesh::floodRetryPrefixMatches(const mesh::Packet* packet) const { return false; } +bool MyMesh::floodRetryPrefixIgnored(const uint8_t* prefix, uint8_t prefix_len) const { + if (prefix == NULL || prefix_len == 0 || prefix_len > MAX_ROUTE_HASH_BYTES) { + return false; + } + for (int i = 0; i < FLOOD_RETRY_IGNORE_PREFIXES; i++) { + const uint8_t* ignored = _prefs.flood_retry_ignore_prefixes[i]; + if ((ignored[0] != 0 || ignored[1] != 0 || ignored[2] != 0) + && memcmp(ignored, prefix, prefix_len) == 0) { + return true; + } + } + return false; +} bool MyMesh::floodRetryPrefixFresh(const uint8_t* prefix, uint8_t prefix_len) const { const auto* recent = ((const SimpleMeshTables *)getTables())->findRecentRepeaterByHash(prefix, prefix_len); if (recent == NULL || recent->last_heard_millis == 0) { @@ -919,10 +932,22 @@ bool MyMesh::floodRetryPrefixFresh(const uint8_t* prefix, uint8_t prefix_len) co } return (uint32_t)(millis() - recent->last_heard_millis) <= 3600000UL; } -int MyMesh::floodRetryBucketForPrefix(const uint8_t* prefix, uint8_t prefix_len, bool require_fresh) const { +static const uint8_t FLOOD_RETRY_BRIDGE_OTHER_BUCKET = FLOOD_RETRY_BRIDGE_BUCKETS; + +static uint8_t floodRetryBucketMask(uint8_t bucket) { + if (bucket >= 8) { + return 0; + } + return (uint8_t)(1U << bucket); +} +int MyMesh::floodRetryBucketForPrefix(const uint8_t* prefix, uint8_t prefix_len, bool require_fresh, + bool include_other) const { if (prefix == NULL || prefix_len == 0 || prefix_len > MAX_ROUTE_HASH_BYTES) { return -1; } + if (floodRetryPrefixIgnored(prefix, prefix_len)) { + return -1; + } if (require_fresh && !floodRetryPrefixFresh(prefix, prefix_len)) { return -1; } @@ -935,18 +960,28 @@ int MyMesh::floodRetryBucketForPrefix(const uint8_t* prefix, uint8_t prefix_len, } } } + if (include_other) { + return FLOOD_RETRY_BRIDGE_OTHER_BUCKET; + } return -1; } +int MyMesh::floodRetryBucketForPathHop(const uint8_t* prefix, uint8_t prefix_len, uint8_t hop, + uint8_t progress_marker) const { + return floodRetryBucketForPrefix(prefix, prefix_len, hop < progress_marker, true); +} int MyMesh::floodRetrySourceBucket(const mesh::Packet* packet) const { - if (packet == NULL || packet->getPathHashCount() < 2) { + if (packet == NULL) { return -1; } uint8_t hash_size = packet->getPathHashSize(); if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { return -1; } + if (packet->getPathHashCount() < 2) { + return FLOOD_RETRY_BRIDGE_OTHER_BUCKET; + } const uint8_t* source_prefix = &packet->path[(packet->getPathHashCount() - 2) * hash_size]; - return floodRetryBucketForPrefix(source_prefix, hash_size, true); + return floodRetryBucketForPrefix(source_prefix, hash_size, true, true); } uint8_t MyMesh::floodRetryBridgeTargetMask(uint8_t source_bucket) const { uint8_t mask = 0; @@ -957,15 +992,20 @@ uint8_t MyMesh::floodRetryBridgeTargetMask(uint8_t source_bucket) const { for (int i = 0; i < FLOOD_RETRY_BUCKET_PREFIXES; i++) { const uint8_t* configured = _prefs.flood_retry_bridge_buckets[bucket][i]; if ((configured[0] != 0 || configured[1] != 0 || configured[2] != 0) + && !floodRetryPrefixIgnored(configured, FLOOD_RETRY_PREFIX_LEN) && floodRetryPrefixFresh(configured, FLOOD_RETRY_PREFIX_LEN)) { - mask |= (uint8_t)(1U << bucket); + mask |= floodRetryBucketMask((uint8_t)bucket); break; } } } + if (source_bucket != FLOOD_RETRY_BRIDGE_OTHER_BUCKET) { + mask |= floodRetryBucketMask(FLOOD_RETRY_BRIDGE_OTHER_BUCKET); + } return mask; } -uint8_t MyMesh::floodRetryBridgeHeardMask(const mesh::Packet* packet, uint8_t source_bucket) const { +uint8_t MyMesh::floodRetryBridgeHeardMask(const mesh::Packet* packet, uint8_t source_bucket, + uint8_t progress_marker) const { if (packet == NULL || packet->getPathHashCount() == 0) { return 0; } @@ -977,9 +1017,13 @@ uint8_t MyMesh::floodRetryBridgeHeardMask(const mesh::Packet* packet, uint8_t so uint8_t mask = 0; const uint8_t* path = packet->path; for (int hop = 0; hop < packet->getPathHashCount(); hop++) { - int bucket = floodRetryBucketForPrefix(path, hash_size, true); + if (progress_marker > 0 && hop == progress_marker - 1) { + path += hash_size; + continue; + } + int bucket = floodRetryBucketForPathHop(path, hash_size, (uint8_t)hop, progress_marker); if (bucket >= 0 && bucket != source_bucket) { - mask |= (uint8_t)(1U << bucket); + mask |= floodRetryBucketMask((uint8_t)bucket); } path += hash_size; } @@ -1019,7 +1063,8 @@ MyMesh::FloodRetryBridgeState* MyMesh::floodRetryBridgeStateFor(const mesh::Pack return NULL; } - uint8_t heard_mask = floodRetryBridgeHeardMask(packet, (uint8_t)source_bucket) & target_mask; + uint8_t progress_marker = packet->getPathHashCount(); + uint8_t heard_mask = floodRetryBridgeHeardMask(packet, (uint8_t)source_bucket, progress_marker) & target_mask; if ((heard_mask & target_mask) == target_mask) { return NULL; } @@ -1029,6 +1074,7 @@ MyMesh::FloodRetryBridgeState* MyMesh::floodRetryBridgeStateFor(const mesh::Pack free_slot->source_bucket = (uint8_t)source_bucket; free_slot->target_mask = target_mask; free_slot->heard_mask = heard_mask; + free_slot->progress_marker = progress_marker; free_slot->active = true; return free_slot; } @@ -1088,8 +1134,13 @@ void MyMesh::refreshFloodRetryHeardRecent(const mesh::Packet* packet) { FloodRetryBridgeState* state = floodRetryBridgeStateFor(packet, false); if (state != NULL) { for (int hop = 0; hop < packet->getPathHashCount(); hop++) { - int bucket = floodRetryBucketForPrefix(path, hash_size, true); - if (bucket >= 0 && bucket != state->source_bucket && (state->target_mask & (uint8_t)(1U << bucket))) { + if (state->progress_marker > 0 && hop == state->progress_marker - 1) { + path += hash_size; + continue; + } + int bucket = floodRetryBucketForPathHop(path, hash_size, (uint8_t)hop, state->progress_marker); + uint8_t bucket_mask = bucket >= 0 ? floodRetryBucketMask((uint8_t)bucket) : 0; + if (bucket >= 0 && bucket != state->source_bucket && (state->target_mask & bucket_mask)) { tables->setRecentRepeater(path, hash_size, packet->_snr, false, true); } path += hash_size; @@ -1161,9 +1212,20 @@ bool MyMesh::formatFloodRetryHeard(char* dest, size_t dest_len, const mesh::Pack } const uint8_t* path = packet->path; for (int hop = 0; hop < packet->getPathHashCount(); hop++) { - int bucket = floodRetryBucketForPrefix(path, hash_size, true); - if (bucket >= 0 && bucket != state->source_bucket && (state->target_mask & (uint8_t)(1U << bucket))) { - size_t needed = (first ? 0 : 1) + 2 + 1 + ((size_t)hash_size * 2) + 1; + if (state->progress_marker > 0 && hop == state->progress_marker - 1) { + path += hash_size; + continue; + } + int bucket = floodRetryBucketForPathHop(path, hash_size, (uint8_t)hop, state->progress_marker); + uint8_t bucket_mask = bucket >= 0 ? floodRetryBucketMask((uint8_t)bucket) : 0; + if (bucket >= 0 && bucket != state->source_bucket && (state->target_mask & bucket_mask)) { + char bucket_label[8]; + if ((uint8_t)bucket == FLOOD_RETRY_BRIDGE_OTHER_BUCKET) { + strcpy(bucket_label, "other"); + } else { + snprintf(bucket_label, sizeof(bucket_label), "b%d", bucket + 1); + } + size_t needed = (first ? 0 : 1) + strlen(bucket_label) + 1 + ((size_t)hash_size * 2) + 1; if (remaining < needed) { if (remaining > 4) { strcpy(out, "..."); @@ -1174,7 +1236,7 @@ bool MyMesh::formatFloodRetryHeard(char* dest, size_t dest_len, const mesh::Pack *out++ = ','; remaining--; } - int n = snprintf(out, remaining, "b%d:", bucket + 1); + int n = snprintf(out, remaining, "%s:", bucket_label); if (n < 0 || (size_t)n >= remaining) { return dest[0] != 0; } @@ -1298,7 +1360,7 @@ bool MyMesh::isFloodRetryEchoTarget(const mesh::Packet* packet, uint8_t progress if (state == NULL) { return false; } - state->heard_mask |= floodRetryBridgeHeardMask(packet, state->source_bucket) & state->target_mask; + state->heard_mask |= floodRetryBridgeHeardMask(packet, state->source_bucket, state->progress_marker) & state->target_mask; return (state->heard_mask & state->target_mask) == state->target_mask; } if (hasFloodRetryPrefixes()) { diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index c189bda9..6716acd1 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -104,6 +104,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { uint8_t source_bucket; uint8_t target_mask; uint8_t heard_mask; + uint8_t progress_marker; bool active; }; mutable FloodRetryBridgeState flood_retry_bridge_states[MAX_FLOOD_RETRY_SLOTS]; @@ -138,11 +139,16 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { bool hasFloodRetryPrefixes() const; bool floodRetryPrefixMatches(const mesh::Packet* packet) const; bool floodRetryLastHopMatches(const mesh::Packet* packet) const; + bool floodRetryPrefixIgnored(const uint8_t* prefix, uint8_t prefix_len) const; bool floodRetryPrefixFresh(const uint8_t* prefix, uint8_t prefix_len) const; - int floodRetryBucketForPrefix(const uint8_t* prefix, uint8_t prefix_len, bool require_fresh) const; + int floodRetryBucketForPrefix(const uint8_t* prefix, uint8_t prefix_len, bool require_fresh, + bool include_other) const; + int floodRetryBucketForPathHop(const uint8_t* prefix, uint8_t prefix_len, uint8_t hop, + uint8_t progress_marker) const; int floodRetrySourceBucket(const mesh::Packet* packet) const; uint8_t floodRetryBridgeTargetMask(uint8_t source_bucket) const; - uint8_t floodRetryBridgeHeardMask(const mesh::Packet* packet, uint8_t source_bucket) const; + uint8_t floodRetryBridgeHeardMask(const mesh::Packet* packet, uint8_t source_bucket, + uint8_t progress_marker) const; FloodRetryBridgeState* floodRetryBridgeStateFor(const mesh::Packet* packet, bool create) const; void clearFloodRetryBridgeState(const mesh::Packet* packet); void refreshFloodRetryHeardRecent(const mesh::Packet* packet); diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 27b2eeb4..ab4513ca 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -213,11 +213,12 @@ static void formatFloodRetryPathGate(char* dest, uint8_t path_gate) { } } -static void formatFloodRetryPrefixes(char* dest, const NodePrefs* prefs) { +static void formatFloodRetryPrefixList(char* dest, const uint8_t prefixes[][FLOOD_RETRY_PREFIX_LEN], + uint8_t max_prefixes) { char* out = dest; bool first = true; - for (int i = 0; i < FLOOD_RETRY_PREFIX_SLOTS; i++) { - const uint8_t* prefix = prefs->flood_retry_prefixes[i]; + for (int i = 0; i < max_prefixes; i++) { + const uint8_t* prefix = prefixes[i]; if (prefix[0] == 0 && prefix[1] == 0 && prefix[2] == 0) { continue; } @@ -231,26 +232,16 @@ static void formatFloodRetryPrefixes(char* dest, const NodePrefs* prefs) { *out = 0; } +static void formatFloodRetryPrefixes(char* dest, const NodePrefs* prefs) { + formatFloodRetryPrefixList(dest, prefs->flood_retry_prefixes, FLOOD_RETRY_PREFIX_SLOTS); +} + static void formatFloodRetryBridgeBucket(char* dest, const NodePrefs* prefs, uint8_t bucket) { - char* out = dest; - bool first = true; if (bucket >= FLOOD_RETRY_BRIDGE_BUCKETS) { - *out = 0; + dest[0] = 0; return; } - for (int i = 0; i < FLOOD_RETRY_BUCKET_PREFIXES; i++) { - const uint8_t* prefix = prefs->flood_retry_bridge_buckets[bucket][i]; - if (prefix[0] == 0 && prefix[1] == 0 && prefix[2] == 0) { - continue; - } - if (!first) { - *out++ = ','; - } - mesh::Utils::toHex(out, prefix, FLOOD_RETRY_PREFIX_LEN); - out += FLOOD_RETRY_PREFIX_LEN * 2; - first = false; - } - *out = 0; + formatFloodRetryPrefixList(dest, prefs->flood_retry_bridge_buckets[bucket], FLOOD_RETRY_BUCKET_PREFIXES); } static bool parseFloodRetryPrefixes(NodePrefs* prefs, const char* value) { @@ -261,7 +252,7 @@ static bool parseFloodRetryPrefixes(NodePrefs* prefs, const char* value) { return true; } - char tmp[96]; + char tmp[FLOOD_RETRY_LIST_TEXT_MAX]; StrHelper::strncpy(tmp, value, sizeof(tmp)); const char* parts[FLOOD_RETRY_PREFIX_SLOTS + 1]; int num = mesh::Utils::parseTextParts(tmp, parts, FLOOD_RETRY_PREFIX_SLOTS + 1); @@ -290,20 +281,20 @@ static bool parseFloodRetryPrefixes(NodePrefs* prefs, const char* value) { } static bool parseFloodRetryPrefixList(uint8_t dest[][FLOOD_RETRY_PREFIX_LEN], uint8_t max_prefixes, const char* value) { - if (max_prefixes > FLOOD_RETRY_BUCKET_PREFIXES) { + if (max_prefixes > FLOOD_RETRY_LIST_PREFIXES) { return false; } - uint8_t parsed[FLOOD_RETRY_BUCKET_PREFIXES][FLOOD_RETRY_PREFIX_LEN]; + uint8_t parsed[FLOOD_RETRY_LIST_PREFIXES][FLOOD_RETRY_PREFIX_LEN]; memset(parsed, 0, sizeof(parsed)); if (value == NULL || value[0] == 0 || strcmp(value, "none") == 0 || strcmp(value, "off") == 0) { memcpy(dest, parsed, max_prefixes * FLOOD_RETRY_PREFIX_LEN); return true; } - char tmp[96]; + char tmp[FLOOD_RETRY_LIST_TEXT_MAX]; StrHelper::strncpy(tmp, value, sizeof(tmp)); - const char* parts[FLOOD_RETRY_BUCKET_PREFIXES + 1]; - int num = mesh::Utils::parseTextParts(tmp, parts, FLOOD_RETRY_BUCKET_PREFIXES + 1); + const char* parts[FLOOD_RETRY_LIST_PREFIXES + 1]; + int num = mesh::Utils::parseTextParts(tmp, parts, FLOOD_RETRY_LIST_PREFIXES + 1); if (num > max_prefixes) { return false; } @@ -436,7 +427,11 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->flood_retry_prefs_magic[0], sizeof(_prefs->flood_retry_prefs_magic)); // 302 file.read((uint8_t *)&_prefs->flood_retry_prefixes[0][0], sizeof(_prefs->flood_retry_prefixes)); // 304 file.read((uint8_t *)&_prefs->flood_retry_bridge_enabled, sizeof(_prefs->flood_retry_bridge_enabled)); // 328 + memset(_prefs->flood_retry_bridge_buckets, 0, sizeof(_prefs->flood_retry_bridge_buckets)); + memset(_prefs->flood_retry_ignore_prefixes, 0, sizeof(_prefs->flood_retry_ignore_prefixes)); file.read((uint8_t *)&_prefs->flood_retry_bridge_buckets[0][0][0], sizeof(_prefs->flood_retry_bridge_buckets)); // 329 + size_t flood_retry_ignore_read = file.read((uint8_t *)&_prefs->flood_retry_ignore_prefixes[0][0], + sizeof(_prefs->flood_retry_ignore_prefixes)); // 635 // PowerSaving-only prefs stored radio_fem_rxgain at 291, before direct retry timing existed. if (radio_fem_rxgain_read != sizeof(_prefs->radio_fem_rxgain) && legacy_retry_attempts_read == sizeof(legacy_retry_attempts_or_radio_fem_rxgain) @@ -444,7 +439,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { || _prefs->direct_retry_timing_magic[1] != DIRECT_RETRY_TIMING_MAGIC_1)) { _prefs->radio_fem_rxgain = constrain(legacy_retry_attempts_or_radio_fem_rxgain, 0, 1); } - // next: 473 + // next: 659 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -505,12 +500,16 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { memset(_prefs->flood_retry_prefixes, 0, sizeof(_prefs->flood_retry_prefixes)); _prefs->flood_retry_bridge_enabled = 0; memset(_prefs->flood_retry_bridge_buckets, 0, sizeof(_prefs->flood_retry_bridge_buckets)); + memset(_prefs->flood_retry_ignore_prefixes, 0, sizeof(_prefs->flood_retry_ignore_prefixes)); } else { _prefs->flood_retry_attempts = constrain(_prefs->flood_retry_attempts, FLOOD_RETRY_COUNT_MIN, FLOOD_RETRY_COUNT_MAX); if (_prefs->flood_retry_path_gate > 63 && _prefs->flood_retry_path_gate != FLOOD_RETRY_PATH_GATE_DISABLED) { _prefs->flood_retry_path_gate = floodRetryPresetPathDefault(_prefs->direct_retry_preset); } _prefs->flood_retry_bridge_enabled = constrain(_prefs->flood_retry_bridge_enabled, 0, 1); + if (flood_retry_ignore_read != sizeof(_prefs->flood_retry_ignore_prefixes)) { + memset(_prefs->flood_retry_ignore_prefixes, 0, sizeof(_prefs->flood_retry_ignore_prefixes)); + } } file.close(); @@ -591,7 +590,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->flood_retry_prefixes[0][0], sizeof(_prefs->flood_retry_prefixes)); // 304 file.write((uint8_t *)&_prefs->flood_retry_bridge_enabled, sizeof(_prefs->flood_retry_bridge_enabled)); // 328 file.write((uint8_t *)&_prefs->flood_retry_bridge_buckets[0][0][0], sizeof(_prefs->flood_retry_bridge_buckets)); // 329 - // next: 473 + file.write((uint8_t *)&_prefs->flood_retry_ignore_prefixes[0][0], sizeof(_prefs->flood_retry_ignore_prefixes)); // 635 + // next: 659 file.close(); } @@ -756,6 +756,9 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else if (memcmp(config, "flood.retry.prefixes", 20) == 0) { formatFloodRetryPrefixes(tmp, _prefs); sprintf(reply, "> %s", tmp[0] ? tmp : "none"); + } else if (memcmp(config, "flood.retry.ignore", 18) == 0) { + formatFloodRetryPrefixList(tmp, _prefs->flood_retry_ignore_prefixes, FLOOD_RETRY_IGNORE_PREFIXES); + sprintf(reply, "> %s", tmp[0] ? tmp : "none"); } else if (memcmp(config, "flood.retry.bridge", 18) == 0) { sprintf(reply, "> %s", _prefs->flood_retry_bridge_enabled ? "on" : "off"); } else if (memcmp(config, "flood.retry.bucket.", 19) == 0) { @@ -1061,6 +1064,15 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else { strcpy(reply, "Error, use comma-separated 3-byte hex prefixes"); } + } else if (memcmp(config, "flood.retry.ignore ", 19) == 0) { + if (parseFloodRetryPrefixList(_prefs->flood_retry_ignore_prefixes, + FLOOD_RETRY_IGNORE_PREFIXES, &config[19])) { + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error, use up to %u comma-separated 3-byte hex prefixes", + (unsigned int)FLOOD_RETRY_IGNORE_PREFIXES); + } } else if (memcmp(config, "flood.retry.bridge ", 19) == 0) { if (memcmp(&config[19], "on", 2) == 0) { _prefs->flood_retry_bridge_enabled = 1; @@ -1084,7 +1096,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re savePrefs(); strcpy(reply, "OK"); } else { - strcpy(reply, "Error, use up to 8 comma-separated 3-byte hex prefixes"); + sprintf(reply, "Error, use up to %u comma-separated 3-byte hex prefixes", + (unsigned int)FLOOD_RETRY_BUCKET_PREFIXES); } } else if (memcmp(config, "direct.txdelay ", 15) == 0) { float f = atof(&config[15]); @@ -1674,6 +1687,15 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { strcpy(reply, "Error, use comma-separated 3-byte hex prefixes"); } + } else if (memcmp(config, "flood.retry.ignore ", 19) == 0) { + if (parseFloodRetryPrefixList(_prefs->flood_retry_ignore_prefixes, + FLOOD_RETRY_IGNORE_PREFIXES, &config[19])) { + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error, use up to %u comma-separated 3-byte hex prefixes", + (unsigned int)FLOOD_RETRY_IGNORE_PREFIXES); + } } else if (memcmp(config, "flood.retry.bridge ", 19) == 0) { if (memcmp(&config[19], "on", 2) == 0) { _prefs->flood_retry_bridge_enabled = 1; @@ -1697,7 +1719,8 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep savePrefs(); strcpy(reply, "OK"); } else { - strcpy(reply, "Error, use up to 8 comma-separated 3-byte hex prefixes"); + sprintf(reply, "Error, use up to %u comma-separated 3-byte hex prefixes", + (unsigned int)FLOOD_RETRY_BUCKET_PREFIXES); } } else if (memcmp(config, "direct.txdelay ", 15) == 0) { float f = atof(&config[15]); @@ -1947,6 +1970,9 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep } else if (memcmp(config, "flood.retry.prefixes", 20) == 0) { formatFloodRetryPrefixes(tmp, _prefs); sprintf(reply, "> %s", tmp[0] ? tmp : "none"); + } else if (memcmp(config, "flood.retry.ignore", 18) == 0) { + formatFloodRetryPrefixList(tmp, _prefs->flood_retry_ignore_prefixes, FLOOD_RETRY_IGNORE_PREFIXES); + sprintf(reply, "> %s", tmp[0] ? tmp : "none"); } else if (memcmp(config, "flood.retry.bridge", 18) == 0) { sprintf(reply, "> %s", _prefs->flood_retry_bridge_enabled ? "on" : "off"); } else if (memcmp(config, "flood.retry.bucket.", 19) == 0) { diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 321c3c8f..40806d49 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -48,7 +48,19 @@ #define FLOOD_RETRY_BRIDGE_BUCKETS 6 #endif #ifndef FLOOD_RETRY_BUCKET_PREFIXES - #define FLOOD_RETRY_BUCKET_PREFIXES 8 + #define FLOOD_RETRY_BUCKET_PREFIXES 17 +#endif +#ifndef FLOOD_RETRY_IGNORE_PREFIXES + #define FLOOD_RETRY_IGNORE_PREFIXES 8 +#endif +#ifndef FLOOD_RETRY_LIST_PREFIXES + #define FLOOD_RETRY_LIST_PREFIXES ((FLOOD_RETRY_IGNORE_PREFIXES > FLOOD_RETRY_BUCKET_PREFIXES) ? FLOOD_RETRY_IGNORE_PREFIXES : FLOOD_RETRY_BUCKET_PREFIXES) +#endif +#ifndef FLOOD_RETRY_LIST_TEXT_MAX + #define FLOOD_RETRY_LIST_TEXT_MAX (FLOOD_RETRY_LIST_PREFIXES * FLOOD_RETRY_PREFIX_LEN * 2 + FLOOD_RETRY_LIST_PREFIXES) +#endif +#ifndef COMMON_CLI_TMP_LEN + #define COMMON_CLI_TMP_LEN ((FLOOD_RETRY_LIST_TEXT_MAX > (PRV_KEY_SIZE * 2 + 4)) ? FLOOD_RETRY_LIST_TEXT_MAX : (PRV_KEY_SIZE * 2 + 4)) #endif struct NodePrefs { // persisted to file @@ -107,6 +119,7 @@ struct NodePrefs { // persisted to file uint8_t flood_retry_prefixes[FLOOD_RETRY_PREFIX_SLOTS][FLOOD_RETRY_PREFIX_LEN]; uint8_t flood_retry_bridge_enabled; uint8_t flood_retry_bridge_buckets[FLOOD_RETRY_BRIDGE_BUCKETS][FLOOD_RETRY_BUCKET_PREFIXES][FLOOD_RETRY_PREFIX_LEN]; + uint8_t flood_retry_ignore_prefixes[FLOOD_RETRY_IGNORE_PREFIXES][FLOOD_RETRY_PREFIX_LEN]; }; class CommonCLICallbacks { @@ -166,7 +179,7 @@ class CommonCLI { SensorManager* _sensors; RegionMap* _region_map; ClientACL* _acl; - char tmp[PRV_KEY_SIZE*2 + 4]; + char tmp[COMMON_CLI_TMP_LEN]; mesh::RTCClock* getRTCClock() { return _rtc; } void savePrefs(); diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index ca81b144..2d2125dd 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -175,8 +175,6 @@ public: f.write((const uint8_t *) &_next_idx, sizeof(_next_idx)); f.write((const uint8_t *) &_acks[0], sizeof(_acks)); f.write((const uint8_t *) &_next_ack_idx, sizeof(_next_ack_idx)); - f.write((const uint8_t *) &_recent_repeaters[0], sizeof(_recent_repeaters)); - f.write((const uint8_t *) &_next_recent_repeater_idx, sizeof(_next_recent_repeater_idx)); } #endif From ae67293bcc854684119fe230b2007da4caf641d0 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 8 May 2026 01:23:03 -0700 Subject: [PATCH 050/214] Honor flood retry ignore for echoes --- docs/cli_commands.md | 2 +- examples/simple_repeater/MyMesh.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 1a6e4233..9631a64a 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -850,7 +850,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Default:** empty -**Note:** Ignored prefixes do not count as a heard bridge bucket or as the implicit catch-all bucket when bridge retry decides whether every target has repeated the flood. +**Note:** In non-bridge retry, an echo whose last hop matches an ignored prefix does not cancel a queued retry as successful. In bridge mode, ignored prefixes do not count as a heard bridge bucket or as the implicit catch-all bucket when bridge retry decides whether every target has repeated the flood. --- diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 3fa770e9..f6f515ee 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1363,6 +1363,17 @@ bool MyMesh::isFloodRetryEchoTarget(const mesh::Packet* packet, uint8_t progress state->heard_mask |= floodRetryBridgeHeardMask(packet, state->source_bucket, state->progress_marker) & state->target_mask; return (state->heard_mask & state->target_mask) == state->target_mask; } + if (packet->getPathHashCount() == 0) { + return false; + } + uint8_t hash_size = packet->getPathHashSize(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + return false; + } + const uint8_t* heard_prefix = &packet->path[(packet->getPathHashCount() - 1) * hash_size]; + if (floodRetryPrefixIgnored(heard_prefix, hash_size)) { + return false; + } if (hasFloodRetryPrefixes()) { return floodRetryLastHopMatches(packet); } From b475bde7303ef745d56ee95527c96882612aca6f Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 8 May 2026 10:53:21 -0700 Subject: [PATCH 051/214] Unify retry preset settings --- docs/cli_commands.md | 45 ++-- docs/halo_keymind_settings.md | 308 ++++++++++++++++++++++++++++ examples/simple_repeater/MyMesh.cpp | 12 +- src/helpers/CommonCLI.cpp | 188 ++++++++--------- src/helpers/CommonCLI.h | 9 +- 5 files changed, 437 insertions(+), 125 deletions(-) create mode 100644 docs/halo_keymind_settings.md diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 9631a64a..c3cf2bd9 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -570,10 +570,10 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- -#### View or change the direct retry timing preset +#### View or change the retry preset **Usage:** -- `get direct.retry.preset` -- `set direct.retry.preset ` +- `get retry.preset` +- `set retry.preset ` **Parameters:** - `value`: `infra`|`rooftop`|`mobile` or `0`|`1`|`2` @@ -581,11 +581,11 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Default:** `rooftop` (`1`) **Presets:** -- `infra` (`0`): `275 ms` base wait, `4` retries, `150 ms` added per retry, SNR gate is SF floor + `15 dB` -- `rooftop` (`1`): `175 ms` base wait, `15` retries, `100 ms` added per retry, SNR gate is SF floor + `5 dB` -- `mobile` (`2`): `175 ms` base wait, `15` retries, `50 ms` added per retry, SNR gate is the SF floor +- `infra` (`0`): `275 ms` direct base wait, `4` direct retries, `150 ms` added per direct retry, SNR gate is SF floor + `15 dB`; flood retry defaults to `1` retry and path gate `1` +- `rooftop` (`1`): `175 ms` direct base wait, `15` direct retries, `100 ms` added per direct retry, SNR gate is SF floor + `5 dB`; flood retry defaults to `3` retries and path gate `2` +- `mobile` (`2`): `175 ms` direct base wait, `15` direct retries, `50 ms` added per direct retry, SNR gate is the SF floor; flood retry defaults to `3` retries and path gate `1` -**Note:** Selecting a preset copies those values into the direct retry settings and also resets flood retry defaults. You can refine `direct.retry.margin`, `direct.retry.count`, `direct.retry.base`, `direct.retry.step`, `flood.retry.count`, or `flood.retry.path` afterward. Retry delay is `direct.txdelay` jitter + base wait + packet-length airtime wait + per-attempt step. +**Note:** Selecting a preset copies those values into the direct retry settings and resets flood retry defaults. You can refine `direct.retry.margin`, `direct.retry.count`, `direct.retry.base`, `direct.retry.step`, `flood.retry.count`, or `flood.retry.path` afterward. Retry delay is `direct.txdelay` jitter + base wait + packet-length airtime wait + per-attempt step. --- @@ -754,23 +754,6 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- -#### View or change the flood retry preset -**Usage:** -- `get flood.retry.preset` -- `set flood.retry.preset ` - -**Parameters:** -- `value`: `infra`|`rooftop`|`mobile` or `0`|`1`|`2` - -**Presets:** -- `infra` (`0`): `1` retry, path gate `1` -- `rooftop` (`1`): `3` retries, path gate `2` -- `mobile` (`2`): `3` retries, path gate `1` - -**Note:** This applies only the flood retry defaults. `set direct.retry.preset` also resets these flood retry defaults. - ---- - #### View or change the number of flood retry attempts **Usage:** - `get flood.retry.count` @@ -797,6 +780,20 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### View or change whether advert packets are flood-retried +**Usage:** +- `get flood.retry.advert` +- `set flood.retry.advert ` + +**Parameters:** +- `state`: `on` or `off` + +**Default:** `off` + +**Note:** When this is `off`, node advert packets (`PAYLOAD_TYPE_ADVERT`, type `4`) are not queued for flood retry. + +--- + #### View or change flood retry target prefixes **Usage:** - `get flood.retry.prefixes` diff --git a/docs/halo_keymind_settings.md b/docs/halo_keymind_settings.md new file mode 100644 index 00000000..c451707f --- /dev/null +++ b/docs/halo_keymind_settings.md @@ -0,0 +1,308 @@ +# Halo and Keymind Branch Settings + +This file covers only CLI settings added by the Halo or Keymind branches. Use +`docs/cli_commands.md` for the general MeshCore CLI. + +## Quick Start + +Use this baseline when bringing a Halo or Keymind repeater onto the network: + +```text +set retry.preset rooftop +set direct.retry.heard on +set flood.retry.advert off +set flood.retry.bridge off +set flood.retry.prefixes none +set flood.retry.ignore none +``` + +Then verify: + +```text +get retry.preset +get direct.retry.heard +get flood.retry.advert +get flood.retry.prefixes +get flood.retry.ignore +``` + +Use prefixes from debug logs such as `path=7773D0>BEEBB0` or `heard=C7618C`. Prefix lists are comma-separated hex values, for example `71CE82,C7618C`. + +## Prefix Worksheet + +Keep Halo and Keymind prefixes in one place before programming buckets or ignore lists. + +| Network | Prefix | Node or site | Use | Notes | +| --- | --- | --- | --- | --- | +| Halo | `A1B2C3` | example remote relay | bucket/target | Replace with real prefix | +| Keymind | `71CE82` | example observed relay | ignore/target | Replace with real prefix | +| Keymind | `C7618C` | example observed relay | ignore/target | Replace with real prefix | + +## Common Examples + +Disable retrying advert packets: + +```text +set flood.retry.advert off +get flood.retry.advert +``` + +Ignore a relay as a successful flood retry echo: + +```text +set flood.retry.ignore 71CE82,C7618C +get flood.retry.ignore +``` + +Only accept specific downstream relays as flood retry success: + +```text +set flood.retry.prefixes BEEBB0,425E5C +get flood.retry.prefixes +``` + +Bridge two groups of repeaters: + +```text +set flood.retry.bridge on +set flood.retry.bucket 1 71CE82,C7618C +set flood.retry.bucket 2 BEEBB0,425E5C +get flood.retry.bucket.1 +get flood.retry.bucket.2 +``` + +Return to simple non-bridge flood retry: + +```text +set flood.retry.bridge off +set flood.retry.prefixes none +set flood.retry.ignore none +``` + +## Added Settings + +| Setting | What it does | How to use | Example | +| --- | --- | --- | --- | +| `recent.repeater` | Shows or seeds the recent repeater prefix/SNR table used by direct retry and bridge freshness checks. | `get recent.repeater`, `get recent.repeater `, `set recent.repeater ` | `set recent.repeater A1B2C3 -8.5` | +| `radio.fem.rxgain` | Controls the external LoRa FEM receive-path LNA where the board supports it. | `get radio.fem.rxgain`, `set radio.fem.rxgain on/off` | `set radio.fem.rxgain on` | + +## Recent Repeater Table + +Direct retry uses the recent repeater table when `direct.retry.heard` is `on`. +Bridge buckets also use this table: a configured bucket prefix is active only +when it was heard within the last hour. + +Show learned rows: + +```text +get recent.repeater +get recent.repeater 2 +``` + +Seed or correct a prefix: + +```text +set recent.repeater A1B2C3 -8.5 +``` + +Rows are sorted by prefix width, then SNR. A full direct retry failure lowers +the matching row by `0.25 dB`. + +## Direct Retry Settings + +Direct retry applies to direct-routed packets. A queued resend is canceled when the next-hop echo is heard. + +| Setting | What it does | How to use | Example | +| --- | --- | --- | --- | +| `retry.preset` | Applies shared direct and flood retry defaults. Values: `infra`, `rooftop`, `mobile` or `0`, `1`, `2`. | `get retry.preset`, `set retry.preset ` | `set retry.preset rooftop` | +| `direct.retry.heard` | Uses the recent repeater table as the direct retry eligibility gate. | `get direct.retry.heard`, `set direct.retry.heard on/off` | `set direct.retry.heard on` | +| `direct.retry.margin` | SNR margin in dB above the SF-specific receive floor. | `get direct.retry.margin`, `set direct.retry.margin <0-40>` | `set direct.retry.margin 5` | +| `direct.retry.count` | Maximum direct retry attempts after initial TX. | `get direct.retry.count`, `set direct.retry.count <1-15>` | `set direct.retry.count 15` | +| `direct.retry.base` | Base wait in milliseconds before retry. | `get direct.retry.base`, `set direct.retry.base <10-5000>` | `set direct.retry.base 175` | +| `direct.retry.step` | Milliseconds added per retry attempt. | `get direct.retry.step`, `set direct.retry.step <0-5000>` | `set direct.retry.step 100` | + +Preset details: + +| Preset | Base | Count | Step | SNR gate | +| --- | ---: | ---: | ---: | --- | +| `infra` | `275 ms` | `4` | `150 ms` | SF floor + `15 dB` | +| `rooftop` | `175 ms` | `15` | `100 ms` | SF floor + `5 dB` | +| `mobile` | `175 ms` | `15` | `50 ms` | SF floor | + +Example for a quiet fixed repeater: + +```text +set retry.preset rooftop +set direct.retry.heard on +set direct.retry.margin 5 +``` + +Example for a moving or weak-link node: + +```text +set retry.preset mobile +set direct.retry.margin 0 +``` + +## Flood And Advert Settings + +Flood retry applies to flood-routed packets. A queued retry is canceled when a qualifying downstream echo is heard. + +| Setting | What it does | How to use | Example | +| --- | --- | --- | --- | +| `flood.retry.count` | Maximum flood retry attempts after initial TX. `0` disables flood retry. | `get flood.retry.count`, `set flood.retry.count <0-3>` | `set flood.retry.count 3` | +| `flood.retry.path` | Maximum path hash count eligible for flood retry, or `off` to disable the gate. | `get flood.retry.path`, `set flood.retry.path <0-63/off>` | `set flood.retry.path 1` | +| `flood.retry.advert` | Allows or blocks retry for node advert packets (`type=4`). Default is `off`. | `get flood.retry.advert`, `set flood.retry.advert on/off` | `set flood.retry.advert off` | +| `flood.retry.prefixes` | Target prefixes. If set, only matching downstream echoes cancel a retry. | `get flood.retry.prefixes`, `set flood.retry.prefixes ` | `set flood.retry.prefixes BEEBB0,425E5C` | +| `flood.retry.ignore` | Ignored prefixes. In non-bridge retry, ignored last-hop echoes do not cancel retry. | `get flood.retry.ignore`, `set flood.retry.ignore ` | `set flood.retry.ignore 71CE82,C7618C` | +| `flood.retry.bridge` | Enables bucket-based bridge retry logic. | `get flood.retry.bridge`, `set flood.retry.bridge on/off` | `set flood.retry.bridge on` | +| `flood.retry.bucket.` | Shows one bridge bucket. Buckets are numbered `1`-`6`. | `get flood.retry.bucket.` | `get flood.retry.bucket.1` | +| `flood.retry.bucket` | Sets bridge bucket prefixes. | `set flood.retry.bucket <1-6> ` | `set flood.retry.bucket 1 71CE82,C7618C` | + +The shared retry preset sets these flood defaults: + +| Preset | Retry count | Path gate | +| --- | ---: | ---: | +| `infra` | `1` | `1` | +| `rooftop` | `3` | `2` | +| `mobile` | `3` | `1` | + +Example for Keymind path-gated retry: + +```text +set retry.preset rooftop +set flood.retry.path 1 +set flood.retry.advert off +set flood.retry.ignore 71CE82,C7618C +``` + +Example for Halo targeted retry: + +```text +set flood.retry.bridge off +set flood.retry.prefixes A1B2C3,D4E5F6 +set flood.retry.ignore none +``` + +Example for Halo/Keymind bridge retry: + +```text +set flood.retry.bridge on +set flood.retry.bucket 1 A1B2C3,D4E5F6 +set flood.retry.bucket 2 71CE82,C7618C +set flood.retry.advert off +``` + +## North South Buckets + +Buckets describe groups of repeaters on different sides of this relay. Bucket +numbers do not have built-in meanings; this example uses bucket `1` for North +and bucket `2` for South. + +```text + North bucket 1 + +-----------------------+ + | A1B2C3 D4E5F6 | + | North A North B | + +-----------+-----------+ + | + v + +-----------+ + | This node | + +-----------+ + ^ + | + +-----------+-----------+ + | 71CE82 C7618C | + | South A South B | + +-----------------------+ + South bucket 2 +``` + +Configure the buckets: + +```text +set flood.retry.bridge on +set flood.retry.bucket 1 A1B2C3,D4E5F6 +set flood.retry.bucket 2 71CE82,C7618C +set flood.retry.ignore none +``` + +Packet heard from the North: + +```text + heard source + | + v + +--------------+ retry targets + | North bucket | -----> South bucket + | bucket 1 | -----> Other fresh/unbucketed relays + +--------------+ +``` + +Packet heard from the South: + +```text + heard source + | + v + +--------------+ retry targets + | South bucket | -----> North bucket + | bucket 2 | -----> Other fresh/unbucketed relays + +--------------+ +``` + +Packet heard from an unbucketed or pathless source: + +```text + heard source + | + v + +--------------+ retry targets + | Other bucket | -----> North bucket + | implicit | -----> South bucket + +--------------+ +``` + +Bridge retry stays eligible until every target bucket has been heard or +`flood.retry.count` is exhausted. A configured bucket is a target only when at +least one of its prefixes is fresh in `recent.repeater`. Prefixes in +`flood.retry.ignore` never count as bucket hits. + +## Troubleshooting + +If advert packets are still retrying: + +```text +get flood.retry.advert +set flood.retry.advert off +``` + +If ignored prefixes still appear in `flood retry good` logs: + +```text +get flood.retry.ignore +set flood.retry.ignore +``` + +The ignored prefix must match the last hop shown as `heard=`. For example, this log needs `C7618C` in the ignore list: + +```text +flood retry good (... path=7773D0>C7618C, heard=C7618C ...) +``` + +If retries are too aggressive: + +```text +set flood.retry.count 1 +set flood.retry.path 1 +set direct.retry.count 4 +``` + +If retries are too sparse: + +```text +set retry.preset rooftop +set flood.retry.count 3 +set flood.retry.path 2 +``` diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index f6f515ee..42505917 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -847,10 +847,10 @@ bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_ho return true; } uint8_t MyMesh::getDirectRetryPreset() const { - if (_prefs.direct_retry_preset <= DIRECT_RETRY_PRESET_MOBILE) { - return _prefs.direct_retry_preset; + if (_prefs.retry_preset <= RETRY_PRESET_MOBILE) { + return _prefs.retry_preset; } - return DIRECT_RETRY_PRESET_ROOFTOP; + return RETRY_PRESET_ROOFTOP; } uint8_t MyMesh::getDirectRetryConfiguredMaxAttempts() const { return constrain(_prefs.direct_retry_attempts, (uint8_t)1, (uint8_t)15); @@ -1099,6 +1099,9 @@ bool MyMesh::allowFloodRetry(const mesh::Packet* packet) const { if (_prefs.disable_fwd || constrain(_prefs.flood_retry_attempts, (uint8_t)0, (uint8_t)3) == 0) { return false; } + if (packet != NULL && packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && !_prefs.flood_retry_advert_enabled) { + return false; + } if (!_prefs.flood_retry_bridge_enabled) { return true; } @@ -1750,10 +1753,11 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.direct_retry_attempts = DIRECT_RETRY_ROOFTOP_COUNT; _prefs.direct_retry_base_ms = DIRECT_RETRY_ROOFTOP_BASE_MS; _prefs.direct_retry_step_ms = DIRECT_RETRY_ROOFTOP_STEP_MS; - _prefs.direct_retry_preset = DIRECT_RETRY_PRESET_ROOFTOP; + _prefs.retry_preset = RETRY_PRESET_ROOFTOP; _prefs.flood_retry_attempts = 3; _prefs.flood_retry_path_gate = 2; _prefs.flood_retry_bridge_enabled = 0; + _prefs.flood_retry_advert_enabled = 0; StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); _prefs.node_lat = ADVERT_LAT; _prefs.node_lon = ADVERT_LON; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index ab4513ca..136451cd 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -29,11 +29,12 @@ #define DIRECT_RETRY_STEP_MS_DEFAULT DIRECT_RETRY_ROOFTOP_STEP_MS #define DIRECT_RETRY_STEP_MS_MIN 0 #define DIRECT_RETRY_STEP_MS_MAX 5000 -#define DIRECT_RETRY_PRESET_DEFAULT DIRECT_RETRY_PRESET_ROOFTOP +#define RETRY_PRESET_DEFAULT RETRY_PRESET_ROOFTOP #define FLOOD_RETRY_PREFS_MAGIC_0 0xF4 #define FLOOD_RETRY_PREFS_MAGIC_1 0x52 #define FLOOD_RETRY_COUNT_MIN 0 #define FLOOD_RETRY_COUNT_MAX 3 +#define FLOOD_RETRY_ADVERT_DEFAULT 0 // Believe it or not, this std C function is busted on some platforms! static uint32_t _atoi(const char* sp) { @@ -78,20 +79,20 @@ static float directRetryMarginX4ToDb(uint8_t margin_x4) { return ((float)margin_x4) / 4.0f; } -static uint8_t directRetryPresetOrDefault(uint8_t preset) { - if (preset <= DIRECT_RETRY_PRESET_MOBILE) { +static uint8_t retryPresetOrDefault(uint8_t preset) { + if (preset <= RETRY_PRESET_MOBILE) { return preset; } - return DIRECT_RETRY_PRESET_DEFAULT; + return RETRY_PRESET_DEFAULT; } -static const char* directRetryPresetName(uint8_t preset) { - switch (directRetryPresetOrDefault(preset)) { - case DIRECT_RETRY_PRESET_INFRA: +static const char* retryPresetName(uint8_t preset) { + switch (retryPresetOrDefault(preset)) { + case RETRY_PRESET_INFRA: return "infra"; - case DIRECT_RETRY_PRESET_MOBILE: + case RETRY_PRESET_MOBILE: return "mobile"; - case DIRECT_RETRY_PRESET_ROOFTOP: + case RETRY_PRESET_ROOFTOP: default: return "rooftop"; } @@ -113,36 +114,36 @@ static uint8_t directRetryEffectiveMarginX4(const NodePrefs* prefs) { return constrain(prefs->direct_retry_snr_margin_db, (uint8_t)0, (uint8_t)DIRECT_RETRY_SNR_MARGIN_X4_MAX); } -static uint16_t directRetryPresetStepDefault(uint8_t preset) { - switch (directRetryPresetOrDefault(preset)) { - case DIRECT_RETRY_PRESET_INFRA: +static uint16_t retryPresetStepDefault(uint8_t preset) { + switch (retryPresetOrDefault(preset)) { + case RETRY_PRESET_INFRA: return DIRECT_RETRY_INFRA_STEP_MS; - case DIRECT_RETRY_PRESET_MOBILE: + case RETRY_PRESET_MOBILE: return DIRECT_RETRY_MOBILE_STEP_MS; - case DIRECT_RETRY_PRESET_ROOFTOP: + case RETRY_PRESET_ROOFTOP: default: return DIRECT_RETRY_ROOFTOP_STEP_MS; } } static uint8_t floodRetryPresetCountDefault(uint8_t preset) { - switch (directRetryPresetOrDefault(preset)) { - case DIRECT_RETRY_PRESET_INFRA: + switch (retryPresetOrDefault(preset)) { + case RETRY_PRESET_INFRA: return 1; - case DIRECT_RETRY_PRESET_MOBILE: - case DIRECT_RETRY_PRESET_ROOFTOP: + case RETRY_PRESET_MOBILE: + case RETRY_PRESET_ROOFTOP: default: return 3; } } static uint8_t floodRetryPresetPathDefault(uint8_t preset) { - switch (directRetryPresetOrDefault(preset)) { - case DIRECT_RETRY_PRESET_INFRA: + switch (retryPresetOrDefault(preset)) { + case RETRY_PRESET_INFRA: return 1; - case DIRECT_RETRY_PRESET_MOBILE: + case RETRY_PRESET_MOBILE: return 1; - case DIRECT_RETRY_PRESET_ROOFTOP: + case RETRY_PRESET_ROOFTOP: default: return 2; } @@ -157,33 +158,22 @@ static uint8_t floodRetryEffectiveCount(const NodePrefs* prefs) { return constrain(prefs->flood_retry_attempts, (uint8_t)FLOOD_RETRY_COUNT_MIN, (uint8_t)FLOOD_RETRY_COUNT_MAX); } -static uint8_t floodRetryPresetForPrefs(const NodePrefs* prefs) { - uint8_t count = floodRetryEffectiveCount(prefs); - uint8_t path_gate = prefs->flood_retry_path_gate; - for (uint8_t preset = DIRECT_RETRY_PRESET_INFRA; preset <= DIRECT_RETRY_PRESET_MOBILE; preset++) { - if (count == floodRetryPresetCountDefault(preset) && path_gate == floodRetryPresetPathDefault(preset)) { - return preset; - } - } - return directRetryPresetOrDefault(prefs->direct_retry_preset); -} - -static void applyDirectRetryPreset(NodePrefs* prefs, uint8_t preset) { - prefs->direct_retry_preset = directRetryPresetOrDefault(preset); - switch (prefs->direct_retry_preset) { - case DIRECT_RETRY_PRESET_INFRA: +static void applyRetryPreset(NodePrefs* prefs, uint8_t preset) { + prefs->retry_preset = retryPresetOrDefault(preset); + switch (prefs->retry_preset) { + case RETRY_PRESET_INFRA: prefs->direct_retry_base_ms = DIRECT_RETRY_INFRA_BASE_MS; prefs->direct_retry_attempts = DIRECT_RETRY_INFRA_COUNT; prefs->direct_retry_step_ms = DIRECT_RETRY_INFRA_STEP_MS; prefs->direct_retry_snr_margin_db = DIRECT_RETRY_INFRA_MARGIN_X4; break; - case DIRECT_RETRY_PRESET_MOBILE: + case RETRY_PRESET_MOBILE: prefs->direct_retry_base_ms = DIRECT_RETRY_MOBILE_BASE_MS; prefs->direct_retry_attempts = DIRECT_RETRY_MOBILE_COUNT; prefs->direct_retry_step_ms = DIRECT_RETRY_MOBILE_STEP_MS; prefs->direct_retry_snr_margin_db = DIRECT_RETRY_MOBILE_MARGIN_X4; break; - case DIRECT_RETRY_PRESET_ROOFTOP: + case RETRY_PRESET_ROOFTOP: default: prefs->direct_retry_base_ms = DIRECT_RETRY_ROOFTOP_BASE_MS; prefs->direct_retry_attempts = DIRECT_RETRY_ROOFTOP_COUNT; @@ -191,7 +181,13 @@ static void applyDirectRetryPreset(NodePrefs* prefs, uint8_t preset) { prefs->direct_retry_snr_margin_db = DIRECT_RETRY_ROOFTOP_MARGIN_X4; break; } - applyFloodRetryPreset(prefs, prefs->direct_retry_preset); + applyFloodRetryPreset(prefs, prefs->retry_preset); +} + +static void formatRetryPreset(char* reply, const NodePrefs* prefs) { + sprintf(reply, "> %d,%s", + (uint32_t)retryPresetOrDefault(prefs->retry_preset), + retryPresetName(prefs->retry_preset)); } static bool parseFloodRetryPathGate(const char* value, uint8_t& path_gate) { @@ -316,21 +312,21 @@ static bool parseFloodRetryPrefixList(uint8_t dest[][FLOOD_RETRY_PREFIX_LEN], ui return true; } -static bool parseDirectRetryPreset(const char* value, uint8_t& preset) { +static bool parseRetryPreset(const char* value, uint8_t& preset) { if (value == NULL) { return false; } if (strcmp(value, "0") == 0 || strcmp(value, "infra") == 0 || strcmp(value, "infa") == 0 || strcmp(value, "infrastructure") == 0) { - preset = DIRECT_RETRY_PRESET_INFRA; + preset = RETRY_PRESET_INFRA; return true; } if (strcmp(value, "1") == 0 || strcmp(value, "rooftop") == 0) { - preset = DIRECT_RETRY_PRESET_ROOFTOP; + preset = RETRY_PRESET_ROOFTOP; return true; } if (strcmp(value, "2") == 0 || strcmp(value, "mobile") == 0) { - preset = DIRECT_RETRY_PRESET_MOBILE; + preset = RETRY_PRESET_MOBILE; return true; } return false; @@ -418,7 +414,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->direct_retry_timing_magic[0], sizeof(_prefs->direct_retry_timing_magic)); // 294 size_t radio_fem_rxgain_read = file.read((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 296 - file.read((uint8_t *)&_prefs->direct_retry_preset, sizeof(_prefs->direct_retry_preset)); // 297 + file.read((uint8_t *)&_prefs->retry_preset, sizeof(_prefs->retry_preset)); // 297 size_t retry_step_read = file.read((uint8_t *)&_prefs->direct_retry_step_ms, sizeof(_prefs->direct_retry_step_ms)); // 298 size_t flood_retry_attempts_read = file.read((uint8_t *)&_prefs->flood_retry_attempts, @@ -432,6 +428,9 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->flood_retry_bridge_buckets[0][0][0], sizeof(_prefs->flood_retry_bridge_buckets)); // 329 size_t flood_retry_ignore_read = file.read((uint8_t *)&_prefs->flood_retry_ignore_prefixes[0][0], sizeof(_prefs->flood_retry_ignore_prefixes)); // 635 + _prefs->flood_retry_advert_enabled = FLOOD_RETRY_ADVERT_DEFAULT; + size_t flood_retry_advert_read = file.read((uint8_t *)&_prefs->flood_retry_advert_enabled, + sizeof(_prefs->flood_retry_advert_enabled)); // 659 // PowerSaving-only prefs stored radio_fem_rxgain at 291, before direct retry timing existed. if (radio_fem_rxgain_read != sizeof(_prefs->radio_fem_rxgain) && legacy_retry_attempts_read == sizeof(legacy_retry_attempts_or_radio_fem_rxgain) @@ -487,29 +486,35 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { // sanitise settings _prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean _prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean - _prefs->direct_retry_preset = directRetryPresetOrDefault(_prefs->direct_retry_preset); + _prefs->retry_preset = retryPresetOrDefault(_prefs->retry_preset); if (retry_step_read != sizeof(_prefs->direct_retry_step_ms)) { - _prefs->direct_retry_step_ms = directRetryPresetStepDefault(_prefs->direct_retry_preset); + _prefs->direct_retry_step_ms = retryPresetStepDefault(_prefs->retry_preset); } else { _prefs->direct_retry_step_ms = constrain(_prefs->direct_retry_step_ms, DIRECT_RETRY_STEP_MS_MIN, DIRECT_RETRY_STEP_MS_MAX); } if (flood_retry_attempts_read != sizeof(_prefs->flood_retry_attempts) || _prefs->flood_retry_prefs_magic[0] != FLOOD_RETRY_PREFS_MAGIC_0 || _prefs->flood_retry_prefs_magic[1] != FLOOD_RETRY_PREFS_MAGIC_1) { - applyFloodRetryPreset(_prefs, _prefs->direct_retry_preset); + applyFloodRetryPreset(_prefs, _prefs->retry_preset); memset(_prefs->flood_retry_prefixes, 0, sizeof(_prefs->flood_retry_prefixes)); _prefs->flood_retry_bridge_enabled = 0; memset(_prefs->flood_retry_bridge_buckets, 0, sizeof(_prefs->flood_retry_bridge_buckets)); memset(_prefs->flood_retry_ignore_prefixes, 0, sizeof(_prefs->flood_retry_ignore_prefixes)); + _prefs->flood_retry_advert_enabled = FLOOD_RETRY_ADVERT_DEFAULT; } else { _prefs->flood_retry_attempts = constrain(_prefs->flood_retry_attempts, FLOOD_RETRY_COUNT_MIN, FLOOD_RETRY_COUNT_MAX); if (_prefs->flood_retry_path_gate > 63 && _prefs->flood_retry_path_gate != FLOOD_RETRY_PATH_GATE_DISABLED) { - _prefs->flood_retry_path_gate = floodRetryPresetPathDefault(_prefs->direct_retry_preset); + _prefs->flood_retry_path_gate = floodRetryPresetPathDefault(_prefs->retry_preset); } _prefs->flood_retry_bridge_enabled = constrain(_prefs->flood_retry_bridge_enabled, 0, 1); if (flood_retry_ignore_read != sizeof(_prefs->flood_retry_ignore_prefixes)) { memset(_prefs->flood_retry_ignore_prefixes, 0, sizeof(_prefs->flood_retry_ignore_prefixes)); } + if (flood_retry_advert_read != sizeof(_prefs->flood_retry_advert_enabled)) { + _prefs->flood_retry_advert_enabled = FLOOD_RETRY_ADVERT_DEFAULT; + } else { + _prefs->flood_retry_advert_enabled = constrain(_prefs->flood_retry_advert_enabled, 0, 1); + } } file.close(); @@ -581,7 +586,7 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { uint8_t retry_timing_magic[2] = { DIRECT_RETRY_TIMING_MAGIC_0, DIRECT_RETRY_TIMING_MAGIC_1 }; file.write(retry_timing_magic, sizeof(retry_timing_magic)); // 294 file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 296 - file.write((uint8_t *)&_prefs->direct_retry_preset, sizeof(_prefs->direct_retry_preset)); // 297 + file.write((uint8_t *)&_prefs->retry_preset, sizeof(_prefs->retry_preset)); // 297 file.write((uint8_t *)&_prefs->direct_retry_step_ms, sizeof(_prefs->direct_retry_step_ms)); // 298 file.write((uint8_t *)&_prefs->flood_retry_attempts, sizeof(_prefs->flood_retry_attempts)); // 300 file.write((uint8_t *)&_prefs->flood_retry_path_gate, sizeof(_prefs->flood_retry_path_gate)); // 301 @@ -591,7 +596,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->flood_retry_bridge_enabled, sizeof(_prefs->flood_retry_bridge_enabled)); // 328 file.write((uint8_t *)&_prefs->flood_retry_bridge_buckets[0][0][0], sizeof(_prefs->flood_retry_bridge_buckets)); // 329 file.write((uint8_t *)&_prefs->flood_retry_ignore_prefixes[0][0], sizeof(_prefs->flood_retry_ignore_prefixes)); // 635 - // next: 659 + file.write((uint8_t *)&_prefs->flood_retry_advert_enabled, sizeof(_prefs->flood_retry_advert_enabled)); // 659 + // next: 660 file.close(); } @@ -747,6 +753,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor)); } else if (memcmp(config, "flood.max", 9) == 0) { sprintf(reply, "> %d", (uint32_t)_prefs->flood_max); + } else if (memcmp(config, "retry.preset", 12) == 0) { + formatRetryPreset(reply, _prefs); } else if (memcmp(config, "flood.retry.count", 17) == 0) { sprintf(reply, "> %d", (uint32_t)floodRetryEffectiveCount(_prefs)); } else if (memcmp(config, "flood.retry.path", 16) == 0) { @@ -759,6 +767,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else if (memcmp(config, "flood.retry.ignore", 18) == 0) { formatFloodRetryPrefixList(tmp, _prefs->flood_retry_ignore_prefixes, FLOOD_RETRY_IGNORE_PREFIXES); sprintf(reply, "> %s", tmp[0] ? tmp : "none"); + } else if (memcmp(config, "flood.retry.advert", 18) == 0) { + sprintf(reply, "> %s", _prefs->flood_retry_advert_enabled ? "on" : "off"); } else if (memcmp(config, "flood.retry.bridge", 18) == 0) { sprintf(reply, "> %s", _prefs->flood_retry_bridge_enabled ? "on" : "off"); } else if (memcmp(config, "flood.retry.bucket.", 19) == 0) { @@ -769,21 +779,12 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else { sprintf(reply, "Error, bucket 1-%d", FLOOD_RETRY_BRIDGE_BUCKETS); } - } else if (memcmp(config, "flood.retry.preset", 18) == 0) { - uint8_t preset = floodRetryPresetForPrefs(_prefs); - sprintf(reply, "> %d,%s", - (uint32_t)preset, - directRetryPresetName(preset)); } else if (memcmp(config, "direct.txdelay", 14) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor)); } else if (memcmp(config, "direct.retry.heard", 18) == 0) { sprintf(reply, "> %s", _prefs->direct_retry_recent_enabled ? "on" : "off"); } else if (memcmp(config, "direct.retry.margin", 19) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(directRetryMarginX4ToDb(directRetryEffectiveMarginX4(_prefs)))); - } else if (memcmp(config, "direct.retry.preset", 19) == 0) { - sprintf(reply, "> %d,%s", - (uint32_t)directRetryPresetOrDefault(_prefs->direct_retry_preset), - directRetryPresetName(_prefs->direct_retry_preset)); } else if (memcmp(config, "direct.retry.count", 18) == 0) { sprintf(reply, "> %d", (uint32_t)directRetryEffectiveCount(_prefs)); } else if (memcmp(config, "direct.retry.base", 17) == 0) { @@ -1030,10 +1031,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else { strcpy(reply, "Error, max 64"); } - } else if (memcmp(config, "flood.retry.preset ", 19) == 0) { + } else if (memcmp(config, "retry.preset ", 13) == 0) { uint8_t preset; - if (parseDirectRetryPreset(&config[19], preset)) { - applyFloodRetryPreset(_prefs, preset); + if (parseRetryPreset(&config[13], preset)) { + applyRetryPreset(_prefs, preset); savePrefs(); strcpy(reply, "OK"); } else { @@ -1073,6 +1074,18 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re sprintf(reply, "Error, use up to %u comma-separated 3-byte hex prefixes", (unsigned int)FLOOD_RETRY_IGNORE_PREFIXES); } + } else if (memcmp(config, "flood.retry.advert ", 19) == 0) { + if (memcmp(&config[19], "on", 2) == 0) { + _prefs->flood_retry_advert_enabled = 1; + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(&config[19], "off", 3) == 0) { + _prefs->flood_retry_advert_enabled = 0; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be on or off"); + } } else if (memcmp(config, "flood.retry.bridge ", 19) == 0) { if (memcmp(&config[19], "on", 2) == 0) { _prefs->flood_retry_bridge_enabled = 1; @@ -1129,15 +1142,6 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else { sprintf(reply, "Error, min 0 and max %d", DIRECT_RETRY_SNR_MARGIN_DB_MAX); } - } else if (memcmp(config, "direct.retry.preset ", 20) == 0) { - uint8_t preset; - if (parseDirectRetryPreset(&config[20], preset)) { - applyDirectRetryPreset(_prefs, preset); - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error, must be infra, rooftop, mobile, 0, 1, or 2"); - } } else if (memcmp(config, "direct.retry.count ", 19) == 0) { int count = atoi(&config[19]); if (count >= DIRECT_RETRY_COUNT_MIN && count <= DIRECT_RETRY_COUNT_MAX) { @@ -1653,10 +1657,10 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { strcpy(reply, "Error, max 64"); } - } else if (memcmp(config, "flood.retry.preset ", 19) == 0) { + } else if (memcmp(config, "retry.preset ", 13) == 0) { uint8_t preset; - if (parseDirectRetryPreset(&config[19], preset)) { - applyFloodRetryPreset(_prefs, preset); + if (parseRetryPreset(&config[13], preset)) { + applyRetryPreset(_prefs, preset); savePrefs(); strcpy(reply, "OK"); } else { @@ -1696,6 +1700,18 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "Error, use up to %u comma-separated 3-byte hex prefixes", (unsigned int)FLOOD_RETRY_IGNORE_PREFIXES); } + } else if (memcmp(config, "flood.retry.advert ", 19) == 0) { + if (memcmp(&config[19], "on", 2) == 0) { + _prefs->flood_retry_advert_enabled = 1; + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(&config[19], "off", 3) == 0) { + _prefs->flood_retry_advert_enabled = 0; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be on or off"); + } } else if (memcmp(config, "flood.retry.bridge ", 19) == 0) { if (memcmp(&config[19], "on", 2) == 0) { _prefs->flood_retry_bridge_enabled = 1; @@ -1752,15 +1768,6 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { sprintf(reply, "Error, min 0 and max %d", DIRECT_RETRY_SNR_MARGIN_DB_MAX); } - } else if (memcmp(config, "direct.retry.preset ", 20) == 0) { - uint8_t preset; - if (parseDirectRetryPreset(&config[20], preset)) { - applyDirectRetryPreset(_prefs, preset); - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error, must be infra, rooftop, mobile, 0, 1, or 2"); - } } else if (memcmp(config, "direct.retry.count ", 19) == 0) { int count = atoi(&config[19]); if (count >= DIRECT_RETRY_COUNT_MIN && count <= DIRECT_RETRY_COUNT_MAX) { @@ -1961,6 +1968,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor)); } else if (memcmp(config, "flood.max", 9) == 0) { sprintf(reply, "> %d", (uint32_t)_prefs->flood_max); + } else if (memcmp(config, "retry.preset", 12) == 0) { + formatRetryPreset(reply, _prefs); } else if (memcmp(config, "flood.retry.count", 17) == 0) { sprintf(reply, "> %d", (uint32_t)floodRetryEffectiveCount(_prefs)); } else if (memcmp(config, "flood.retry.path", 16) == 0) { @@ -1973,6 +1982,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep } else if (memcmp(config, "flood.retry.ignore", 18) == 0) { formatFloodRetryPrefixList(tmp, _prefs->flood_retry_ignore_prefixes, FLOOD_RETRY_IGNORE_PREFIXES); sprintf(reply, "> %s", tmp[0] ? tmp : "none"); + } else if (memcmp(config, "flood.retry.advert", 18) == 0) { + sprintf(reply, "> %s", _prefs->flood_retry_advert_enabled ? "on" : "off"); } else if (memcmp(config, "flood.retry.bridge", 18) == 0) { sprintf(reply, "> %s", _prefs->flood_retry_bridge_enabled ? "on" : "off"); } else if (memcmp(config, "flood.retry.bucket.", 19) == 0) { @@ -1983,21 +1994,12 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep } else { sprintf(reply, "Error, bucket 1-%d", FLOOD_RETRY_BRIDGE_BUCKETS); } - } else if (memcmp(config, "flood.retry.preset", 18) == 0) { - uint8_t preset = floodRetryPresetForPrefs(_prefs); - sprintf(reply, "> %d,%s", - (uint32_t)preset, - directRetryPresetName(preset)); } else if (memcmp(config, "direct.txdelay", 14) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor)); } else if (memcmp(config, "direct.retry.heard", 18) == 0) { sprintf(reply, "> %s", _prefs->direct_retry_recent_enabled ? "on" : "off"); } else if (memcmp(config, "direct.retry.margin", 19) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(directRetryMarginX4ToDb(directRetryEffectiveMarginX4(_prefs)))); - } else if (memcmp(config, "direct.retry.preset", 19) == 0) { - sprintf(reply, "> %d,%s", - (uint32_t)directRetryPresetOrDefault(_prefs->direct_retry_preset), - directRetryPresetName(_prefs->direct_retry_preset)); } else if (memcmp(config, "direct.retry.count", 18) == 0) { sprintf(reply, "> %d", (uint32_t)directRetryEffectiveCount(_prefs)); } else if (memcmp(config, "direct.retry.base", 17) == 0) { diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 40806d49..5c1eb62f 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -19,9 +19,9 @@ #define LOOP_DETECT_MODERATE 2 #define LOOP_DETECT_STRICT 3 -#define DIRECT_RETRY_PRESET_INFRA 0 -#define DIRECT_RETRY_PRESET_ROOFTOP 1 -#define DIRECT_RETRY_PRESET_MOBILE 2 +#define RETRY_PRESET_INFRA 0 +#define RETRY_PRESET_ROOFTOP 1 +#define RETRY_PRESET_MOBILE 2 #define DIRECT_RETRY_INFRA_BASE_MS 275 #define DIRECT_RETRY_INFRA_COUNT 4 @@ -111,7 +111,7 @@ struct NodePrefs { // persisted to file uint8_t direct_retry_attempts; uint16_t direct_retry_base_ms; uint8_t direct_retry_timing_magic[2]; - uint8_t direct_retry_preset; + uint8_t retry_preset; uint16_t direct_retry_step_ms; uint8_t flood_retry_attempts; uint8_t flood_retry_path_gate; @@ -120,6 +120,7 @@ struct NodePrefs { // persisted to file uint8_t flood_retry_bridge_enabled; uint8_t flood_retry_bridge_buckets[FLOOD_RETRY_BRIDGE_BUCKETS][FLOOD_RETRY_BUCKET_PREFIXES][FLOOD_RETRY_PREFIX_LEN]; uint8_t flood_retry_ignore_prefixes[FLOOD_RETRY_IGNORE_PREFIXES][FLOOD_RETRY_PREFIX_LEN]; + uint8_t flood_retry_advert_enabled; }; class CommonCLICallbacks { From c244effb52ba90fd30cb1fe5ba2ecfa8f979e340 Mon Sep 17 00:00:00 2001 From: mikecarper <135079168+mikecarper@users.noreply.github.com> Date: Fri, 8 May 2026 11:06:24 -0700 Subject: [PATCH 052/214] Revise Halo and Keymind settings documentation Updated settings and examples for Halo and Keymind configuration, including changes to retry settings and prefix handling. --- docs/halo_keymind_settings.md | 47 ++++++++--------------------------- 1 file changed, 10 insertions(+), 37 deletions(-) diff --git a/docs/halo_keymind_settings.md b/docs/halo_keymind_settings.md index c451707f..908c390c 100644 --- a/docs/halo_keymind_settings.md +++ b/docs/halo_keymind_settings.md @@ -5,7 +5,6 @@ This file covers only CLI settings added by the Halo or Keymind branches. Use ## Quick Start -Use this baseline when bringing a Halo or Keymind repeater onto the network: ```text set retry.preset rooftop @@ -26,17 +25,7 @@ get flood.retry.prefixes get flood.retry.ignore ``` -Use prefixes from debug logs such as `path=7773D0>BEEBB0` or `heard=C7618C`. Prefix lists are comma-separated hex values, for example `71CE82,C7618C`. - -## Prefix Worksheet - -Keep Halo and Keymind prefixes in one place before programming buckets or ignore lists. - -| Network | Prefix | Node or site | Use | Notes | -| --- | --- | --- | --- | --- | -| Halo | `A1B2C3` | example remote relay | bucket/target | Replace with real prefix | -| Keymind | `71CE82` | example observed relay | ignore/target | Replace with real prefix | -| Keymind | `C7618C` | example observed relay | ignore/target | Replace with real prefix | +Use prefixes from the analyzer or neighbors list or `get recent.repeater` after the repeater has been online for a few hours. ## Common Examples @@ -47,21 +36,23 @@ set flood.retry.advert off get flood.retry.advert ``` -Ignore a relay as a successful flood retry echo: +Ignore a repeater as a successful flood retry echo: +Use this if you have a car repeater and a house repeater; have the house ignore the car. ```text set flood.retry.ignore 71CE82,C7618C get flood.retry.ignore ``` -Only accept specific downstream relays as flood retry success: +Only accept specific downstream relays as flood retry success: +You're in a hole and need to hit a mountain top repeater to get out; keep trying till one you see one of these send out your packet. ```text -set flood.retry.prefixes BEEBB0,425E5C +set flood.retry.prefixes A58296,860CCA,425E5C get flood.retry.prefixes ``` -Bridge two groups of repeaters: +Bridge two groups of repeaters: ```text set flood.retry.bridge on @@ -102,7 +93,7 @@ get recent.repeater 2 Seed or correct a prefix: ```text -set recent.repeater A1B2C3 -8.5 +set recent.repeater A1B2C3 8.5 ``` Rows are sorted by prefix width, then SNR. A full direct retry failure lowers @@ -167,7 +158,7 @@ The shared retry preset sets these flood defaults: | `rooftop` | `3` | `2` | | `mobile` | `3` | `1` | -Example for Keymind path-gated retry: +Example for path-gated retry: ```text set retry.preset rooftop @@ -176,23 +167,6 @@ set flood.retry.advert off set flood.retry.ignore 71CE82,C7618C ``` -Example for Halo targeted retry: - -```text -set flood.retry.bridge off -set flood.retry.prefixes A1B2C3,D4E5F6 -set flood.retry.ignore none -``` - -Example for Halo/Keymind bridge retry: - -```text -set flood.retry.bridge on -set flood.retry.bucket 1 A1B2C3,D4E5F6 -set flood.retry.bucket 2 71CE82,C7618C -set flood.retry.advert off -``` - ## North South Buckets Buckets describe groups of repeaters on different sides of this relay. Bucket @@ -302,7 +276,6 @@ set direct.retry.count 4 If retries are too sparse: ```text -set retry.preset rooftop -set flood.retry.count 3 +set flood.retry.count 7 set flood.retry.path 2 ``` From bb8e7140a91557c3ec69d3b153d2448fae6c983f Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 8 May 2026 12:37:08 -0700 Subject: [PATCH 053/214] Add adaptive CR for direct retries --- docs/cli_commands.md | 18 +++ examples/simple_repeater/MyMesh.cpp | 52 ++++++++ examples/simple_repeater/MyMesh.h | 3 + src/Dispatcher.cpp | 23 ++++ src/Dispatcher.h | 10 ++ src/Mesh.cpp | 11 +- src/Mesh.h | 5 + src/Packet.cpp | 4 +- src/Packet.h | 1 + src/helpers/CommonCLI.cpp | 132 ++++++++++++++++++- src/helpers/CommonCLI.h | 9 ++ src/helpers/radiolib/CustomLLCC68Wrapper.h | 9 ++ src/helpers/radiolib/CustomLR1110Wrapper.h | 10 ++ src/helpers/radiolib/CustomSTM32WLxWrapper.h | 9 ++ src/helpers/radiolib/CustomSX1262Wrapper.h | 9 ++ src/helpers/radiolib/CustomSX1268Wrapper.h | 9 ++ src/helpers/radiolib/CustomSX1276Wrapper.h | 9 ++ 17 files changed, 317 insertions(+), 6 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index bf77872f..f9dcc16a 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -556,6 +556,24 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### View or change adaptive coding rate for direct retry packets +**Usage:** +- `get direct.retry.cr` +- `set direct.retry.cr ,,` +- `set direct.retry.cr ,,,` + +**Parameters:** +- `cr4_min`: SNR in dB where retry packets use `CR4` +- `cr5_min`: SNR in dB where retry packets use `CR5` +- `cr8_max`: SNR in dB where retry packets use `CR8` +- `low`: optional repeated low boundary; both low values must match + +**Default:** `10.0,7.5,2.5,2.5` + +**Note:** DM retry packets with a recent repeater table entry use that entry's SNR to pick a local transmit coding rate. With the default, SNR `10.0 dB` and up uses `CR4`, SNR `7.5 dB` and up uses `CR5`, SNR `2.5 dB` and down uses `CR8`, and the middle band uses `CR7`. `CR6` is never selected. The shorter form `set direct.retry.cr 10.0,7.5,2.5` is equivalent to `set direct.retry.cr 10.0,7.5,2.5,2.5`. + +--- + #### View or change the SNR margin used for direct retry eligibility **Usage:** - `get direct.retry.margin` diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index f9a961b9..24e5c81d 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -846,6 +846,55 @@ bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_ho return true; } +uint8_t MyMesh::getDirectRetryCodingRateForSNR(int8_t snr_x4) const { + if (snr_x4 >= _prefs.direct_retry_cr4_snr_x4) { + return 4; + } + if (snr_x4 >= _prefs.direct_retry_cr5_snr_x4) { + return 5; + } + if (snr_x4 <= _prefs.direct_retry_cr8_snr_x4) { + return 8; + } + return 7; +} +void MyMesh::configureDirectRetryPacket(mesh::Packet* retry, const mesh::Packet* original, uint8_t retry_attempt) { + (void) original; + (void) retry_attempt; + + if (retry == NULL || !retry->isRouteDirect()) { + return; + } + + switch (retry->getPayloadType()) { + case PAYLOAD_TYPE_ACK: + case PAYLOAD_TYPE_PATH: + case PAYLOAD_TYPE_REQ: + case PAYLOAD_TYPE_RESPONSE: + case PAYLOAD_TYPE_TXT_MSG: + case PAYLOAD_TYPE_ANON_REQ: + case PAYLOAD_TYPE_MULTIPART: + break; + default: + return; + } + + uint8_t prefix[MAX_ROUTE_HASH_BYTES]; + uint8_t prefix_len = 0; + if (!extractDirectRetryPrefix(retry, prefix, prefix_len)) { + return; + } + + const auto* recent = ((const SimpleMeshTables *)getTables())->findRecentRepeaterByHash(prefix, prefix_len); + if (recent == NULL) { + return; + } + + uint8_t retry_cr = getDirectRetryCodingRateForSNR(recent->snr_x4); + if (retry_cr >= 4 && retry_cr <= 8 && retry_cr != active_cr) { + retry->tx_cr = retry_cr; + } +} uint8_t MyMesh::getDirectRetryPreset() const { if (_prefs.direct_retry_preset <= DIRECT_RETRY_PRESET_MOBILE) { return _prefs.direct_retry_preset; @@ -1243,6 +1292,9 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.direct_retry_base_ms = DIRECT_RETRY_ROOFTOP_BASE_MS; _prefs.direct_retry_step_ms = DIRECT_RETRY_ROOFTOP_STEP_MS; _prefs.direct_retry_preset = DIRECT_RETRY_PRESET_ROOFTOP; + _prefs.direct_retry_cr4_snr_x4 = DIRECT_RETRY_CR4_MIN_SNR_X4_DEFAULT; + _prefs.direct_retry_cr5_snr_x4 = DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT; + _prefs.direct_retry_cr8_snr_x4 = DIRECT_RETRY_CR8_MAX_SNR_X4_DEFAULT; StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); _prefs.node_lat = ADVERT_LAT; _prefs.node_lon = ADVERT_LON; diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 1a2f505c..a3a67676 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -124,6 +124,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { bool extractDirectRetryPrefix(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const; int8_t getDirectRetryMinSNRX4() const; + uint8_t getDirectRetryCodingRateForSNR(int8_t snr_x4) const; uint8_t getDirectRetryPreset() const; uint8_t getDirectRetryConfiguredMaxAttempts() const; uint32_t getDirectRetryAttemptStepMillis() const; @@ -154,7 +155,9 @@ protected: uint32_t getRetransmitDelay(const mesh::Packet* packet) override; uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override; + uint8_t getDefaultTxCodingRate() const override { return active_cr; } bool allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const override; + void configureDirectRetryPacket(mesh::Packet* retry, const mesh::Packet* original, uint8_t retry_attempt) override; uint32_t getDirectRetryEchoDelay(const mesh::Packet* packet) const override; uint8_t getDirectRetryMaxAttempts(const mesh::Packet* packet) const override; uint32_t getDirectRetryAttemptDelay(const mesh::Packet* packet, uint8_t attempt_idx) override; diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index cccbd36c..4eb7eaf1 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -52,6 +52,13 @@ void Dispatcher::updateTxBudget() { } } +void Dispatcher::restoreOutboundCodingRate() { + if (outbound_restore_cr != 0) { + _radio->setCodingRate(outbound_restore_cr); + outbound_restore_cr = 0; + } +} + int Dispatcher::calcRxDelay(float score, uint32_t air_time) const { return (int) ((pow(10, 0.85f - score) - 1.0) * air_time); } @@ -105,6 +112,7 @@ void Dispatcher::loop() { } _radio->onSendFinished(); + restoreOutboundCodingRate(); logTx(outbound, 2 + outbound->getPathByteLen() + outbound->payload_len); onSendComplete(outbound); if (outbound->isRouteFlood()) { @@ -118,6 +126,7 @@ void Dispatcher::loop() { MESH_DEBUG_PRINTLN("%s Dispatcher::loop(): WARNING: outbound packed send timed out!", getLogDateTime()); _radio->onSendFinished(); + restoreOutboundCodingRate(); logTxFail(outbound, 2 + outbound->getPathByteLen() + outbound->payload_len); onSendFail(outbound); @@ -150,6 +159,7 @@ void Dispatcher::loop() { bool Dispatcher::tryParsePacket(Packet* pkt, const uint8_t* raw, int len) { int i = 0; + pkt->tx_cr = 0; pkt->header = raw[i++]; if (pkt->getPayloadVer() > PAYLOAD_VER_1) { MESH_DEBUG_PRINTLN("%s Dispatcher::checkRecv(): unsupported packet version", getLogDateTime()); @@ -326,11 +336,23 @@ void Dispatcher::checkSend() { memcpy(&raw[len], outbound->payload, outbound->payload_len); len += outbound->payload_len; uint32_t max_airtime = _radio->getEstAirtimeFor(len)*3/2; + outbound_restore_cr = 0; + uint8_t default_cr = getDefaultTxCodingRate(); + if (outbound->tx_cr >= 4 && outbound->tx_cr <= 8 && default_cr >= 4 && default_cr <= 8 + && outbound->tx_cr != default_cr) { + if (_radio->setCodingRate(outbound->tx_cr)) { + outbound_restore_cr = default_cr; + max_airtime = _radio->getEstAirtimeFor(len)*3/2; + } else { + MESH_DEBUG_PRINTLN("%s Dispatcher::checkSend(): WARN: failed to set packet CR%d", getLogDateTime(), (uint32_t)outbound->tx_cr); + } + } outbound_start = _ms->getMillis(); bool success = _radio->startSendRaw(raw, len); if (!success) { MESH_DEBUG_PRINTLN("%s Dispatcher::loop(): ERROR: send start failed!", getLogDateTime()); + restoreOutboundCodingRate(); logTxFail(outbound, outbound->getRawLength()); releasePacket(outbound); // return to pool @@ -361,6 +383,7 @@ Packet* Dispatcher::obtainNewPacket() { } else { pkt->payload_len = pkt->path_len = 0; pkt->_snr = 0; + pkt->tx_cr = 0; } return pkt; } diff --git a/src/Dispatcher.h b/src/Dispatcher.h index 90ee5cdb..2a910d8f 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -46,6 +46,12 @@ public: */ virtual bool startSendRaw(const uint8_t* bytes, int len) = 0; + /** + * \brief Sets LoRa coding rate for subsequent transmits/receives. + * \returns true if the radio accepted the coding rate. + */ + virtual bool setCodingRate(uint8_t cr) { return false; } + /** * \returns true if the previous 'startSendRaw()' completed successfully. */ @@ -116,6 +122,7 @@ typedef uint32_t DispatcherAction; class Dispatcher { Packet* outbound; // current outbound packet unsigned long outbound_expiry, outbound_start, total_air_time, rx_air_time; + uint8_t outbound_restore_cr; unsigned long next_tx_time; unsigned long cad_busy_start; unsigned long radio_nonrx_start; @@ -128,6 +135,7 @@ class Dispatcher { unsigned long duty_cycle_window_ms; void processRecvPacket(Packet* pkt); + void restoreOutboundCodingRate(); void updateTxBudget(); protected: @@ -140,6 +148,7 @@ protected: : _radio(&radio), _ms(&ms), _mgr(&mgr) { outbound = NULL; + outbound_restore_cr = 0; total_air_time = rx_air_time = 0; next_tx_time = ms.getMillis(); cad_busy_start = 0; @@ -167,6 +176,7 @@ protected: virtual int calcRxDelay(float score, uint32_t air_time) const; virtual uint32_t getCADFailRetryDelay() const; virtual uint32_t getCADFailMaxDuration() const; + virtual uint8_t getDefaultTxCodingRate() const { return 0; } virtual int getInterferenceThreshold() const { return 0; } // disabled by default virtual int getAGCResetInterval() const { return 0; } // disabled by default virtual unsigned long getDutyCycleWindowMs() const { return 3600000; } diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 42bcf5b1..9228c88c 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -626,6 +626,9 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { } *retry = *packet; + retry->tx_cr = 0; + uint8_t retry_attempt = _direct_retries[i].retry_attempts_sent + 1; + configureDirectRetryPacket(retry, packet, retry_attempt); uint32_t retry_delay = getDirectRetryAttemptDelay(packet, _direct_retries[i].retry_attempts_sent); sendPacket(retry, _direct_retries[i].priority, retry_delay); if (isDirectRetryQueued(retry)) { @@ -633,10 +636,10 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { _direct_retries[i].retry_delay = retry_delay; _direct_retries[i].retry_at = futureMillis(retry_delay); _direct_retries[i].waiting_final_echo = false; - onDirectRetryEvent("queued", retry, retry_delay, _direct_retries[i].retry_attempts_sent + 1); + onDirectRetryEvent("queued", retry, retry_delay, retry_attempt); } else { - onDirectRetryEvent("dropped_queue_full", retry, retry_delay, _direct_retries[i].retry_attempts_sent + 1); - onDirectRetryEvent("failure", retry, elapsed_millis, _direct_retries[i].retry_attempts_sent + 1); + onDirectRetryEvent("dropped_queue_full", retry, retry_delay, retry_attempt); + onDirectRetryEvent("failure", retry, elapsed_millis, retry_attempt); clearDirectRetrySlot(i); } } @@ -657,6 +660,8 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { } *retry = *packet; + retry->tx_cr = 0; + configureDirectRetryPacket(retry, packet, 1); // Start the echo wait only after the initial direct transmission actually completed. sendPacket(retry, _direct_retries[i].priority, _direct_retries[i].retry_delay); diff --git a/src/Mesh.h b/src/Mesh.h index 91aec9a6..f2ce4538 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -130,6 +130,11 @@ protected: */ virtual void onDirectRetryEvent(const char* event, const Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) { } + /** + * \brief Optional hook to set local-only transmit options on a retry packet before it is queued. + */ + virtual void configureDirectRetryPacket(Packet* retry, const Packet* original, uint8_t retry_attempt) { } + /** * \brief Perform search of local DB of peers/contacts. * \returns Number of peers with matching hash diff --git a/src/Packet.cpp b/src/Packet.cpp index aad3e2f4..3aab6349 100644 --- a/src/Packet.cpp +++ b/src/Packet.cpp @@ -8,6 +8,7 @@ Packet::Packet() { header = 0; path_len = 0; payload_len = 0; + tx_cr = 0; } bool Packet::isValidPathLen(uint8_t path_len) { @@ -64,6 +65,7 @@ uint8_t Packet::writeTo(uint8_t dest[]) const { bool Packet::readFrom(const uint8_t src[], uint8_t len) { uint8_t i = 0; + tx_cr = 0; header = src[i++]; if (hasTransportCodes()) { memcpy(&transport_codes[0], &src[i], 2); i += 2; @@ -84,4 +86,4 @@ bool Packet::readFrom(const uint8_t src[], uint8_t len) { return true; // success } -} \ No newline at end of file +} diff --git a/src/Packet.h b/src/Packet.h index 0886a06c..2943c037 100644 --- a/src/Packet.h +++ b/src/Packet.h @@ -49,6 +49,7 @@ public: uint8_t path[MAX_PATH_SIZE]; uint8_t payload[MAX_PACKET_PAYLOAD]; int8_t _snr; + uint8_t tx_cr; // volatile local-only TX coding-rate override; not serialized /** * \brief calculate the hash of payload + type diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index dc206516..42b41f29 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -85,6 +85,38 @@ static uint8_t directRetryEffectiveMarginX4(const NodePrefs* prefs) { return constrain(prefs->direct_retry_snr_margin_db, (uint8_t)0, (uint8_t)DIRECT_RETRY_SNR_MARGIN_X4_MAX); } +static float directRetryCrX4ToDb(int8_t snr_x4) { + return ((float)snr_x4) / 4.0f; +} + +static void setDirectRetryCrDefaults(NodePrefs* prefs) { + prefs->direct_retry_cr4_snr_x4 = DIRECT_RETRY_CR4_MIN_SNR_X4_DEFAULT; + prefs->direct_retry_cr5_snr_x4 = DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT; + prefs->direct_retry_cr8_snr_x4 = DIRECT_RETRY_CR8_MAX_SNR_X4_DEFAULT; +} + +static bool directRetryCrThresholdsAreValid(int8_t cr4_snr_x4, int8_t cr5_snr_x4, int8_t cr8_snr_x4) { + return (cr4_snr_x4 != 0 || cr5_snr_x4 != 0 || cr8_snr_x4 != 0) + && cr4_snr_x4 >= cr5_snr_x4 + && cr5_snr_x4 >= cr8_snr_x4; +} + +static void sanitizeDirectRetryCrThresholds(NodePrefs* prefs) { + if (!directRetryCrThresholdsAreValid(prefs->direct_retry_cr4_snr_x4, + prefs->direct_retry_cr5_snr_x4, + prefs->direct_retry_cr8_snr_x4)) { + setDirectRetryCrDefaults(prefs); + } +} + +static void formatDirectRetryCrThresholds(const NodePrefs* prefs, char* reply) { + char cr4[12], cr5[12], cr8[12]; + strcpy(cr4, StrHelper::ftoa(directRetryCrX4ToDb(prefs->direct_retry_cr4_snr_x4))); + strcpy(cr5, StrHelper::ftoa(directRetryCrX4ToDb(prefs->direct_retry_cr5_snr_x4))); + strcpy(cr8, StrHelper::ftoa(directRetryCrX4ToDb(prefs->direct_retry_cr8_snr_x4))); + sprintf(reply, "> %s,%s,%s,%s", cr4, cr5, cr8, cr8); +} + static uint16_t directRetryPresetStepDefault(uint8_t preset) { switch (directRetryPresetOrDefault(preset)) { case DIRECT_RETRY_PRESET_INFRA: @@ -142,6 +174,63 @@ static bool parseDirectRetryPreset(const char* value, uint8_t& preset) { return false; } +static bool parseDirectRetryCrDb(const char* value, int8_t& snr_x4) { + if (value == NULL) { + return false; + } + + char* end = NULL; + float snr_db = strtof(value, &end); + while (end != NULL && *end == ' ') end++; + if (end == value || (end != NULL && *end != 0)) { + return false; + } + + int32_t scaled_x4 = (int32_t)((snr_db * 4.0f) + (snr_db >= 0.0f ? 0.5f : -0.5f)); + if (scaled_x4 < DIRECT_RETRY_CR_SNR_X4_MIN || scaled_x4 > DIRECT_RETRY_CR_SNR_X4_MAX) { + return false; + } + snr_x4 = (int8_t)scaled_x4; + return true; +} + +static bool parseDirectRetryCrThresholds(char* value, NodePrefs* prefs) { + if (value == NULL || prefs == NULL) { + return false; + } + + const char* parts[4]; + int num = mesh::Utils::parseTextParts(value, parts, 4); + if (num != 3 && num != 4) { + return false; + } + + int8_t cr4_snr_x4; + int8_t cr5_snr_x4; + int8_t cr8_snr_x4; + if (!parseDirectRetryCrDb(parts[0], cr4_snr_x4) + || !parseDirectRetryCrDb(parts[1], cr5_snr_x4) + || !parseDirectRetryCrDb(parts[num == 4 ? 3 : 2], cr8_snr_x4)) { + return false; + } + + if (num == 4) { + int8_t repeated_low_snr_x4; + if (!parseDirectRetryCrDb(parts[2], repeated_low_snr_x4) || repeated_low_snr_x4 != cr8_snr_x4) { + return false; + } + } + + if (!directRetryCrThresholdsAreValid(cr4_snr_x4, cr5_snr_x4, cr8_snr_x4)) { + return false; + } + + prefs->direct_retry_cr4_snr_x4 = cr4_snr_x4; + prefs->direct_retry_cr5_snr_x4 = cr5_snr_x4; + prefs->direct_retry_cr8_snr_x4 = cr8_snr_x4; + return true; +} + static bool isValidName(const char *n) { while (*n) { if (*n == '[' || *n == ']' || *n == '\\' || *n == ':' || *n == ',' || *n == '?' || *n == '*') return false; @@ -157,6 +246,8 @@ void CommonCLI::loadPrefs(FILESYSTEM* fs) { loadPrefsInt(fs, "/node_prefs"); savePrefs(fs); // save to new filename fs->remove("/node_prefs"); // remove old + } else { + setDirectRetryCrDefaults(_prefs); } } @@ -227,6 +318,13 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->direct_retry_preset, sizeof(_prefs->direct_retry_preset)); // 297 size_t retry_step_read = file.read((uint8_t *)&_prefs->direct_retry_step_ms, sizeof(_prefs->direct_retry_step_ms)); // 298 + size_t retry_cr_read = 0; + retry_cr_read += file.read((uint8_t *)&_prefs->direct_retry_cr4_snr_x4, + sizeof(_prefs->direct_retry_cr4_snr_x4)); // 300 + retry_cr_read += file.read((uint8_t *)&_prefs->direct_retry_cr5_snr_x4, + sizeof(_prefs->direct_retry_cr5_snr_x4)); // 301 + retry_cr_read += file.read((uint8_t *)&_prefs->direct_retry_cr8_snr_x4, + sizeof(_prefs->direct_retry_cr8_snr_x4)); // 302 // PowerSaving-only prefs stored radio_fem_rxgain at 291, before direct retry timing existed. if (radio_fem_rxgain_read != sizeof(_prefs->radio_fem_rxgain) && legacy_retry_attempts_read == sizeof(legacy_retry_attempts_or_radio_fem_rxgain) @@ -234,7 +332,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { || _prefs->direct_retry_timing_magic[1] != DIRECT_RETRY_TIMING_MAGIC_1)) { _prefs->radio_fem_rxgain = constrain(legacy_retry_attempts_or_radio_fem_rxgain, 0, 1); } - // next: 298 + // next: 303 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -288,6 +386,13 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { } else { _prefs->direct_retry_step_ms = constrain(_prefs->direct_retry_step_ms, DIRECT_RETRY_STEP_MS_MIN, DIRECT_RETRY_STEP_MS_MAX); } + if (retry_cr_read != sizeof(_prefs->direct_retry_cr4_snr_x4) + + sizeof(_prefs->direct_retry_cr5_snr_x4) + + sizeof(_prefs->direct_retry_cr8_snr_x4)) { + setDirectRetryCrDefaults(_prefs); + } else { + sanitizeDirectRetryCrThresholds(_prefs); + } file.close(); } @@ -360,7 +465,10 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 296 file.write((uint8_t *)&_prefs->direct_retry_preset, sizeof(_prefs->direct_retry_preset)); // 297 file.write((uint8_t *)&_prefs->direct_retry_step_ms, sizeof(_prefs->direct_retry_step_ms)); // 298 - // next: 300 + file.write((uint8_t *)&_prefs->direct_retry_cr4_snr_x4, sizeof(_prefs->direct_retry_cr4_snr_x4)); // 300 + file.write((uint8_t *)&_prefs->direct_retry_cr5_snr_x4, sizeof(_prefs->direct_retry_cr5_snr_x4)); // 301 + file.write((uint8_t *)&_prefs->direct_retry_cr8_snr_x4, sizeof(_prefs->direct_retry_cr8_snr_x4)); // 302 + // next: 303 file.close(); } @@ -532,6 +640,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re sprintf(reply, "> %d", (uint32_t)directRetryEffectiveBaseMs(_prefs)); } else if (memcmp(config, "direct.retry.step", 17) == 0) { sprintf(reply, "> %d", (uint32_t)directRetryEffectiveStepMs(_prefs)); + } else if (memcmp(config, "direct.retry.cr", 15) == 0) { + formatDirectRetryCrThresholds(_prefs, reply); } else if (memcmp(config, "owner.info", 10) == 0) { *reply++ = '>'; *reply++ = ' '; @@ -838,6 +948,14 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else { sprintf(reply, "Error, min %d and max %d", DIRECT_RETRY_STEP_MS_MIN, DIRECT_RETRY_STEP_MS_MAX); } + } else if (memcmp(config, "direct.retry.cr ", 16) == 0) { + StrHelper::strncpy(tmp, &config[16], sizeof(tmp)); + if (parseDirectRetryCrThresholds(tmp, _prefs)) { + savePrefs(); + formatDirectRetryCrThresholds(_prefs, reply); + } else { + strcpy(reply, "Error, expected cr4,cr5,cr8 or cr4,cr5,low,low"); + } } else if (memcmp(config, "owner.info ", 11) == 0) { config += 11; char *dp = _prefs->owner_info; @@ -1392,6 +1510,14 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { sprintf(reply, "Error, min %d and max %d", DIRECT_RETRY_STEP_MS_MIN, DIRECT_RETRY_STEP_MS_MAX); } + } else if (memcmp(config, "direct.retry.cr ", 16) == 0) { + StrHelper::strncpy(tmp, &config[16], sizeof(tmp)); + if (parseDirectRetryCrThresholds(tmp, _prefs)) { + savePrefs(); + formatDirectRetryCrThresholds(_prefs, reply); + } else { + strcpy(reply, "Error, expected cr4,cr5,cr8 or cr4,cr5,low,low"); + } } else if (memcmp(config, "owner.info ", 11) == 0) { config += 11; char *dp = _prefs->owner_info; @@ -1581,6 +1707,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "> %d", (uint32_t)directRetryEffectiveBaseMs(_prefs)); } else if (memcmp(config, "direct.retry.step", 17) == 0) { sprintf(reply, "> %d", (uint32_t)directRetryEffectiveStepMs(_prefs)); + } else if (memcmp(config, "direct.retry.cr", 15) == 0) { + formatDirectRetryCrThresholds(_prefs, reply); } else if (memcmp(config, "owner.info", 10) == 0) { *reply++ = '>'; *reply++ = ' '; diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index ea30777a..43e8e6d7 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -38,6 +38,12 @@ #define DIRECT_RETRY_MOBILE_STEP_MS 50 #define DIRECT_RETRY_MOBILE_MARGIN_X4 0 +#define DIRECT_RETRY_CR4_MIN_SNR_X4_DEFAULT 40 // 10.0 dB and up => CR4 +#define DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT 30 // 7.5 dB and up => CR5 +#define DIRECT_RETRY_CR8_MAX_SNR_X4_DEFAULT 10 // 2.5 dB and down => CR8 +#define DIRECT_RETRY_CR_SNR_X4_MIN -128 +#define DIRECT_RETRY_CR_SNR_X4_MAX 127 + struct NodePrefs { // persisted to file float airtime_factor; char node_name[32]; @@ -88,6 +94,9 @@ struct NodePrefs { // persisted to file uint8_t direct_retry_timing_magic[2]; uint8_t direct_retry_preset; uint16_t direct_retry_step_ms; + int8_t direct_retry_cr4_snr_x4; + int8_t direct_retry_cr5_snr_x4; + int8_t direct_retry_cr8_snr_x4; }; class CommonCLICallbacks { diff --git a/src/helpers/radiolib/CustomLLCC68Wrapper.h b/src/helpers/radiolib/CustomLLCC68Wrapper.h index fc0975cf..1b1ddcaa 100644 --- a/src/helpers/radiolib/CustomLLCC68Wrapper.h +++ b/src/helpers/radiolib/CustomLLCC68Wrapper.h @@ -20,6 +20,15 @@ public: int sf = ((CustomLLCC68 *)_radio)->spreadingFactor; return packetScoreInt(snr, sf, packet_len); } + bool setCodingRate(uint8_t cr) override { + idle(); + int err = ((CustomLLCC68 *)_radio)->setCodingRate(cr); + if (err != RADIOLIB_ERR_NONE) { + MESH_DEBUG_PRINTLN("CustomLLCC68Wrapper: error: setCodingRate(%d)=%d", (uint32_t)cr, err); + return false; + } + return true; + } void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); } diff --git a/src/helpers/radiolib/CustomLR1110Wrapper.h b/src/helpers/radiolib/CustomLR1110Wrapper.h index 42d36440..b07e561d 100644 --- a/src/helpers/radiolib/CustomLR1110Wrapper.h +++ b/src/helpers/radiolib/CustomLR1110Wrapper.h @@ -25,6 +25,16 @@ public: float getLastRSSI() const override { return ((CustomLR1110 *)_radio)->getRSSI(); } float getLastSNR() const override { return ((CustomLR1110 *)_radio)->getSNR(); } + bool setCodingRate(uint8_t cr) override { + idle(); + int err = ((CustomLR1110 *)_radio)->setCodingRate(cr); + if (err != RADIOLIB_ERR_NONE) { + MESH_DEBUG_PRINTLN("CustomLR1110Wrapper: error: setCodingRate(%d)=%d", (uint32_t)cr, err); + return false; + } + return true; + } + void setRxBoostedGainMode(bool en) override { ((CustomLR1110 *)_radio)->setRxBoostedGainMode(en); } diff --git a/src/helpers/radiolib/CustomSTM32WLxWrapper.h b/src/helpers/radiolib/CustomSTM32WLxWrapper.h index e3e52029..d0aa2dae 100644 --- a/src/helpers/radiolib/CustomSTM32WLxWrapper.h +++ b/src/helpers/radiolib/CustomSTM32WLxWrapper.h @@ -21,6 +21,15 @@ public: int sf = ((CustomSTM32WLx *)_radio)->spreadingFactor; return packetScoreInt(snr, sf, packet_len); } + bool setCodingRate(uint8_t cr) override { + idle(); + int err = ((CustomSTM32WLx *)_radio)->setCodingRate(cr); + if (err != RADIOLIB_ERR_NONE) { + MESH_DEBUG_PRINTLN("CustomSTM32WLxWrapper: error: setCodingRate(%d)=%d", (uint32_t)cr, err); + return false; + } + return true; + } void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); } }; diff --git a/src/helpers/radiolib/CustomSX1262Wrapper.h b/src/helpers/radiolib/CustomSX1262Wrapper.h index 6499deb2..72f6ba38 100644 --- a/src/helpers/radiolib/CustomSX1262Wrapper.h +++ b/src/helpers/radiolib/CustomSX1262Wrapper.h @@ -24,6 +24,15 @@ public: int sf = ((CustomSX1262 *)_radio)->spreadingFactor; return packetScoreInt(snr, sf, packet_len); } + bool setCodingRate(uint8_t cr) override { + idle(); + int err = ((CustomSX1262 *)_radio)->setCodingRate(cr); + if (err != RADIOLIB_ERR_NONE) { + MESH_DEBUG_PRINTLN("CustomSX1262Wrapper: error: setCodingRate(%d)=%d", (uint32_t)cr, err); + return false; + } + return true; + } virtual void powerOff() override { ((CustomSX1262 *)_radio)->sleep(false); } diff --git a/src/helpers/radiolib/CustomSX1268Wrapper.h b/src/helpers/radiolib/CustomSX1268Wrapper.h index 54c37ee8..50dfa9c2 100644 --- a/src/helpers/radiolib/CustomSX1268Wrapper.h +++ b/src/helpers/radiolib/CustomSX1268Wrapper.h @@ -24,6 +24,15 @@ public: int sf = ((CustomSX1268 *)_radio)->spreadingFactor; return packetScoreInt(snr, sf, packet_len); } + bool setCodingRate(uint8_t cr) override { + idle(); + int err = ((CustomSX1268 *)_radio)->setCodingRate(cr); + if (err != RADIOLIB_ERR_NONE) { + MESH_DEBUG_PRINTLN("CustomSX1268Wrapper: error: setCodingRate(%d)=%d", (uint32_t)cr, err); + return false; + } + return true; + } void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); } diff --git a/src/helpers/radiolib/CustomSX1276Wrapper.h b/src/helpers/radiolib/CustomSX1276Wrapper.h index 5cde72f7..dd976306 100644 --- a/src/helpers/radiolib/CustomSX1276Wrapper.h +++ b/src/helpers/radiolib/CustomSX1276Wrapper.h @@ -23,4 +23,13 @@ public: int sf = ((CustomSX1276 *)_radio)->spreadingFactor; return packetScoreInt(snr, sf, packet_len); } + bool setCodingRate(uint8_t cr) override { + idle(); + int err = ((CustomSX1276 *)_radio)->setCodingRate(cr); + if (err != RADIOLIB_ERR_NONE) { + MESH_DEBUG_PRINTLN("CustomSX1276Wrapper: error: setCodingRate(%d)=%d", (uint32_t)cr, err); + return false; + } + return true; + } }; From 7d237f873001b1f08775ca5da80d7a284ab20e8f Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 8 May 2026 16:57:03 -0700 Subject: [PATCH 054/214] Update keymind retry controls --- docs/cli_commands.md | 10 +-- docs/halo_keymind_settings.md | 11 +++- examples/simple_repeater/MyMesh.cpp | 37 ++++++++--- src/Mesh.cpp | 5 +- src/helpers/ArduinoSerialInterface.cpp | 4 ++ src/helpers/ArduinoSerialInterface.h | 3 +- src/helpers/CommonCLI.cpp | 86 ++++++++++++++++++-------- src/helpers/CommonCLI.h | 2 + 8 files changed, 114 insertions(+), 44 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 505a8205..18e80163 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -559,18 +559,18 @@ This document provides an overview of CLI commands that can be sent to MeshCore #### View or change adaptive coding rate for direct retry packets **Usage:** - `get direct.retry.cr` -- `set direct.retry.cr ,,` -- `set direct.retry.cr ,,,` +- `set direct.retry.cr ,,,` +- `set direct.retry.cr off` **Parameters:** - `cr4_min`: SNR in dB where retry packets use `CR4` - `cr5_min`: SNR in dB where retry packets use `CR5` +- `cr7_min`: SNR in dB where retry packets use `CR7` - `cr8_max`: SNR in dB where retry packets use `CR8` -- `low`: optional repeated low boundary; both low values must match **Default:** `10.0,7.5,2.5,2.5` -**Note:** DM retry packets with a recent repeater table entry use that entry's SNR to pick a local transmit coding rate. With the default, SNR `10.0 dB` and up uses `CR4`, SNR `7.5 dB` and up uses `CR5`, SNR `2.5 dB` and down uses `CR8`, and the middle band uses `CR7`. `CR6` is never selected. The shorter form `set direct.retry.cr 10.0,7.5,2.5` is equivalent to `set direct.retry.cr 10.0,7.5,2.5,2.5`. +**Note:** DM retry packets use the next-hop SNR from a recent repeater table entry to pick a local transmit coding rate; if no recent entry is available, retry packets use `CR5`. With the default, SNR `10.0 dB` and up uses `CR4`, SNR `7.5 dB` and up uses `CR5`, SNR `2.5 dB` and down uses `CR8`, and the middle band uses `CR7`. `CR6` is never selected. Use `set direct.retry.cr off` to disable adaptive coding-rate overrides. If adaptive selection chooses `CR4`, retries after the third attempt use `CR5`. --- @@ -778,7 +778,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore - `set flood.retry.count ` **Parameters:** -- `value`: Maximum retry attempts after initial flood TX (`0`-`3`) +- `value`: Maximum retry attempts after initial flood TX (`0`-`15`) **Default:** `3` for `rooftop` and `mobile`, `1` for `infra` diff --git a/docs/halo_keymind_settings.md b/docs/halo_keymind_settings.md index 908c390c..1be3ddb0 100644 --- a/docs/halo_keymind_settings.md +++ b/docs/halo_keymind_settings.md @@ -111,6 +111,15 @@ Direct retry applies to direct-routed packets. A queued resend is canceled when | `direct.retry.count` | Maximum direct retry attempts after initial TX. | `get direct.retry.count`, `set direct.retry.count <1-15>` | `set direct.retry.count 15` | | `direct.retry.base` | Base wait in milliseconds before retry. | `get direct.retry.base`, `set direct.retry.base <10-5000>` | `set direct.retry.base 175` | | `direct.retry.step` | Milliseconds added per retry attempt. | `get direct.retry.step`, `set direct.retry.step <0-5000>` | `set direct.retry.step 100` | +| `direct.retry.cr` | Adaptive coding-rate thresholds for direct retry packets. Uses `CR4`, `CR5`, `CR7`, or `CR8`; `CR6` is never selected. | `get direct.retry.cr`, `set direct.retry.cr ,,,`, `set direct.retry.cr off` | `set direct.retry.cr 10.0,7.5,2.5,0` | + +The default adaptive coding-rate profile is `10.0,7.5,2.5,2.5`. +SNR `10.0 dB` and up uses `CR4`, `7.5 dB` and up uses `CR5`, +`2.5 dB` and down uses `CR8`, and the middle band uses `CR7`. If no +recent repeater table entry is available, retry packets use `CR5`. Use +`set direct.retry.cr off` to disable adaptive coding-rate overrides. If +adaptive selection chooses `CR4`, retries after the third attempt use +`CR5`. Preset details: @@ -141,7 +150,7 @@ Flood retry applies to flood-routed packets. A queued retry is canceled when a q | Setting | What it does | How to use | Example | | --- | --- | --- | --- | -| `flood.retry.count` | Maximum flood retry attempts after initial TX. `0` disables flood retry. | `get flood.retry.count`, `set flood.retry.count <0-3>` | `set flood.retry.count 3` | +| `flood.retry.count` | Maximum flood retry attempts after initial TX. `0` disables flood retry. | `get flood.retry.count`, `set flood.retry.count <0-15>` | `set flood.retry.count 7` | | `flood.retry.path` | Maximum path hash count eligible for flood retry, or `off` to disable the gate. | `get flood.retry.path`, `set flood.retry.path <0-63/off>` | `set flood.retry.path 1` | | `flood.retry.advert` | Allows or blocks retry for node advert packets (`type=4`). Default is `off`. | `get flood.retry.advert`, `set flood.retry.advert on/off` | `set flood.retry.advert off` | | `flood.retry.prefixes` | Target prefixes. If set, only matching downstream echoes cancel a retry. | `get flood.retry.prefixes`, `set flood.retry.prefixes ` | `set flood.retry.prefixes BEEBB0,425E5C` | diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index b127db30..d8e72cde 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -744,7 +744,8 @@ void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, u time_label = "echo_ms"; } - MESH_DEBUG_PRINTLN("%s direct retry %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%s, next_hop=%s, pkt=%s, tbl=%s, %s=%lu)", + uint8_t log_cr = (packet->tx_cr >= 4 && packet->tx_cr <= 8) ? packet->tx_cr : active_cr; + MESH_DEBUG_PRINTLN("%s direct retry %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%s, next_hop=%s, pkt=%s, tbl=%s, cr=%u, %s=%lu)", getLogDateTime(), event, (unsigned int)retry_attempt, @@ -755,6 +756,7 @@ void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, u next_hop, snr_pkt_text, snr_table_text, + (unsigned int)log_cr, time_label, (unsigned long)delay_millis); @@ -762,7 +764,7 @@ void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, u File f = openAppend(PACKET_LOG_FILE); if (f) { f.print(getLogDateTime()); - f.printf(": DIRECT RETRY %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%s, next_hop=%s, pkt=%s, tbl=%s, %s=%lu)\n", + f.printf(": DIRECT RETRY %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%s, next_hop=%s, pkt=%s, tbl=%s, cr=%u, %s=%lu)\n", event, (unsigned int)retry_attempt, (uint32_t)packet->getPayloadType(), @@ -772,6 +774,7 @@ void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, u next_hop, snr_pkt_text, snr_table_text, + (unsigned int)log_cr, time_label, (unsigned long)delay_millis); f.close(); @@ -847,6 +850,12 @@ bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_ho return true; } uint8_t MyMesh::getDirectRetryCodingRateForSNR(int8_t snr_x4) const { + if (_prefs.direct_retry_cr4_snr_x4 == 0 + && _prefs.direct_retry_cr5_snr_x4 == 0 + && _prefs.direct_retry_cr7_snr_x4 == 0 + && _prefs.direct_retry_cr8_snr_x4 == 0) { + return 0; + } if (snr_x4 >= _prefs.direct_retry_cr4_snr_x4) { return 4; } @@ -856,11 +865,13 @@ uint8_t MyMesh::getDirectRetryCodingRateForSNR(int8_t snr_x4) const { if (snr_x4 <= _prefs.direct_retry_cr8_snr_x4) { return 8; } + if (snr_x4 >= _prefs.direct_retry_cr7_snr_x4) { + return 7; + } return 7; } void MyMesh::configureDirectRetryPacket(mesh::Packet* retry, const mesh::Packet* original, uint8_t retry_attempt) { (void) original; - (void) retry_attempt; if (retry == NULL || !retry->isRouteDirect()) { return; @@ -873,6 +884,7 @@ void MyMesh::configureDirectRetryPacket(mesh::Packet* retry, const mesh::Packet* case PAYLOAD_TYPE_RESPONSE: case PAYLOAD_TYPE_TXT_MSG: case PAYLOAD_TYPE_ANON_REQ: + case PAYLOAD_TYPE_TRACE: case PAYLOAD_TYPE_MULTIPART: break; default: @@ -885,13 +897,19 @@ void MyMesh::configureDirectRetryPacket(mesh::Packet* retry, const mesh::Packet* return; } - const auto* recent = ((const SimpleMeshTables *)getTables())->findRecentRepeaterByHash(prefix, prefix_len); - if (recent == NULL) { + if (_prefs.direct_retry_cr4_snr_x4 == 0 + && _prefs.direct_retry_cr5_snr_x4 == 0 + && _prefs.direct_retry_cr7_snr_x4 == 0 + && _prefs.direct_retry_cr8_snr_x4 == 0) { return; } - uint8_t retry_cr = getDirectRetryCodingRateForSNR(recent->snr_x4); - if (retry_cr >= 4 && retry_cr <= 8 && retry_cr != active_cr) { + const auto* recent = ((const SimpleMeshTables *)getTables())->findRecentRepeaterByHash(prefix, prefix_len); + uint8_t retry_cr = (recent != NULL) ? getDirectRetryCodingRateForSNR(recent->snr_x4) : 5; + if (retry_cr == 4 && retry_attempt > 3) { + retry_cr = 5; + } + if (retry_cr >= 4 && retry_cr <= 8 && retry_cr != 6 && retry_cr != active_cr) { retry->tx_cr = retry_cr; } } @@ -1145,7 +1163,7 @@ uint32_t MyMesh::getDirectRetryEchoDelay(const mesh::Packet* packet) const { return base_wait_millis + scaled_wait_millis; } bool MyMesh::allowFloodRetry(const mesh::Packet* packet) const { - if (_prefs.disable_fwd || constrain(_prefs.flood_retry_attempts, (uint8_t)0, (uint8_t)3) == 0) { + if (_prefs.disable_fwd || constrain(_prefs.flood_retry_attempts, (uint8_t)0, (uint8_t)15) == 0) { return false; } if (packet != NULL && packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && !_prefs.flood_retry_advert_enabled) { @@ -1401,7 +1419,7 @@ uint8_t MyMesh::getFloodRetryMaxAttempts(const mesh::Packet* packet) const { if (_prefs.disable_fwd) { return 0; } - return constrain(_prefs.flood_retry_attempts, (uint8_t)0, (uint8_t)3); + return constrain(_prefs.flood_retry_attempts, (uint8_t)0, (uint8_t)15); } bool MyMesh::isFloodRetryEchoTarget(const mesh::Packet* packet, uint8_t progress_marker) const { if (packet == NULL || !packet->isRouteFlood()) { @@ -1809,6 +1827,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_retry_advert_enabled = 0; _prefs.direct_retry_cr4_snr_x4 = DIRECT_RETRY_CR4_MIN_SNR_X4_DEFAULT; _prefs.direct_retry_cr5_snr_x4 = DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT; + _prefs.direct_retry_cr7_snr_x4 = DIRECT_RETRY_CR7_MIN_SNR_X4_DEFAULT; _prefs.direct_retry_cr8_snr_x4 = DIRECT_RETRY_CR8_MAX_SNR_X4_DEFAULT; StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); _prefs.node_lat = ADVERT_LAT; diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 730d9f49..747ab36d 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -6,6 +6,7 @@ namespace mesh { static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT = 15; static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX = 15; static const uint8_t FLOOD_RETRY_MAX_ATTEMPTS_DEFAULT = 3; +static const uint8_t FLOOD_RETRY_MAX_ATTEMPTS_HARD_MAX = 15; static uint8_t decodeTraceHashSize(uint8_t flags, uint8_t route_bytes) { uint8_t code = flags & 0x03; @@ -966,8 +967,8 @@ void Mesh::armFloodRetryOnSendComplete(const Packet* packet) { uint8_t max_attempts = getFloodRetryMaxAttempts(packet); if (max_attempts < 1) { max_attempts = 1; - } else if (max_attempts > FLOOD_RETRY_MAX_ATTEMPTS_DEFAULT) { - max_attempts = FLOOD_RETRY_MAX_ATTEMPTS_DEFAULT; + } else if (max_attempts > FLOOD_RETRY_MAX_ATTEMPTS_HARD_MAX) { + max_attempts = FLOOD_RETRY_MAX_ATTEMPTS_HARD_MAX; } if (_flood_retries[i].retry_attempts_sent >= max_attempts) { Packet* final_wait = obtainNewPacket(); diff --git a/src/helpers/ArduinoSerialInterface.cpp b/src/helpers/ArduinoSerialInterface.cpp index a01fa586..6b443974 100644 --- a/src/helpers/ArduinoSerialInterface.cpp +++ b/src/helpers/ArduinoSerialInterface.cpp @@ -17,6 +17,10 @@ bool ArduinoSerialInterface::isConnected() const { return true; // no way of knowing, so assume yes } +bool ArduinoSerialInterface::isReadBusy() const { + return false; +} + bool ArduinoSerialInterface::isWriteBusy() const { return false; } diff --git a/src/helpers/ArduinoSerialInterface.h b/src/helpers/ArduinoSerialInterface.h index c4086353..4fa2b75d 100644 --- a/src/helpers/ArduinoSerialInterface.h +++ b/src/helpers/ArduinoSerialInterface.h @@ -28,7 +28,8 @@ public: bool isConnected() const override; + bool isReadBusy() const override; bool isWriteBusy() const override; size_t writeFrame(const uint8_t src[], size_t len) override; size_t checkRecvFrame(uint8_t dest[]) override; -}; \ No newline at end of file +}; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 6a383ce4..db56c558 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -33,7 +33,7 @@ #define FLOOD_RETRY_PREFS_MAGIC_0 0xF4 #define FLOOD_RETRY_PREFS_MAGIC_1 0x52 #define FLOOD_RETRY_COUNT_MIN 0 -#define FLOOD_RETRY_COUNT_MAX 3 +#define FLOOD_RETRY_COUNT_MAX 15 #define FLOOD_RETRY_ADVERT_DEFAULT 0 // Believe it or not, this std C function is busted on some platforms! @@ -121,29 +121,53 @@ static float directRetryCrX4ToDb(int8_t snr_x4) { static void setDirectRetryCrDefaults(NodePrefs* prefs) { prefs->direct_retry_cr4_snr_x4 = DIRECT_RETRY_CR4_MIN_SNR_X4_DEFAULT; prefs->direct_retry_cr5_snr_x4 = DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT; + prefs->direct_retry_cr7_snr_x4 = DIRECT_RETRY_CR7_MIN_SNR_X4_DEFAULT; prefs->direct_retry_cr8_snr_x4 = DIRECT_RETRY_CR8_MAX_SNR_X4_DEFAULT; } -static bool directRetryCrThresholdsAreValid(int8_t cr4_snr_x4, int8_t cr5_snr_x4, int8_t cr8_snr_x4) { - return (cr4_snr_x4 != 0 || cr5_snr_x4 != 0 || cr8_snr_x4 != 0) - && cr4_snr_x4 >= cr5_snr_x4 - && cr5_snr_x4 >= cr8_snr_x4; +static void setDirectRetryCrOff(NodePrefs* prefs) { + prefs->direct_retry_cr4_snr_x4 = 0; + prefs->direct_retry_cr5_snr_x4 = 0; + prefs->direct_retry_cr7_snr_x4 = 0; + prefs->direct_retry_cr8_snr_x4 = 0; +} + +static bool directRetryCrThresholdsAreOff(int8_t cr4_snr_x4, int8_t cr5_snr_x4, int8_t cr7_snr_x4, int8_t cr8_snr_x4) { + return cr4_snr_x4 == 0 && cr5_snr_x4 == 0 && cr7_snr_x4 == 0 && cr8_snr_x4 == 0; +} + +static bool directRetryCrThresholdsAreValid(int8_t cr4_snr_x4, int8_t cr5_snr_x4, int8_t cr7_snr_x4, int8_t cr8_snr_x4) { + return directRetryCrThresholdsAreOff(cr4_snr_x4, cr5_snr_x4, cr7_snr_x4, cr8_snr_x4) + || ((cr4_snr_x4 != 0 || cr5_snr_x4 != 0 || cr7_snr_x4 != 0 || cr8_snr_x4 != 0) + && cr4_snr_x4 >= cr5_snr_x4 + && cr5_snr_x4 >= cr7_snr_x4 + && cr7_snr_x4 >= cr8_snr_x4); } static void sanitizeDirectRetryCrThresholds(NodePrefs* prefs) { if (!directRetryCrThresholdsAreValid(prefs->direct_retry_cr4_snr_x4, prefs->direct_retry_cr5_snr_x4, + prefs->direct_retry_cr7_snr_x4, prefs->direct_retry_cr8_snr_x4)) { setDirectRetryCrDefaults(prefs); } } static void formatDirectRetryCrThresholds(const NodePrefs* prefs, char* reply) { - char cr4[12], cr5[12], cr8[12]; + if (directRetryCrThresholdsAreOff(prefs->direct_retry_cr4_snr_x4, + prefs->direct_retry_cr5_snr_x4, + prefs->direct_retry_cr7_snr_x4, + prefs->direct_retry_cr8_snr_x4)) { + strcpy(reply, "> off"); + return; + } + + char cr4[12], cr5[12], cr7[12], cr8[12]; strcpy(cr4, StrHelper::ftoa(directRetryCrX4ToDb(prefs->direct_retry_cr4_snr_x4))); strcpy(cr5, StrHelper::ftoa(directRetryCrX4ToDb(prefs->direct_retry_cr5_snr_x4))); + strcpy(cr7, StrHelper::ftoa(directRetryCrX4ToDb(prefs->direct_retry_cr7_snr_x4))); strcpy(cr8, StrHelper::ftoa(directRetryCrX4ToDb(prefs->direct_retry_cr8_snr_x4))); - sprintf(reply, "> %s,%s,%s,%s", cr4, cr5, cr8, cr8); + sprintf(reply, "> %s,%s,%s,%s", cr4, cr5, cr7, cr8); } static uint16_t retryPresetStepDefault(uint8_t preset) { @@ -388,35 +412,36 @@ static bool parseDirectRetryCrThresholds(char* value, NodePrefs* prefs) { if (value == NULL || prefs == NULL) { return false; } + if (strcmp(value, "off") == 0) { + setDirectRetryCrOff(prefs); + return true; + } - const char* parts[4]; - int num = mesh::Utils::parseTextParts(value, parts, 4); - if (num != 3 && num != 4) { + const char* parts[5]; + int num = mesh::Utils::parseTextParts(value, parts, 5); + if (num != 4) { return false; } int8_t cr4_snr_x4; int8_t cr5_snr_x4; + int8_t cr7_snr_x4; int8_t cr8_snr_x4; if (!parseDirectRetryCrDb(parts[0], cr4_snr_x4) || !parseDirectRetryCrDb(parts[1], cr5_snr_x4) - || !parseDirectRetryCrDb(parts[num == 4 ? 3 : 2], cr8_snr_x4)) { + || !parseDirectRetryCrDb(parts[2], cr7_snr_x4) + || !parseDirectRetryCrDb(parts[3], cr8_snr_x4)) { return false; } - if (num == 4) { - int8_t repeated_low_snr_x4; - if (!parseDirectRetryCrDb(parts[2], repeated_low_snr_x4) || repeated_low_snr_x4 != cr8_snr_x4) { - return false; - } - } - - if (!directRetryCrThresholdsAreValid(cr4_snr_x4, cr5_snr_x4, cr8_snr_x4)) { + if (directRetryCrThresholdsAreOff(cr4_snr_x4, cr5_snr_x4, cr7_snr_x4, cr8_snr_x4) + || !directRetryCrThresholdsAreValid(cr4_snr_x4, cr5_snr_x4, cr7_snr_x4, cr8_snr_x4)) { return false; } prefs->direct_retry_cr4_snr_x4 = cr4_snr_x4; prefs->direct_retry_cr5_snr_x4 = cr5_snr_x4; + prefs->direct_retry_cr7_snr_x4 = cr7_snr_x4; prefs->direct_retry_cr8_snr_x4 = cr8_snr_x4; return true; } @@ -527,8 +552,10 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { sizeof(_prefs->direct_retry_cr4_snr_x4)); // 660 retry_cr_read += file.read((uint8_t *)&_prefs->direct_retry_cr5_snr_x4, sizeof(_prefs->direct_retry_cr5_snr_x4)); // 661 + retry_cr_read += file.read((uint8_t *)&_prefs->direct_retry_cr7_snr_x4, + sizeof(_prefs->direct_retry_cr7_snr_x4)); // 662 retry_cr_read += file.read((uint8_t *)&_prefs->direct_retry_cr8_snr_x4, - sizeof(_prefs->direct_retry_cr8_snr_x4)); // 662 + sizeof(_prefs->direct_retry_cr8_snr_x4)); // 663 // PowerSaving-only prefs stored radio_fem_rxgain at 291, before direct retry timing existed. if (radio_fem_rxgain_read != sizeof(_prefs->radio_fem_rxgain) && legacy_retry_attempts_read == sizeof(legacy_retry_attempts_or_radio_fem_rxgain) @@ -536,7 +563,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { || _prefs->direct_retry_timing_magic[1] != DIRECT_RETRY_TIMING_MAGIC_1)) { _prefs->radio_fem_rxgain = constrain(legacy_retry_attempts_or_radio_fem_rxgain, 0, 1); } - // next: 663 + // next: 664 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -614,8 +641,14 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->flood_retry_advert_enabled = constrain(_prefs->flood_retry_advert_enabled, 0, 1); } } - if (retry_cr_read != sizeof(_prefs->direct_retry_cr4_snr_x4) + if (retry_cr_read == sizeof(_prefs->direct_retry_cr4_snr_x4) + sizeof(_prefs->direct_retry_cr5_snr_x4) + + sizeof(_prefs->direct_retry_cr7_snr_x4)) { + _prefs->direct_retry_cr8_snr_x4 = _prefs->direct_retry_cr7_snr_x4; + sanitizeDirectRetryCrThresholds(_prefs); + } else if (retry_cr_read != sizeof(_prefs->direct_retry_cr4_snr_x4) + + sizeof(_prefs->direct_retry_cr5_snr_x4) + + sizeof(_prefs->direct_retry_cr7_snr_x4) + sizeof(_prefs->direct_retry_cr8_snr_x4)) { setDirectRetryCrDefaults(_prefs); } else { @@ -704,8 +737,9 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->flood_retry_advert_enabled, sizeof(_prefs->flood_retry_advert_enabled)); // 659 file.write((uint8_t *)&_prefs->direct_retry_cr4_snr_x4, sizeof(_prefs->direct_retry_cr4_snr_x4)); // 660 file.write((uint8_t *)&_prefs->direct_retry_cr5_snr_x4, sizeof(_prefs->direct_retry_cr5_snr_x4)); // 661 - file.write((uint8_t *)&_prefs->direct_retry_cr8_snr_x4, sizeof(_prefs->direct_retry_cr8_snr_x4)); // 662 - // next: 663 + file.write((uint8_t *)&_prefs->direct_retry_cr7_snr_x4, sizeof(_prefs->direct_retry_cr7_snr_x4)); // 662 + file.write((uint8_t *)&_prefs->direct_retry_cr8_snr_x4, sizeof(_prefs->direct_retry_cr8_snr_x4)); // 663 + // next: 664 file.close(); } @@ -1285,7 +1319,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re savePrefs(); formatDirectRetryCrThresholds(_prefs, reply); } else { - strcpy(reply, "Error, expected cr4,cr5,cr8 or cr4,cr5,low,low"); + strcpy(reply, "Error, expected off or cr4,cr5,cr7,cr8"); } } else if (memcmp(config, "owner.info ", 11) == 0) { config += 11; @@ -1919,7 +1953,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep savePrefs(); formatDirectRetryCrThresholds(_prefs, reply); } else { - strcpy(reply, "Error, expected cr4,cr5,cr8 or cr4,cr5,low,low"); + strcpy(reply, "Error, expected off or cr4,cr5,cr7,cr8"); } } else if (memcmp(config, "owner.info ", 11) == 0) { config += 11; diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index ed573649..104a7150 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -65,6 +65,7 @@ #define DIRECT_RETRY_CR4_MIN_SNR_X4_DEFAULT 40 // 10.0 dB and up => CR4 #define DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT 30 // 7.5 dB and up => CR5 +#define DIRECT_RETRY_CR7_MIN_SNR_X4_DEFAULT 10 // 2.5 dB and up => CR7 #define DIRECT_RETRY_CR8_MAX_SNR_X4_DEFAULT 10 // 2.5 dB and down => CR8 #define DIRECT_RETRY_CR_SNR_X4_MIN -128 #define DIRECT_RETRY_CR_SNR_X4_MAX 127 @@ -129,6 +130,7 @@ struct NodePrefs { // persisted to file uint8_t flood_retry_advert_enabled; int8_t direct_retry_cr4_snr_x4; int8_t direct_retry_cr5_snr_x4; + int8_t direct_retry_cr7_snr_x4; int8_t direct_retry_cr8_snr_x4; }; From 9a8609097825980ea74e72409a345f6ea89cef44 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 8 May 2026 17:04:47 -0700 Subject: [PATCH 055/214] Update direct retry CR controls --- docs/cli_commands.md | 8 +-- examples/simple_repeater/MyMesh.cpp | 34 +++++++++--- src/helpers/CommonCLI.cpp | 84 ++++++++++++++++++++--------- src/helpers/CommonCLI.h | 2 + 4 files changed, 92 insertions(+), 36 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index f9dcc16a..ab6af502 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -559,18 +559,18 @@ This document provides an overview of CLI commands that can be sent to MeshCore #### View or change adaptive coding rate for direct retry packets **Usage:** - `get direct.retry.cr` -- `set direct.retry.cr ,,` -- `set direct.retry.cr ,,,` +- `set direct.retry.cr ,,,` +- `set direct.retry.cr off` **Parameters:** - `cr4_min`: SNR in dB where retry packets use `CR4` - `cr5_min`: SNR in dB where retry packets use `CR5` +- `cr7_min`: SNR in dB where retry packets use `CR7` - `cr8_max`: SNR in dB where retry packets use `CR8` -- `low`: optional repeated low boundary; both low values must match **Default:** `10.0,7.5,2.5,2.5` -**Note:** DM retry packets with a recent repeater table entry use that entry's SNR to pick a local transmit coding rate. With the default, SNR `10.0 dB` and up uses `CR4`, SNR `7.5 dB` and up uses `CR5`, SNR `2.5 dB` and down uses `CR8`, and the middle band uses `CR7`. `CR6` is never selected. The shorter form `set direct.retry.cr 10.0,7.5,2.5` is equivalent to `set direct.retry.cr 10.0,7.5,2.5,2.5`. +**Note:** DM retry packets use the next-hop SNR from a recent repeater table entry to pick a local transmit coding rate; if no recent entry is available, retry packets use `CR5`. With the default, SNR `10.0 dB` and up uses `CR4`, SNR `7.5 dB` and up uses `CR5`, SNR `2.5 dB` and down uses `CR8`, and the middle band uses `CR7`. `CR6` is never selected. Use `set direct.retry.cr off` to disable adaptive coding-rate overrides. If adaptive selection chooses `CR4`, retries after the third attempt use `CR5`. --- diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 24e5c81d..89bd6020 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -744,7 +744,8 @@ void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, u time_label = "echo_ms"; } - MESH_DEBUG_PRINTLN("%s direct retry %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%s, next_hop=%s, pkt=%s, tbl=%s, %s=%lu)", + uint8_t log_cr = (packet->tx_cr >= 4 && packet->tx_cr <= 8) ? packet->tx_cr : active_cr; + MESH_DEBUG_PRINTLN("%s direct retry %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%s, next_hop=%s, pkt=%s, tbl=%s, cr=%u, %s=%lu)", getLogDateTime(), event, (unsigned int)retry_attempt, @@ -755,6 +756,7 @@ void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, u next_hop, snr_pkt_text, snr_table_text, + (unsigned int)log_cr, time_label, (unsigned long)delay_millis); @@ -762,7 +764,7 @@ void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, u File f = openAppend(PACKET_LOG_FILE); if (f) { f.print(getLogDateTime()); - f.printf(": DIRECT RETRY %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%s, next_hop=%s, pkt=%s, tbl=%s, %s=%lu)\n", + f.printf(": DIRECT RETRY %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%s, next_hop=%s, pkt=%s, tbl=%s, cr=%u, %s=%lu)\n", event, (unsigned int)retry_attempt, (uint32_t)packet->getPayloadType(), @@ -772,6 +774,7 @@ void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, u next_hop, snr_pkt_text, snr_table_text, + (unsigned int)log_cr, time_label, (unsigned long)delay_millis); f.close(); @@ -847,6 +850,12 @@ bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_ho return true; } uint8_t MyMesh::getDirectRetryCodingRateForSNR(int8_t snr_x4) const { + if (_prefs.direct_retry_cr4_snr_x4 == 0 + && _prefs.direct_retry_cr5_snr_x4 == 0 + && _prefs.direct_retry_cr7_snr_x4 == 0 + && _prefs.direct_retry_cr8_snr_x4 == 0) { + return 0; + } if (snr_x4 >= _prefs.direct_retry_cr4_snr_x4) { return 4; } @@ -856,11 +865,13 @@ uint8_t MyMesh::getDirectRetryCodingRateForSNR(int8_t snr_x4) const { if (snr_x4 <= _prefs.direct_retry_cr8_snr_x4) { return 8; } + if (snr_x4 >= _prefs.direct_retry_cr7_snr_x4) { + return 7; + } return 7; } void MyMesh::configureDirectRetryPacket(mesh::Packet* retry, const mesh::Packet* original, uint8_t retry_attempt) { (void) original; - (void) retry_attempt; if (retry == NULL || !retry->isRouteDirect()) { return; @@ -873,6 +884,7 @@ void MyMesh::configureDirectRetryPacket(mesh::Packet* retry, const mesh::Packet* case PAYLOAD_TYPE_RESPONSE: case PAYLOAD_TYPE_TXT_MSG: case PAYLOAD_TYPE_ANON_REQ: + case PAYLOAD_TYPE_TRACE: case PAYLOAD_TYPE_MULTIPART: break; default: @@ -885,13 +897,20 @@ void MyMesh::configureDirectRetryPacket(mesh::Packet* retry, const mesh::Packet* return; } - const auto* recent = ((const SimpleMeshTables *)getTables())->findRecentRepeaterByHash(prefix, prefix_len); - if (recent == NULL) { + if (_prefs.direct_retry_cr4_snr_x4 == 0 + && _prefs.direct_retry_cr5_snr_x4 == 0 + && _prefs.direct_retry_cr7_snr_x4 == 0 + && _prefs.direct_retry_cr8_snr_x4 == 0) { return; } - uint8_t retry_cr = getDirectRetryCodingRateForSNR(recent->snr_x4); - if (retry_cr >= 4 && retry_cr <= 8 && retry_cr != active_cr) { + const auto* recent = ((const SimpleMeshTables *)getTables())->findRecentRepeaterByHash(prefix, prefix_len); + uint8_t retry_cr = (recent != NULL) ? getDirectRetryCodingRateForSNR(recent->snr_x4) : 5; + if (retry_cr == 4 && retry_attempt > 3) { + retry_cr = 5; + } + + if (retry_cr >= 4 && retry_cr <= 8 && retry_cr != 6 && retry_cr != active_cr) { retry->tx_cr = retry_cr; } } @@ -1294,6 +1313,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.direct_retry_preset = DIRECT_RETRY_PRESET_ROOFTOP; _prefs.direct_retry_cr4_snr_x4 = DIRECT_RETRY_CR4_MIN_SNR_X4_DEFAULT; _prefs.direct_retry_cr5_snr_x4 = DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT; + _prefs.direct_retry_cr7_snr_x4 = DIRECT_RETRY_CR7_MIN_SNR_X4_DEFAULT; _prefs.direct_retry_cr8_snr_x4 = DIRECT_RETRY_CR8_MAX_SNR_X4_DEFAULT; StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); _prefs.node_lat = ADVERT_LAT; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 42b41f29..87126629 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -92,29 +92,53 @@ static float directRetryCrX4ToDb(int8_t snr_x4) { static void setDirectRetryCrDefaults(NodePrefs* prefs) { prefs->direct_retry_cr4_snr_x4 = DIRECT_RETRY_CR4_MIN_SNR_X4_DEFAULT; prefs->direct_retry_cr5_snr_x4 = DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT; + prefs->direct_retry_cr7_snr_x4 = DIRECT_RETRY_CR7_MIN_SNR_X4_DEFAULT; prefs->direct_retry_cr8_snr_x4 = DIRECT_RETRY_CR8_MAX_SNR_X4_DEFAULT; } -static bool directRetryCrThresholdsAreValid(int8_t cr4_snr_x4, int8_t cr5_snr_x4, int8_t cr8_snr_x4) { - return (cr4_snr_x4 != 0 || cr5_snr_x4 != 0 || cr8_snr_x4 != 0) - && cr4_snr_x4 >= cr5_snr_x4 - && cr5_snr_x4 >= cr8_snr_x4; +static void setDirectRetryCrOff(NodePrefs* prefs) { + prefs->direct_retry_cr4_snr_x4 = 0; + prefs->direct_retry_cr5_snr_x4 = 0; + prefs->direct_retry_cr7_snr_x4 = 0; + prefs->direct_retry_cr8_snr_x4 = 0; +} + +static bool directRetryCrThresholdsAreOff(int8_t cr4_snr_x4, int8_t cr5_snr_x4, int8_t cr7_snr_x4, int8_t cr8_snr_x4) { + return cr4_snr_x4 == 0 && cr5_snr_x4 == 0 && cr7_snr_x4 == 0 && cr8_snr_x4 == 0; +} + +static bool directRetryCrThresholdsAreValid(int8_t cr4_snr_x4, int8_t cr5_snr_x4, int8_t cr7_snr_x4, int8_t cr8_snr_x4) { + return directRetryCrThresholdsAreOff(cr4_snr_x4, cr5_snr_x4, cr7_snr_x4, cr8_snr_x4) + || ((cr4_snr_x4 != 0 || cr5_snr_x4 != 0 || cr7_snr_x4 != 0 || cr8_snr_x4 != 0) + && cr4_snr_x4 >= cr5_snr_x4 + && cr5_snr_x4 >= cr7_snr_x4 + && cr7_snr_x4 >= cr8_snr_x4); } static void sanitizeDirectRetryCrThresholds(NodePrefs* prefs) { if (!directRetryCrThresholdsAreValid(prefs->direct_retry_cr4_snr_x4, prefs->direct_retry_cr5_snr_x4, + prefs->direct_retry_cr7_snr_x4, prefs->direct_retry_cr8_snr_x4)) { setDirectRetryCrDefaults(prefs); } } static void formatDirectRetryCrThresholds(const NodePrefs* prefs, char* reply) { - char cr4[12], cr5[12], cr8[12]; + if (directRetryCrThresholdsAreOff(prefs->direct_retry_cr4_snr_x4, + prefs->direct_retry_cr5_snr_x4, + prefs->direct_retry_cr7_snr_x4, + prefs->direct_retry_cr8_snr_x4)) { + strcpy(reply, "> off"); + return; + } + + char cr4[12], cr5[12], cr7[12], cr8[12]; strcpy(cr4, StrHelper::ftoa(directRetryCrX4ToDb(prefs->direct_retry_cr4_snr_x4))); strcpy(cr5, StrHelper::ftoa(directRetryCrX4ToDb(prefs->direct_retry_cr5_snr_x4))); + strcpy(cr7, StrHelper::ftoa(directRetryCrX4ToDb(prefs->direct_retry_cr7_snr_x4))); strcpy(cr8, StrHelper::ftoa(directRetryCrX4ToDb(prefs->direct_retry_cr8_snr_x4))); - sprintf(reply, "> %s,%s,%s,%s", cr4, cr5, cr8, cr8); + sprintf(reply, "> %s,%s,%s,%s", cr4, cr5, cr7, cr8); } static uint16_t directRetryPresetStepDefault(uint8_t preset) { @@ -198,35 +222,36 @@ static bool parseDirectRetryCrThresholds(char* value, NodePrefs* prefs) { if (value == NULL || prefs == NULL) { return false; } + if (strcmp(value, "off") == 0) { + setDirectRetryCrOff(prefs); + return true; + } - const char* parts[4]; - int num = mesh::Utils::parseTextParts(value, parts, 4); - if (num != 3 && num != 4) { + const char* parts[5]; + int num = mesh::Utils::parseTextParts(value, parts, 5); + if (num != 4) { return false; } int8_t cr4_snr_x4; int8_t cr5_snr_x4; + int8_t cr7_snr_x4; int8_t cr8_snr_x4; if (!parseDirectRetryCrDb(parts[0], cr4_snr_x4) || !parseDirectRetryCrDb(parts[1], cr5_snr_x4) - || !parseDirectRetryCrDb(parts[num == 4 ? 3 : 2], cr8_snr_x4)) { + || !parseDirectRetryCrDb(parts[2], cr7_snr_x4) + || !parseDirectRetryCrDb(parts[3], cr8_snr_x4)) { return false; } - if (num == 4) { - int8_t repeated_low_snr_x4; - if (!parseDirectRetryCrDb(parts[2], repeated_low_snr_x4) || repeated_low_snr_x4 != cr8_snr_x4) { - return false; - } - } - - if (!directRetryCrThresholdsAreValid(cr4_snr_x4, cr5_snr_x4, cr8_snr_x4)) { + if (directRetryCrThresholdsAreOff(cr4_snr_x4, cr5_snr_x4, cr7_snr_x4, cr8_snr_x4) + || !directRetryCrThresholdsAreValid(cr4_snr_x4, cr5_snr_x4, cr7_snr_x4, cr8_snr_x4)) { return false; } prefs->direct_retry_cr4_snr_x4 = cr4_snr_x4; prefs->direct_retry_cr5_snr_x4 = cr5_snr_x4; + prefs->direct_retry_cr7_snr_x4 = cr7_snr_x4; prefs->direct_retry_cr8_snr_x4 = cr8_snr_x4; return true; } @@ -323,8 +348,10 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { sizeof(_prefs->direct_retry_cr4_snr_x4)); // 300 retry_cr_read += file.read((uint8_t *)&_prefs->direct_retry_cr5_snr_x4, sizeof(_prefs->direct_retry_cr5_snr_x4)); // 301 + retry_cr_read += file.read((uint8_t *)&_prefs->direct_retry_cr7_snr_x4, + sizeof(_prefs->direct_retry_cr7_snr_x4)); // 302 retry_cr_read += file.read((uint8_t *)&_prefs->direct_retry_cr8_snr_x4, - sizeof(_prefs->direct_retry_cr8_snr_x4)); // 302 + sizeof(_prefs->direct_retry_cr8_snr_x4)); // 303 // PowerSaving-only prefs stored radio_fem_rxgain at 291, before direct retry timing existed. if (radio_fem_rxgain_read != sizeof(_prefs->radio_fem_rxgain) && legacy_retry_attempts_read == sizeof(legacy_retry_attempts_or_radio_fem_rxgain) @@ -332,7 +359,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { || _prefs->direct_retry_timing_magic[1] != DIRECT_RETRY_TIMING_MAGIC_1)) { _prefs->radio_fem_rxgain = constrain(legacy_retry_attempts_or_radio_fem_rxgain, 0, 1); } - // next: 303 + // next: 304 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -386,8 +413,14 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { } else { _prefs->direct_retry_step_ms = constrain(_prefs->direct_retry_step_ms, DIRECT_RETRY_STEP_MS_MIN, DIRECT_RETRY_STEP_MS_MAX); } - if (retry_cr_read != sizeof(_prefs->direct_retry_cr4_snr_x4) + if (retry_cr_read == sizeof(_prefs->direct_retry_cr4_snr_x4) + sizeof(_prefs->direct_retry_cr5_snr_x4) + + sizeof(_prefs->direct_retry_cr7_snr_x4)) { + _prefs->direct_retry_cr8_snr_x4 = _prefs->direct_retry_cr7_snr_x4; + sanitizeDirectRetryCrThresholds(_prefs); + } else if (retry_cr_read != sizeof(_prefs->direct_retry_cr4_snr_x4) + + sizeof(_prefs->direct_retry_cr5_snr_x4) + + sizeof(_prefs->direct_retry_cr7_snr_x4) + sizeof(_prefs->direct_retry_cr8_snr_x4)) { setDirectRetryCrDefaults(_prefs); } else { @@ -467,8 +500,9 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->direct_retry_step_ms, sizeof(_prefs->direct_retry_step_ms)); // 298 file.write((uint8_t *)&_prefs->direct_retry_cr4_snr_x4, sizeof(_prefs->direct_retry_cr4_snr_x4)); // 300 file.write((uint8_t *)&_prefs->direct_retry_cr5_snr_x4, sizeof(_prefs->direct_retry_cr5_snr_x4)); // 301 - file.write((uint8_t *)&_prefs->direct_retry_cr8_snr_x4, sizeof(_prefs->direct_retry_cr8_snr_x4)); // 302 - // next: 303 + file.write((uint8_t *)&_prefs->direct_retry_cr7_snr_x4, sizeof(_prefs->direct_retry_cr7_snr_x4)); // 302 + file.write((uint8_t *)&_prefs->direct_retry_cr8_snr_x4, sizeof(_prefs->direct_retry_cr8_snr_x4)); // 303 + // next: 304 file.close(); } @@ -954,7 +988,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re savePrefs(); formatDirectRetryCrThresholds(_prefs, reply); } else { - strcpy(reply, "Error, expected cr4,cr5,cr8 or cr4,cr5,low,low"); + strcpy(reply, "Error, expected off or cr4,cr5,cr7,cr8"); } } else if (memcmp(config, "owner.info ", 11) == 0) { config += 11; @@ -1516,7 +1550,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep savePrefs(); formatDirectRetryCrThresholds(_prefs, reply); } else { - strcpy(reply, "Error, expected cr4,cr5,cr8 or cr4,cr5,low,low"); + strcpy(reply, "Error, expected off or cr4,cr5,cr7,cr8"); } } else if (memcmp(config, "owner.info ", 11) == 0) { config += 11; diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 43e8e6d7..92401b38 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -40,6 +40,7 @@ #define DIRECT_RETRY_CR4_MIN_SNR_X4_DEFAULT 40 // 10.0 dB and up => CR4 #define DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT 30 // 7.5 dB and up => CR5 +#define DIRECT_RETRY_CR7_MIN_SNR_X4_DEFAULT 10 // 2.5 dB and up => CR7 #define DIRECT_RETRY_CR8_MAX_SNR_X4_DEFAULT 10 // 2.5 dB and down => CR8 #define DIRECT_RETRY_CR_SNR_X4_MIN -128 #define DIRECT_RETRY_CR_SNR_X4_MAX 127 @@ -96,6 +97,7 @@ struct NodePrefs { // persisted to file uint16_t direct_retry_step_ms; int8_t direct_retry_cr4_snr_x4; int8_t direct_retry_cr5_snr_x4; + int8_t direct_retry_cr7_snr_x4; int8_t direct_retry_cr8_snr_x4; }; From 8abf4a92ff8aa9e17b9284744807789ffe26833c Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Sun, 10 May 2026 22:09:56 +0700 Subject: [PATCH 056/214] Added RADIO_FEM_RXGAIN in build option for T096 --- variants/heltec_t096/T096Board.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/variants/heltec_t096/T096Board.cpp b/variants/heltec_t096/T096Board.cpp index 54425145..c7f69722 100644 --- a/variants/heltec_t096/T096Board.cpp +++ b/variants/heltec_t096/T096Board.cpp @@ -126,6 +126,10 @@ const char* T096Board::getManufacturerName() const { } bool T096Board::setLoRaFemLnaEnabled(bool enable) { +#if defined(RADIO_FEM_RXGAIN) && (RADIO_FEM_RXGAIN == 0) + enable = false; +#endif + if (!loRaFEMControl.isLnaCanControl()) { return false; } From 09a27a259177c76e62f128f10ca96108c7442a0a Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Mon, 11 May 2026 17:24:09 -0600 Subject: [PATCH 057/214] fix(mesh): widen TRACE offset to uint16 to avoid narrowing --- src/Mesh.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 57fee140..0c96e14d 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -50,7 +50,9 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { uint8_t path_sz = flags & 0x03; // NEW v1.11+: lower 2 bits is path hash size uint8_t len = pkt->payload_len - i; - uint8_t offset = pkt->path_len << path_sz; + // path_len*entry_size can exceed 255 (path_len up to 63, entry_size up to 8); + // a uint8_t offset would wrap and steer the isHashMatch() read to the wrong place. + uint16_t offset = (uint16_t)pkt->path_len << path_sz; if (offset >= len) { // TRACE has reached end of given path onTraceRecv(pkt, trace_tag, auth_code, flags, pkt->path, &pkt->payload[i], len); } else if (self_id.isHashMatch(&pkt->payload[i + offset], 1 << path_sz) && allowPacketForward(pkt) && !_tables->hasSeen(pkt)) { From 3a546a6395c440a2c463283ef1b3e7d178e81836 Mon Sep 17 00:00:00 2001 From: Quency-D Date: Tue, 12 May 2026 14:53:17 +0800 Subject: [PATCH 058/214] add heltec tower v2 --- boards/heltec_tower_v2.json | 61 +++++++++++ .../heltec_tower_v2/HeltecTowerV2Board.cpp | 102 +++++++++++++++++ variants/heltec_tower_v2/HeltecTowerV2Board.h | 23 ++++ variants/heltec_tower_v2/platformio.ini | 103 ++++++++++++++++++ variants/heltec_tower_v2/target.cpp | 31 ++++++ variants/heltec_tower_v2/target.h | 28 +++++ variants/heltec_tower_v2/variant.cpp | 25 +++++ variants/heltec_tower_v2/variant.h | 101 +++++++++++++++++ 8 files changed, 474 insertions(+) create mode 100644 boards/heltec_tower_v2.json create mode 100644 variants/heltec_tower_v2/HeltecTowerV2Board.cpp create mode 100644 variants/heltec_tower_v2/HeltecTowerV2Board.h create mode 100644 variants/heltec_tower_v2/platformio.ini create mode 100644 variants/heltec_tower_v2/target.cpp create mode 100644 variants/heltec_tower_v2/target.h create mode 100644 variants/heltec_tower_v2/variant.cpp create mode 100644 variants/heltec_tower_v2/variant.h diff --git a/boards/heltec_tower_v2.json b/boards/heltec_tower_v2.json new file mode 100644 index 00000000..9f9b6ab5 --- /dev/null +++ b/boards/heltec_tower_v2.json @@ -0,0 +1,61 @@ +{ + "build": { + "arduino": { + "ldscript": "nrf52840_s140_v6.ld" + }, + "core": "nRF5", + "cpu": "cortex-m4", + "extra_flags": "-DNRF52840_XXAA", + "f_cpu": "64000000L", + "hwids": [ + ["0x239A","0x4405"], + ["0x239A","0x0029"], + ["0x239A","0x002A"], + ["0x239A","0x0071"] + ], + "usb_product": "HT-n5262", + "mcu": "nrf52840", + "variant": "heltec_tower_v2", + "bsp": { + "name": "adafruit" + }, + "softdevice": { + "sd_flags": "-DS140", + "sd_name": "s140", + "sd_version": "6.1.1", + "sd_fwid": "0x00B6" + }, + "bootloader": { + "settings_addr": "0xFF000" + } + }, + "connectivity": [ + "bluetooth" + ], + "debug": { + "jlink_device": "nRF52840_xxAA", + "svd_path": "nrf52840.svd", + "openocd_target": "nrf52.cfg" + }, + "frameworks": [ + "arduino" + ], + "name": "Heltec Tower V2 Board", + "upload": { + "maximum_ram_size": 235520, + "maximum_size": 815104, + "speed": 115200, + "protocol": "nrfutil", + "protocols": [ + "jlink", + "nrfjprog", + "nrfutil", + "stlink" + ], + "use_1200bps_touch": true, + "require_upload_port": true, + "wait_for_upload_port": true + }, + "url": "https://heltec.org/", + "vendor": "Heltec" +} diff --git a/variants/heltec_tower_v2/HeltecTowerV2Board.cpp b/variants/heltec_tower_v2/HeltecTowerV2Board.cpp new file mode 100644 index 00000000..2fd7a618 --- /dev/null +++ b/variants/heltec_tower_v2/HeltecTowerV2Board.cpp @@ -0,0 +1,102 @@ +#include "HeltecTowerV2Board.h" + +#include +#include + +extern void variant_shutdown(); + +#ifdef NRF52_POWER_MANAGEMENT +const PowerMgtConfig power_config = { + .lpcomp_ain_channel = PWRMGT_LPCOMP_AIN, + .lpcomp_refsel = PWRMGT_LPCOMP_REFSEL, + .voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK +}; + +void HeltecTowerV2Board::initiateShutdown(uint8_t reason) { + pinMode(PIN_GPS_EN, OUTPUT); + digitalWrite(PIN_GPS_EN, !PIN_GPS_EN_ACTIVE); + pinMode(PIN_GPS_STANDBY, OUTPUT); + digitalWrite(PIN_GPS_STANDBY, LOW); + pinMode(PIN_GPS_RESET, OUTPUT); + digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); + + bool enable_lpcomp = (reason == SHUTDOWN_REASON_LOW_VOLTAGE || + reason == SHUTDOWN_REASON_BOOT_PROTECT); + pinMode(PIN_BAT_CTL, OUTPUT); + digitalWrite(PIN_BAT_CTL, enable_lpcomp ? HIGH : LOW); + + if (enable_lpcomp) { + configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel); + } + + variant_shutdown(); + enterSystemOff(reason); +} +#endif + +void HeltecTowerV2Board::begin() { + NRF52Board::begin(); + +#ifdef P_LORA_TX_LED + pinMode(P_LORA_TX_LED, OUTPUT); + digitalWrite(P_LORA_TX_LED, !LED_STATE_ON); +#endif + + pinMode(PIN_BAT_CTL, OUTPUT); + digitalWrite(PIN_BAT_CTL, LOW); + +#ifdef NRF52_POWER_MANAGEMENT + checkBootVoltage(&power_config); +#endif + + Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL); + Wire.begin(); + + pinMode(PIN_GPS_EN, OUTPUT); + digitalWrite(PIN_GPS_EN, !PIN_GPS_EN_ACTIVE); + pinMode(PIN_GPS_RESET, OUTPUT); + digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); + pinMode(PIN_GPS_STANDBY, OUTPUT); + digitalWrite(PIN_GPS_STANDBY, HIGH); +} + +#ifdef P_LORA_TX_LED +void HeltecTowerV2Board::onBeforeTransmit() { + digitalWrite(P_LORA_TX_LED, LED_STATE_ON); +} + +void HeltecTowerV2Board::onAfterTransmit() { + digitalWrite(P_LORA_TX_LED, !LED_STATE_ON); +} +#endif + +uint16_t HeltecTowerV2Board::getBattMilliVolts() { + analogReadResolution(12); + analogReference(VBAT_AR_INTERNAL); + pinMode(PIN_VBAT_READ, INPUT); + pinMode(PIN_BAT_CTL, OUTPUT); + digitalWrite(PIN_BAT_CTL, HIGH); + + delay(10); + int adcvalue = analogRead(PIN_VBAT_READ); + digitalWrite(PIN_BAT_CTL, LOW); + + return (uint16_t)((float)adcvalue * MV_LSB * ADC_MULTIPLIER); +} + +const char* HeltecTowerV2Board::getManufacturerName() const { + return "Heltec Tower V2"; +} + +void HeltecTowerV2Board::powerOff() { + pinMode(PIN_GPS_EN, OUTPUT); + digitalWrite(PIN_GPS_EN, !PIN_GPS_EN_ACTIVE); + pinMode(PIN_GPS_STANDBY, OUTPUT); + digitalWrite(PIN_GPS_STANDBY, LOW); + pinMode(PIN_GPS_RESET, OUTPUT); + digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); + pinMode(PIN_BAT_CTL, OUTPUT); + digitalWrite(PIN_BAT_CTL, LOW); + variant_shutdown(); + sd_power_system_off(); +} diff --git a/variants/heltec_tower_v2/HeltecTowerV2Board.h b/variants/heltec_tower_v2/HeltecTowerV2Board.h new file mode 100644 index 00000000..817d3c1a --- /dev/null +++ b/variants/heltec_tower_v2/HeltecTowerV2Board.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +class HeltecTowerV2Board : public NRF52BoardDCDC { +protected: +#ifdef NRF52_POWER_MANAGEMENT + void initiateShutdown(uint8_t reason) override; +#endif + +public: + HeltecTowerV2Board() : NRF52Board("TOWER_V2_OTA") {} + void begin(); +#ifdef P_LORA_TX_LED + void onBeforeTransmit() override; + void onAfterTransmit() override; +#endif + uint16_t getBattMilliVolts() override; + const char* getManufacturerName() const override; + void powerOff() override; +}; diff --git a/variants/heltec_tower_v2/platformio.ini b/variants/heltec_tower_v2/platformio.ini new file mode 100644 index 00000000..2fac9fbc --- /dev/null +++ b/variants/heltec_tower_v2/platformio.ini @@ -0,0 +1,103 @@ +[Heltec_tower_v2] +extends = nrf52_base +board = heltec_tower_v2 +board_build.ldscript = boards/nrf52840_s140_v6.ld +build_flags = ${nrf52_base.build_flags} + -D ENV_INCLUDE_GPS=1 + -I lib/nrf52/s140_nrf52_6.1.1_API/include + -I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52 + -I variants/heltec_tower_v2 + -D HELTEC_TOWER_V2 + -D NRF52_POWER_MANAGEMENT + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D SX126X_CURRENT_LIMIT=140 + -D SX126X_RX_BOOSTED_GAIN=1 +build_src_filter = ${nrf52_base.build_src_filter} + + + + + +<../variants/heltec_tower_v2> +lib_deps = + ${nrf52_base.lib_deps} + stevemarple/MicroNMEA @ ^2.0.6 +debug_tool = jlink +upload_protocol = nrfutil + +[env:Heltec_tower_v2_repeater] +extends = Heltec_tower_v2 +build_src_filter = ${Heltec_tower_v2.build_src_filter} + +<../examples/simple_repeater> +build_flags = + ${Heltec_tower_v2.build_flags} + -D ADVERT_NAME='"Heltec_Tower_V2 Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + +[env:Heltec_tower_v2_room_server] +extends = Heltec_tower_v2 +build_src_filter = ${Heltec_tower_v2.build_src_filter} + +<../examples/simple_room_server> +build_flags = + ${Heltec_tower_v2.build_flags} + -D ADVERT_NAME='"Heltec_Tower_V2 Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + +[env:Heltec_tower_v2_companion_radio_ble] +extends = Heltec_tower_v2 +board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld +board_upload.maximum_size = 712704 +build_flags = + ${Heltec_tower_v2.build_flags} + -I examples/companion_radio/ui-new + -D DISPLAY_CLASS=NullDisplayDriver + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 +; -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_tower_v2.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_tower_v2.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:Heltec_tower_v2_companion_radio_usb] +extends = Heltec_tower_v2 +board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld +board_upload.maximum_size = 712704 +build_flags = + ${Heltec_tower_v2.build_flags} + -I examples/companion_radio/ui-new + -D DISPLAY_CLASS=NullDisplayDriver + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 +; -D BLE_PIN_CODE=123456 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${Heltec_tower_v2.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_tower_v2.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:Heltec_tower_v2_kiss_modem] +extends = Heltec_tower_v2 +build_src_filter = ${Heltec_tower_v2.build_src_filter} + +<../examples/kiss_modem/> diff --git a/variants/heltec_tower_v2/target.cpp b/variants/heltec_tower_v2/target.cpp new file mode 100644 index 00000000..ad457354 --- /dev/null +++ b/variants/heltec_tower_v2/target.cpp @@ -0,0 +1,31 @@ +#include "target.h" + +#include +#include +#include + +HeltecTowerV2Board board; + +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); + +WRAPPER_CLASS radio_driver(radio, board); + +VolatileRTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); +MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); +EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); + +#ifdef DISPLAY_CLASS +DISPLAY_CLASS display; +MomentaryButton user_btn(PIN_USER_BTN, 1000, true); +#endif + +bool radio_init() { + rtc_clock.begin(Wire); + return radio.std_init(&SPI); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); +} diff --git a/variants/heltec_tower_v2/target.h b/variants/heltec_tower_v2/target.h new file mode 100644 index 00000000..03719246 --- /dev/null +++ b/variants/heltec_tower_v2/target.h @@ -0,0 +1,28 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include +#include + +#ifdef DISPLAY_CLASS +#include +#include "helpers/ui/NullDisplayDriver.h" +#endif + +extern HeltecTowerV2Board board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern EnvironmentSensorManager sensors; + +#ifdef DISPLAY_CLASS +extern DISPLAY_CLASS display; +extern MomentaryButton user_btn; +#endif + +bool radio_init(); +mesh::LocalIdentity radio_new_identity(); diff --git a/variants/heltec_tower_v2/variant.cpp b/variants/heltec_tower_v2/variant.cpp new file mode 100644 index 00000000..699c6e4a --- /dev/null +++ b/variants/heltec_tower_v2/variant.cpp @@ -0,0 +1,25 @@ +#include "variant.h" + +#include "Arduino.h" +#include "nrf.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[] = { + 0xff, 0xff, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47 +}; + +void initVariant() +{ + +} + +void variant_shutdown() +{ + nrf_gpio_cfg_default(PIN_GPS_PPS); + detachInterrupt(PIN_GPS_PPS); + detachInterrupt(PIN_BUTTON1); +} diff --git a/variants/heltec_tower_v2/variant.h b/variants/heltec_tower_v2/variant.h new file mode 100644 index 00000000..d3f2599a --- /dev/null +++ b/variants/heltec_tower_v2/variant.h @@ -0,0 +1,101 @@ +#pragma once + +#include "WVariant.h" + +#define USE_LFXO +#define VARIANT_MCK (64000000ul) + +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (1) +#define NUM_ANALOG_OUTPUTS (0) + +#define WIRE_INTERFACES_COUNT (1) +#define PIN_WIRE_SDA (0 + 11) +#define PIN_WIRE_SCL (0 + 12) +#define PIN_BOARD_SDA PIN_WIRE_SDA +#define PIN_BOARD_SCL PIN_WIRE_SCL + +#define SPI_INTERFACES_COUNT (1) +#define PIN_SPI_MISO (0 + 23) +#define PIN_SPI_MOSI (0 + 22) +#define PIN_SPI_SCK (0 + 19) +#define PIN_SPI_NSS LORA_CS + +#define LED_BUILTIN (32 + 15) +#define PIN_LED LED_BUILTIN +#define LED_RED (-1) +#define LED_GREEN (-1) +#define LED_BLUE (-1) +#define LED_PIN (-1) +#define P_LORA_TX_LED LED_BUILTIN +#define LED_STATE_ON LOW + +#define PIN_BUTTON1 (32 + 10) +#define BUTTON_PIN PIN_BUTTON1 +#define PIN_USER_BTN BUTTON_PIN + +#define USE_SX1262 +#define SX126X_CS (0 + 24) +#define LORA_CS SX126X_CS +#define SX126X_DIO1 (0 + 20) +#define SX126X_BUSY (0 + 17) +#define SX126X_RESET (0 + 25) +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +#define P_LORA_NSS LORA_CS +#define P_LORA_DIO_1 SX126X_DIO1 +#define P_LORA_BUSY SX126X_BUSY +#define P_LORA_RESET SX126X_RESET +#define P_LORA_MISO PIN_SPI_MISO +#define P_LORA_MOSI PIN_SPI_MOSI +#define P_LORA_SCLK PIN_SPI_SCK + +#define GPS_L76K +#define GPS_RESET_MODE LOW +#define PIN_GPS_RESET (32 + 6) +#define PIN_GPS_RESET_ACTIVE GPS_RESET_MODE +#define PIN_GPS_EN (0 + 7) +#define PIN_GPS_EN_ACTIVE LOW +#define GPS_EN_ACTIVE PIN_GPS_EN_ACTIVE +#define PIN_GPS_STANDBY (32 + 2) +#define PIN_GPS_PPS (32 + 4) +#define GPS_BAUD_RATE 9600 + +// Upstream names are from the GPS perspective. MeshCore's PIN_GPS_TX is the +// CPU RX pin because EnvironmentSensorManager passes it as Serial1 RX. +#define GPS_TX_PIN (32 + 7) +#define GPS_RX_PIN (32 + 5) +#define PIN_GPS_TX GPS_RX_PIN +#define PIN_GPS_RX GPS_TX_PIN + +#define PIN_SERIAL1_RX PIN_GPS_TX +#define PIN_SERIAL1_TX PIN_GPS_RX +#define PIN_SERIAL2_RX (-1) +#define PIN_SERIAL2_TX (-1) + +#define HAS_HARDWARE_WATCHDOG +#define HARDWARE_WATCHDOG_DONE (0 + 9) +#define HARDWARE_WATCHDOG_WAKE (0 + 10) +#define HARDWARE_WATCHDOG_TIMEOUT_MS (8 * 60 * 1000) + +#define SERIAL_PRINT_PORT 0 + +#define PIN_BAT_CTL (0 + 21) +#define ADC_CTRL PIN_BAT_CTL +#define ADC_CTRL_ENABLED HIGH +#define BATTERY_PIN (0 + 4) +#define PIN_VBAT_READ BATTERY_PIN +#define ADC_RESOLUTION 14 +#define BATTERY_SENSE_RESOLUTION_BITS 12 +#define BATTERY_SENSE_RESOLUTION 4096.0 +#define AREF_VOLTAGE 3.0 +#define VBAT_AR_INTERNAL AR_INTERNAL_3_0 +#define ADC_MULTIPLIER (4.916F) +#define MV_LSB (3000.0F / 4096.0F) + +#define NRF52_POWER_MANAGEMENT +#define PWRMGT_VOLTAGE_BOOTLOCK 3100 +#define PWRMGT_LPCOMP_AIN 2 +#define PWRMGT_LPCOMP_REFSEL 1 \ No newline at end of file From 16cb6d518f7639d755cf4aea22d7f741b25ff81f Mon Sep 17 00:00:00 2001 From: AI7NC <77077873+AI7NC@users.noreply.github.com> Date: Tue, 12 May 2026 12:42:33 -0700 Subject: [PATCH 059/214] Update cli_commands.md to include 'ver' Include the 'ver' command for retrieving the firmware version --- docs/cli_commands.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index fb698228..99dced36 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -405,6 +405,11 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### View this node's firmware version +**Usage:** `ver` + +--- + #### View this node's configured role **Usage:** `get role` From 69a76b0cdae6f44fdaa4f72d39ecb3bffd15ca60 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Fri, 15 May 2026 21:07:10 +0700 Subject: [PATCH 060/214] Fixed boot loop due to flash mode for Xiao C6 --- variants/xiao_c6/platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/xiao_c6/platformio.ini b/variants/xiao_c6/platformio.ini index 717be7b9..2aab6b7e 100644 --- a/variants/xiao_c6/platformio.ini +++ b/variants/xiao_c6/platformio.ini @@ -1,6 +1,7 @@ [Xiao_C6] extends = esp32c6_base board = esp32-c6-devkitm-1 +board_build.flash_mode = dio board_build.partitions = min_spiffs.csv ; get around 4mb flash limit build_flags = ${esp32c6_base.build_flags} From d4c99dec65331b95ee3fe628112153cf1af75701 Mon Sep 17 00:00:00 2001 From: Rastislav Vysoky Date: Fri, 15 May 2026 16:42:29 +0200 Subject: [PATCH 061/214] Change MeshCore intro video link to The Comms Channel's MC intro playlist --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f8b9e5e0..d5f2a16f 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ MeshCore provides the ability to create wireless mesh networks, similar to Mesht ## 🚀 How to Get Started -- Watch the [MeshCore Intro Video](https://www.youtube.com/watch?v=t1qne8uJBAc) by Andy Kirby. +- Watch the [MeshCore QuickStart Playlist](https://www.youtube.com/watch?v=iaFltojJrAc&list=PLshzThxhw4O4WU_iZo3NmNZOv6KMrUuF9) by The Comms Channel - Watch the [MeshCore Technical Presentation](https://www.youtube.com/watch?v=OwmkVkZQTf4) by Liam Cottle. - Read through our [Frequently Asked Questions](./docs/faq.md) and [Documentation](https://docs.meshcore.io). - Flash the MeshCore firmware on a supported device. From f29cae602f0f2e1102fb085023202ad6861620e1 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Sat, 16 May 2026 09:30:49 +0700 Subject: [PATCH 062/214] Added flash_mode=dio to avoid boot loop when flashing using merge.bin --- variants/xiao_c6/platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/xiao_c6/platformio.ini b/variants/xiao_c6/platformio.ini index 9f504b8e..0d8c2a79 100644 --- a/variants/xiao_c6/platformio.ini +++ b/variants/xiao_c6/platformio.ini @@ -1,6 +1,7 @@ [Xiao_C6] extends = esp32c6_base board = esp32-c6-devkitm-1 +board_build.flash_mode = dio board_build.partitions = min_spiffs.csv ; get around 4mb flash limit build_flags = ${esp32c6_base.build_flags} From 19703ddbdab36239741b3ba60ad216a964ae1b86 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Thu, 21 May 2026 10:24:00 +0700 Subject: [PATCH 063/214] Added minor changes from PR1687 --- examples/simple_repeater/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 17a3c192..c9b9aa91 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -152,10 +152,10 @@ void loop() { if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) { #if defined(NRF52_PLATFORM) - board.sleep(1800); // nrf ignores seconds param, sleeps whenever possible + board.sleep(0); // nrf ignores seconds param, sleeps whenever possible #else if (the_mesh.millisHasNowPassed(POWERSAVING_FIRSTSLEEP_SECS * 1000)) { // To check if it is time to sleep - board.sleep(30); // Sleep. Wake up after some seconds or when receiving a LoRa packet + board.sleep(30); // Sleep. Wake up after a while or when receiving a LoRa packet } #endif } From e59a3f5c21e268a3ec309c3bf1d133059e7502da Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Thu, 21 May 2026 10:37:09 +0700 Subject: [PATCH 064/214] Minor sleep settings for room servers --- examples/simple_room_server/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 55179809..637a694a 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -119,10 +119,10 @@ void loop() { if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) { #if defined(NRF52_PLATFORM) - board.sleep(1800); // nrf ignores seconds param, sleeps whenever possible + board.sleep(0); // nrf ignores seconds param, sleeps whenever possible #else if (the_mesh.millisHasNowPassed(POWERSAVING_FIRSTSLEEP_SECS * 1000)) { // To check if it is time to sleep - board.sleep(1800); // Sleep. Wake up after 30 minutes or when receiving a LoRa packet + board.sleep(30); // Sleep. Wake up after a while or when receiving a LoRa packet } #endif } From c15fdfeaf9a297446cdf53f6b6eabb05f2bc31d7 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Thu, 21 May 2026 18:54:56 +0700 Subject: [PATCH 065/214] Fixed hasPendingWork for BLE companions. --- examples/companion_radio/MyMesh.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 8a6f63c5..1b29daa0 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -2223,8 +2223,5 @@ bool MyMesh::advert() { // To check if there is pending work bool MyMesh::hasPendingWork() const { -#if defined(WITH_BRIDGE) - if (bridge.isRunning()) return true; // bridge needs WiFi radio, can't sleep -#endif return _mgr->getOutboundTotal() > 0; } From 49b6b632e8b9fa0c5916454f16663ac6199364ce Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Fri, 22 May 2026 23:11:05 +0700 Subject: [PATCH 066/214] Supported PowerSaving for USB companion --- src/helpers/ArduinoSerialInterface.cpp | 4 ++++ src/helpers/ArduinoSerialInterface.h | 1 + 2 files changed, 5 insertions(+) diff --git a/src/helpers/ArduinoSerialInterface.cpp b/src/helpers/ArduinoSerialInterface.cpp index a01fa586..6b443974 100644 --- a/src/helpers/ArduinoSerialInterface.cpp +++ b/src/helpers/ArduinoSerialInterface.cpp @@ -17,6 +17,10 @@ bool ArduinoSerialInterface::isConnected() const { return true; // no way of knowing, so assume yes } +bool ArduinoSerialInterface::isReadBusy() const { + return false; +} + bool ArduinoSerialInterface::isWriteBusy() const { return false; } diff --git a/src/helpers/ArduinoSerialInterface.h b/src/helpers/ArduinoSerialInterface.h index c4086353..3eed7671 100644 --- a/src/helpers/ArduinoSerialInterface.h +++ b/src/helpers/ArduinoSerialInterface.h @@ -28,6 +28,7 @@ public: bool isConnected() const override; + bool isReadBusy() const override; bool isWriteBusy() const override; size_t writeFrame(const uint8_t src[], size_t len) override; size_t checkRecvFrame(uint8_t dest[]) override; From 475b3ac20bdb381752524d05bfe63bb48323177a Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Fri, 22 May 2026 23:12:22 +0700 Subject: [PATCH 067/214] Supported PowerSaving for 3 Tbeam boards --- build-iotthinks.sh | 13 ++++++--- variants/lilygo_tbeam_SX1262/platformio.ini | 27 +++++++++++++++++++ variants/lilygo_tbeam_SX1276/platformio.ini | 25 +++++++++++++++++ .../platformio.ini | 23 ++++++++++++++++ 4 files changed, 85 insertions(+), 3 deletions(-) diff --git a/build-iotthinks.sh b/build-iotthinks.sh index 9bff2eff..502331d5 100644 --- a/build-iotthinks.sh +++ b/build-iotthinks.sh @@ -1,5 +1,5 @@ # sh ./build-repeaters-iotthinks.sh -export FIRMWARE_VERSION="PowerSaving15.0.2" +export FIRMWARE_VERSION="PowerSaving15.0.3" ############# Repeaters ############# # Commonly-used boards @@ -88,7 +88,7 @@ Heltec_t096_companion_radio_ble \ Heltec_t096_companion_radio_ble_femoff ############# Companions BLE PS ############# -# ESP32 - 13 boards +# ESP32 - 16 boards sh build.sh build-firmware \ Heltec_v3_companion_radio_ble_ps \ heltec_v4_companion_radio_ble_ps \ @@ -103,4 +103,11 @@ Heltec_Wireless_Tracker_companion_radio_ble_ps \ heltec_tracker_v2_companion_radio_ble_ps \ Heltec_Wireless_Paper_companion_radio_ble_ps \ LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps \ -Heltec_ct62_companion_radio_ble_ps +Heltec_ct62_companion_radio_ble_ps \ +T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps \ +Tbeam_SX1262_companion_radio_ble_ps \ +Tbeam_SX1276_companion_radio_ble_ps + +############# Companions USB ############# +sh build.sh build-firmware \ +Heltec_t096_companion_radio_usb diff --git a/variants/lilygo_tbeam_SX1262/platformio.ini b/variants/lilygo_tbeam_SX1262/platformio.ini index 1585dd74..cfd8557f 100644 --- a/variants/lilygo_tbeam_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_SX1262/platformio.ini @@ -65,6 +65,33 @@ lib_deps = ${LilyGo_TBeam_SX1262.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Tbeam_SX1262_companion_radio_ble_ps] +extends = LilyGo_TBeam_SX1262 +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +board_build.upload.maximum_ram_size=2000000 +build_flags = + ${LilyGo_TBeam_SX1262.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=100 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 + -D OFFLINE_QUEUE_SIZE=256 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +; -D RADIOLIB_DEBUG_BASIC=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TBeam_SX1262.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${LilyGo_TBeam_SX1262.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:Tbeam_SX1262_repeater] extends = LilyGo_TBeam_SX1262 build_flags = diff --git a/variants/lilygo_tbeam_SX1276/platformio.ini b/variants/lilygo_tbeam_SX1276/platformio.ini index 7482ef7b..a134335c 100644 --- a/variants/lilygo_tbeam_SX1276/platformio.ini +++ b/variants/lilygo_tbeam_SX1276/platformio.ini @@ -61,6 +61,31 @@ lib_deps = ${LilyGo_TBeam_SX1276.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Tbeam_SX1276_companion_radio_ble_ps] +extends = LilyGo_TBeam_SX1276 +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +board_build.upload.maximum_ram_size=2000000 +build_flags = + ${LilyGo_TBeam_SX1276.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=160 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 +; -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +; -D RADIOLIB_DEBUG_BASIC=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${LilyGo_TBeam_SX1276.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${LilyGo_TBeam_SX1276.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:Tbeam_SX1276_repeater] extends = LilyGo_TBeam_SX1276 build_flags = diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index 249e6871..48a9ddc6 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -142,6 +142,29 @@ lib_deps = ${T_Beam_S3_Supreme_SX1262.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps] +extends = T_Beam_S3_Supreme_SX1262 +platform_packages = + framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${T_Beam_S3_Supreme_SX1262.build_flags} + -I examples/companion_radio/ui-new + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D OFFLINE_QUEUE_SIZE=256 +; -D BLE_DEBUG_LOGGING=1 +; -D MESH_PACKET_LOGGING=8 +; -D MESH_DEBUG=1 +build_src_filter = ${T_Beam_S3_Supreme_SX1262.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${T_Beam_S3_Supreme_SX1262.lib_deps} + densaugeo/base64 @ ~1.4.0 + [env:T_Beam_S3_Supreme_SX1262_companion_radio_wifi] extends = T_Beam_S3_Supreme_SX1262 build_flags = From b59966b6faef86daa58e7d2d03de3d9e18308a9c Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 22 May 2026 14:16:14 -0700 Subject: [PATCH 068/214] Add direct path override support --- docs/cli_commands.md | 27 +++ examples/simple_repeater/MyMesh.cpp | 320 ++++++++++++++++++++++++++-- examples/simple_repeater/MyMesh.h | 19 +- src/Mesh.cpp | 2 +- src/Mesh.h | 5 + src/helpers/ClientACL.cpp | 13 +- src/helpers/ClientACL.h | 5 +- 7 files changed, 373 insertions(+), 18 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 920c0fd7..c838fd10 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -134,6 +134,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore - If a full failure has no row yet, it first seeds the row at the active retry cutoff + `2.5 dB`, then applies the `0.25 dB` penalty. - Serial CLI page size is fixed at `128` rows; choose page with `get recent.repeater `. - Over LoRa remote CLI, page size is fixed at `7` rows; choose page with `get recent.repeater `. +- Repeaters can use adjacent entries in this table to short-circuit non-TRACE direct packets when this node appears later in the direct path. --- @@ -801,6 +802,8 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Default:** `2` for `rooftop`, `1` for `infra` and `mobile` +**Note:** Prefixes in `flood.retry.ignore` do not count toward this path length. + --- #### View or change whether advert packets are flood-retried @@ -900,6 +903,30 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### View or set direct path overrides for the current remote client +**Usage:** +- `get outpath` +- `set outpath ` +- `set outpath clear` +- `set outpath flood` +- `get altpath` +- `set altpath ` +- `set altpath clear` + +**Parameters:** +- `hopN_hex`: Hop hash, `2`, `4`, or `6` hex characters. All hops must use the same width. + +**Notes:** +- These commands require remote client context; they target the caller's ACL entry. +- The path hash size is inferred from the hop hash width. +- `outpath` overrides the primary direct route used for replies to the caller. +- `clear` forgets the current direct path and allows normal path discovery to repopulate it. +- `flood` forces replies to use flood packets until the client logs in again. +- `altpath` is an optional second direct route used for duplicate response attempts. +- `set altpath clear` removes the duplicate route so only one reply is sent. + +--- + #### View or change this room server's 'read-only' flag **Usage:** - `get allow.read.only` diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index d8e72cde..5071eb8c 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -529,6 +529,64 @@ void MyMesh::sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, ui } } +static bool pathsEqual(const uint8_t* a, uint8_t a_len, const uint8_t* b, uint8_t b_len) { + if (a == NULL || b == NULL || a_len != b_len || !mesh::Packet::isValidPathLen(a_len)) return false; + + uint8_t hash_count = a_len & 63; + uint8_t hash_size = (a_len >> 6) + 1; + return memcmp(a, b, hash_count * hash_size) == 0; +} + +static bool hasUsablePath(const uint8_t* path, uint8_t path_len) { + return path != NULL && mesh::Packet::isValidPathLen(path_len) && (path_len & 63) > 0; +} + +mesh::Packet* MyMesh::createPacketCopy(const mesh::Packet* packet, const char* caller) { + if (packet == NULL) return NULL; + + mesh::Packet* copy = obtainNewPacket(); + if (copy == NULL) { + MESH_DEBUG_PRINTLN("%s %s: error, packet pool empty", getLogDateTime(), caller); + return NULL; + } + *copy = *packet; + return copy; +} + +mesh::Packet* MyMesh::createAltPathCopy(const mesh::Packet* packet, + const uint8_t* primary_path, uint8_t primary_path_len, + const uint8_t* alt_path, uint8_t alt_path_len) { + if (!hasUsablePath(alt_path, alt_path_len)) return NULL; + if (hasUsablePath(primary_path, primary_path_len) && pathsEqual(primary_path, primary_path_len, alt_path, alt_path_len)) { + return NULL; + } + return createPacketCopy(packet, "MyMesh::createAltPathCopy()"); +} + +void MyMesh::sendFloodReplyWithAltPath(mesh::Packet* packet, + const uint8_t* direct_path, uint8_t direct_path_len, + const uint8_t* alt_path, uint8_t alt_path_len, + unsigned long delay_millis, uint8_t path_hash_size) { + mesh::Packet* direct = hasUsablePath(direct_path, direct_path_len) + ? createPacketCopy(packet, "MyMesh::sendFloodReplyWithAltPath(direct)") + : NULL; + mesh::Packet* alt = createAltPathCopy(packet, direct_path, direct_path_len, alt_path, alt_path_len); + + sendFloodReply(packet, delay_millis, path_hash_size); + if (direct) sendDirect(direct, direct_path, direct_path_len, delay_millis); + if (alt) sendDirect(alt, alt_path, alt_path_len, delay_millis); +} + +void MyMesh::sendDirectWithAltPath(mesh::Packet* packet, + const uint8_t* path, uint8_t path_len, + const uint8_t* alt_path, uint8_t alt_path_len, + uint32_t delay_millis) { + mesh::Packet* alt = createAltPathCopy(packet, path, path_len, alt_path, alt_path_len); + + sendDirect(packet, path, path_len, delay_millis); + if (alt) sendDirect(alt, alt_path, alt_path_len, delay_millis); +} + bool MyMesh::allowPacketForward(const mesh::Packet *packet) { if (_prefs.disable_fwd) return false; @@ -849,6 +907,54 @@ bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_ho return true; } +bool MyMesh::maybeShortCircuitDirect(mesh::Packet* packet) { + if (packet == NULL || !packet->isRouteDirect() || packet->getPayloadType() == PAYLOAD_TYPE_TRACE) { + return false; + } + + uint8_t hash_count = packet->getPathHashCount(); + uint8_t hash_size = packet->getPathHashSize(); + if (hash_count < 2 || hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + return false; + } + + // Normal direct forwarding handles the first path entry. Short-circuit only when we are later in the route. + int self_idx = -1; + for (uint8_t i = 1; i < hash_count; i++) { + if (self_id.isHashMatch(&packet->path[i * hash_size], hash_size)) { + self_idx = i; + break; + } + } + if (self_idx < 0) { + return false; + } + + const SimpleMeshTables* tables = (const SimpleMeshTables*)getTables(); + bool adjacent_recent = false; + if (self_idx > 0 + && tables->findRecentRepeaterByHash(&packet->path[(self_idx - 1) * hash_size], hash_size) != NULL) { + adjacent_recent = true; + } + if (!adjacent_recent && self_idx + 1 < hash_count + && tables->findRecentRepeaterByHash(&packet->path[(self_idx + 1) * hash_size], hash_size) != NULL) { + adjacent_recent = true; + } + if (!adjacent_recent) { + return false; + } + + uint8_t remaining = hash_count - (uint8_t)self_idx; + memmove(packet->path, &packet->path[self_idx * hash_size], remaining * hash_size); + packet->setPathHashCount(remaining); + + MESH_DEBUG_PRINTLN("%s direct short-circuit (type=%d, original_hop=%d, remaining_hops=%d)", + getLogDateTime(), + (uint32_t)packet->getPayloadType(), + self_idx + 1, + (uint32_t)remaining); + return true; +} uint8_t MyMesh::getDirectRetryCodingRateForSNR(int8_t snr_x4) const { if (_prefs.direct_retry_cr4_snr_x4 == 0 && _prefs.direct_retry_cr5_snr_x4 == 0 @@ -992,6 +1098,31 @@ bool MyMesh::floodRetryPrefixIgnored(const uint8_t* prefix, uint8_t prefix_len) } return false; } +uint8_t MyMesh::floodRetryEffectivePathLength(const mesh::Packet* packet, uint8_t max_hops) const { + if (packet == NULL || !packet->isRouteFlood() || packet->getPathHashCount() == 0) { + return 0; + } + + uint8_t hash_size = packet->getPathHashSize(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + return packet->getPathHashCount(); + } + + uint8_t hop_count = packet->getPathHashCount(); + if (max_hops < hop_count) { + hop_count = max_hops; + } + + uint8_t effective_len = 0; + const uint8_t* path = packet->path; + for (uint8_t hop = 0; hop < hop_count; hop++) { + if (!floodRetryPrefixIgnored(path, hash_size)) { + effective_len++; + } + path += hash_size; + } + return effective_len; +} bool MyMesh::floodRetryPrefixFresh(const uint8_t* prefix, uint8_t prefix_len) const { const auto* recent = ((const SimpleMeshTables *)getTables())->findRecentRepeaterByHash(prefix, prefix_len); if (recent == NULL || recent->last_heard_millis == 0) { @@ -1413,7 +1544,15 @@ uint8_t MyMesh::getFloodRetryMaxPathLength(const mesh::Packet* packet) const { if (gate == FLOOD_RETRY_PATH_GATE_DISABLED) { return FLOOD_RETRY_PATH_GATE_DISABLED; } - return gate <= 63 ? gate : 2; + if (gate > 63) { + gate = 2; + } + + uint8_t raw_hops = packet != NULL ? packet->getPathHashCount() : 0; + uint8_t effective_hops = floodRetryEffectivePathLength(packet); + uint8_t ignored_hops = raw_hops > effective_hops ? raw_hops - effective_hops : 0; + uint16_t adjusted_gate = (uint16_t)gate + ignored_hops; + return adjusted_gate > 63 ? 63 : (uint8_t)adjusted_gate; } uint8_t MyMesh::getFloodRetryMaxAttempts(const mesh::Packet* packet) const { if (_prefs.disable_fwd) { @@ -1447,7 +1586,7 @@ bool MyMesh::isFloodRetryEchoTarget(const mesh::Packet* packet, uint8_t progress if (hasFloodRetryPrefixes()) { return floodRetryLastHopMatches(packet); } - if (packet->getPathHashCount() <= progress_marker) { + if (floodRetryEffectivePathLength(packet) <= floodRetryEffectivePathLength(packet, progress_marker)) { return false; } return true; @@ -1607,13 +1746,22 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response mesh::Packet *path = createPathReturn(client->id, secret, packet->path, packet->path_len, PAYLOAD_TYPE_RESPONSE, reply_data, reply_len); - if (path) sendFloodReply(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize()); + if (path) { + if (hasUsablePath(client->out_path, client->out_path_len)) { + sendFloodReplyWithAltPath(path, client->out_path, client->out_path_len, + client->alt_path, client->alt_path_len, + SERVER_RESPONSE_DELAY, packet->getPathHashSize()); + } else { + sendFloodReply(path, SERVER_RESPONSE_DELAY, packet->getPathHashSize()); + } + } } else { mesh::Packet *reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len); if (reply) { - if (client->out_path_len != OUT_PATH_UNKNOWN) { // we have an out_path, so send DIRECT - sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY); + if (hasUsablePath(client->out_path, client->out_path_len)) { // we have an out_path, so send DIRECT + sendDirectWithAltPath(reply, client->out_path, client->out_path_len, + client->alt_path, client->alt_path_len, SERVER_RESPONSE_DELAY); } else { sendFloodReply(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize()); } @@ -1645,10 +1793,11 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, mesh::Packet *ack = createAck(ack_hash); if (ack) { - if (client->out_path_len == OUT_PATH_UNKNOWN) { - sendFloodReply(ack, TXT_ACK_DELAY, packet->getPathHashSize()); + if (hasUsablePath(client->out_path, client->out_path_len)) { + sendDirectWithAltPath(ack, client->out_path, client->out_path_len, + client->alt_path, client->alt_path_len, TXT_ACK_DELAY); } else { - sendDirect(ack, client->out_path, client->out_path_len, TXT_ACK_DELAY); + sendFloodReply(ack, TXT_ACK_DELAY, packet->getPathHashSize()); } } } @@ -1659,7 +1808,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, if (is_retry) { *reply = 0; } else { - handleCommand(sender_timestamp, command, reply); + handleCommand(sender_timestamp, client, command, reply); } int text_len = strlen(reply); if (text_len > 0) { @@ -1673,10 +1822,11 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len); if (reply) { - if (client->out_path_len == OUT_PATH_UNKNOWN) { - sendFloodReply(reply, CLI_REPLY_DELAY_MILLIS, packet->getPathHashSize()); + if (hasUsablePath(client->out_path, client->out_path_len)) { + sendDirectWithAltPath(reply, client->out_path, client->out_path_len, + client->alt_path, client->alt_path_len, CLI_REPLY_DELAY_MILLIS); } else { - sendDirect(reply, client->out_path, client->out_path_len, CLI_REPLY_DELAY_MILLIS); + sendFloodReply(reply, CLI_REPLY_DELAY_MILLIS, packet->getPathHashSize()); } } } @@ -1696,7 +1846,9 @@ bool MyMesh::onPeerPathRecv(mesh::Packet *packet, int sender_idx, const uint8_t auto client = acl.getClientByIdx(i); // store a copy of path, for sendDirect() - client->out_path_len = mesh::Packet::copyPath(client->out_path, path, path_len); + if (client->out_path_len != OUT_PATH_FORCE_FLOOD) { + client->out_path_len = mesh::Packet::copyPath(client->out_path, path, path_len); + } client->last_activity = getRTCClock()->getCurrentTime(); } else { MESH_DEBUG_PRINTLN("onPeerPathRecv: invalid peer idx: %d", i); @@ -2127,7 +2279,100 @@ void MyMesh::clearStats() { ((SimpleMeshTables *)getTables())->resetStats(); } -void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply) { +static char* trimSpaces(char* s) { + while (*s == ' ') s++; + char* end = s + strlen(s); + while (end > s && end[-1] == ' ') end--; + *end = 0; + return s; +} + +static bool parsePathCommand(char* raw, uint8_t* out_path, uint8_t& out_path_len, const char*& err) { + if (raw == NULL || out_path == NULL) { + err = "Err - bad params"; + return false; + } + + char* spec = trimSpaces(raw); + if (*spec == 0) { + err = "Err - missing path"; + return false; + } + if (strcmp(spec, "clear") == 0 || strcmp(spec, "-") == 0 || strcmp(spec, "none") == 0) { + out_path_len = OUT_PATH_UNKNOWN; + return true; + } + if (strcmp(spec, "flood") == 0) { + out_path_len = OUT_PATH_FORCE_FLOOD; + return true; + } + + uint8_t hash_size = 0; + uint8_t hop_count = 0; + char* token = spec; + while (token && *token) { + char* comma = strchr(token, ','); + if (comma) *comma = 0; + token = trimSpaces(token); + + int hex_len = strlen(token); + if (!(hex_len == 2 || hex_len == 4 || hex_len == 6)) { + err = "Err - each hop must be 1/2/3 bytes hex"; + return false; + } + + uint8_t hop_hash_size = (uint8_t)(hex_len / 2); + if (hash_size == 0) { + hash_size = hop_hash_size; + } else if (hash_size != hop_hash_size) { + err = "Err - mixed hash sizes in path"; + return false; + } + + if (hop_count >= 63 || (hop_count + 1) * hash_size > MAX_PATH_SIZE) { + err = "Err - path too long"; + return false; + } + if (!mesh::Utils::fromHex(&out_path[hop_count * hash_size], hash_size, token)) { + err = "Err - bad hex"; + return false; + } + + hop_count++; + token = comma ? comma + 1 : NULL; + } + + if (hash_size == 0 || hop_count == 0) { + err = "Err - missing path"; + return false; + } + out_path_len = ((hash_size - 1) << 6) | (hop_count & 63); + return true; +} + +static void formatPathReply(const uint8_t* path, uint8_t path_len, char* out, size_t out_len) { + if (path_len == OUT_PATH_FORCE_FLOOD) { + snprintf(out, out_len, "> flood"); + return; + } + if (path_len == OUT_PATH_UNKNOWN) { + snprintf(out, out_len, "> unknown"); + return; + } + if (!mesh::Packet::isValidPathLen(path_len)) { + snprintf(out, out_len, "> invalid"); + return; + } + + uint8_t hash_size = (path_len >> 6) + 1; + uint8_t hop_count = path_len & 63; + uint8_t byte_len = hop_count * hash_size; + char hex[(MAX_PATH_SIZE * 2) + 1]; + mesh::Utils::toHex(hex, path, byte_len); + snprintf(out, out_len, "> hs=%u hops=%u hex=%s", (uint32_t)hash_size, (uint32_t)hop_count, hex); +} + +void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *command, char *reply) { if (region_load_active) { if (StrHelper::isBlank(command)) { // empty/blank line, signal to terminate 'load' operation region_map = temp_map; // copy over the temp instance as new current map @@ -2204,6 +2449,53 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply Serial.printf("\n"); } reply[0] = 0; + } else if (strcmp(command, "get outpath") == 0 + || strcmp(command, "set outpath") == 0 + || strncmp(command, "set outpath ", 12) == 0 + || strcmp(command, "get altpath") == 0 + || strcmp(command, "set altpath") == 0 + || strncmp(command, "set altpath ", 12) == 0) { + bool is_get = strncmp(command, "get ", 4) == 0; + bool is_alt = strstr(command, "altpath") != NULL; + if (sender == NULL) { + strcpy(reply, "Err - command needs remote client context"); + } else if (is_get) { + formatPathReply(is_alt ? sender->alt_path : sender->out_path, + is_alt ? sender->alt_path_len : sender->out_path_len, + reply, 160); + } else { + char* spec = command + 11; // length of "set outpath"/"set altpath" + if (*spec == ' ') spec++; + + uint8_t path[MAX_PATH_SIZE]; + uint8_t path_len = OUT_PATH_UNKNOWN; + const char* err = NULL; + if (!parsePathCommand(spec, path, path_len, err)) { + strcpy(reply, err ? err : "Err - invalid path"); + } else if (is_alt && path_len == OUT_PATH_FORCE_FLOOD) { + strcpy(reply, "Err - bad params"); + } else { + if (is_alt) { + if (path_len == OUT_PATH_UNKNOWN) { + memset(sender->alt_path, 0, sizeof(sender->alt_path)); + sender->alt_path_len = OUT_PATH_UNKNOWN; + } else { + sender->alt_path_len = mesh::Packet::copyPath(sender->alt_path, path, path_len); + } + } else { + if (path_len == OUT_PATH_UNKNOWN || path_len == OUT_PATH_FORCE_FLOOD) { + memset(sender->out_path, 0, sizeof(sender->out_path)); + sender->out_path_len = path_len; + } else { + sender->out_path_len = mesh::Packet::copyPath(sender->out_path, path, path_len); + } + } + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); + formatPathReply(is_alt ? sender->alt_path : sender->out_path, + is_alt ? sender->alt_path_len : sender->out_path_len, + reply, 160); + } + } } else if (strncmp(command, "get recent.repeater", 19) == 0 || strncmp(command, "set recent.repeater", 19) == 0 || strncmp(command, "clear recent.repeater", 21) == 0 diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 73f80556..0e6c5f70 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -141,6 +141,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { bool floodRetryPrefixMatches(const mesh::Packet* packet) const; bool floodRetryLastHopMatches(const mesh::Packet* packet) const; bool floodRetryPrefixIgnored(const uint8_t* prefix, uint8_t prefix_len) const; + uint8_t floodRetryEffectivePathLength(const mesh::Packet* packet, uint8_t max_hops = 0xFF) const; bool floodRetryPrefixFresh(const uint8_t* prefix, uint8_t prefix_len) const; int floodRetryBucketForPrefix(const uint8_t* prefix, uint8_t prefix_len, bool require_fresh, bool include_other) const; @@ -184,6 +185,7 @@ protected: uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override; uint8_t getDefaultTxCodingRate() const override { return active_cr; } bool allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const override; + bool maybeShortCircuitDirect(mesh::Packet* packet) override; void configureDirectRetryPacket(mesh::Packet* retry, const mesh::Packet* original, uint8_t retry_attempt) override; uint32_t getDirectRetryEchoDelay(const mesh::Packet* packet) const override; uint8_t getDirectRetryMaxAttempts(const mesh::Packet* packet) const override; @@ -223,6 +225,18 @@ protected: void onControlDataRecv(mesh::Packet* packet) override; void sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, uint8_t path_hash_size); + mesh::Packet* createPacketCopy(const mesh::Packet* packet, const char* caller); + mesh::Packet* createAltPathCopy(const mesh::Packet* packet, + const uint8_t* primary_path, uint8_t primary_path_len, + const uint8_t* alt_path, uint8_t alt_path_len); + void sendFloodReplyWithAltPath(mesh::Packet* packet, + const uint8_t* direct_path, uint8_t direct_path_len, + const uint8_t* alt_path, uint8_t alt_path_len, + unsigned long delay_millis, uint8_t path_hash_size); + void sendDirectWithAltPath(mesh::Packet* packet, + const uint8_t* path, uint8_t path_len, + const uint8_t* alt_path, uint8_t alt_path_len, + uint32_t delay_millis); public: MyMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables); @@ -272,7 +286,10 @@ public: void saveIdentity(const mesh::LocalIdentity& new_id) override; void clearStats() override; - void handleCommand(uint32_t sender_timestamp, char* command, char* reply); + void handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char* command, char* reply); + void handleCommand(uint32_t sender_timestamp, char* command, char* reply) { + handleCommand(sender_timestamp, NULL, command, reply); + } void loop(); #if defined(WITH_BRIDGE) diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 941c71a1..8b088b31 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -254,7 +254,7 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { } } - if (self_id.isHashMatch(pkt->path, pkt->getPathHashSize()) && allowPacketForward(pkt)) { + if ((self_id.isHashMatch(pkt->path, pkt->getPathHashSize()) || maybeShortCircuitDirect(pkt)) && allowPacketForward(pkt)) { if (pkt->getPayloadType() == PAYLOAD_TYPE_MULTIPART) { return forwardMultipartDirect(pkt); } else if (pkt->getPayloadType() == PAYLOAD_TYPE_ACK) { diff --git a/src/Mesh.h b/src/Mesh.h index 3f86653e..483d4826 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -135,6 +135,11 @@ protected: */ virtual bool allowDirectRetry(const Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const; + /** + * \brief Allow subclasses to rewrite a non-TRACE DIRECT packet path when this node can safely skip ahead. + */ + virtual bool maybeShortCircuitDirect(Packet* packet) { return false; } + /** * \returns milliseconds to wait for the next-hop echo before queueing one retry of the DIRECT packet. */ diff --git a/src/helpers/ClientACL.cpp b/src/helpers/ClientACL.cpp index 12823827..1d880823 100644 --- a/src/helpers/ClientACL.cpp +++ b/src/helpers/ClientACL.cpp @@ -1,5 +1,7 @@ #include "ClientACL.h" +static const uint8_t CONTACT_RECORD_VERSION_ALT_PATH = 1; + static File openWrite(FILESYSTEM* _fs, const char* filename) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) _fs->remove(filename); @@ -28,6 +30,7 @@ void ClientACL::load(FILESYSTEM* fs, const mesh::LocalIdentity& self_id) { uint8_t unused[2]; memset(&c, 0, sizeof(c)); + c.alt_path_len = OUT_PATH_UNKNOWN; bool success = (file.read(pub_key, 32) == 32); success = success && (file.read((uint8_t *) &c.permissions, 1) == 1); @@ -36,6 +39,10 @@ void ClientACL::load(FILESYSTEM* fs, const mesh::LocalIdentity& self_id) { success = success && (file.read((uint8_t *)&c.out_path_len, 1) == 1); success = success && (file.read(c.out_path, 64) == 64); success = success && (file.read(c.shared_secret, PUB_KEY_SIZE) == PUB_KEY_SIZE); // will be recalculated below + if (success && unused[0] >= CONTACT_RECORD_VERSION_ALT_PATH) { + success = success && (file.read((uint8_t *)&c.alt_path_len, 1) == 1); + success = success && (file.read(c.alt_path, 64) == 64); + } if (!success) break; // EOF @@ -57,7 +64,8 @@ void ClientACL::save(FILESYSTEM* fs, bool (*filter)(ClientInfo*)) { File file = openWrite(_fs, "/s_contacts"); if (file) { uint8_t unused[2]; - memset(unused, 0, sizeof(unused)); + unused[0] = CONTACT_RECORD_VERSION_ALT_PATH; + unused[1] = 0; for (int i = 0; i < num_clients; i++) { auto c = &clients[i]; @@ -70,6 +78,8 @@ void ClientACL::save(FILESYSTEM* fs, bool (*filter)(ClientInfo*)) { success = success && (file.write((uint8_t *)&c->out_path_len, 1) == 1); success = success && (file.write(c->out_path, 64) == 64); success = success && (file.write(c->shared_secret, PUB_KEY_SIZE) == PUB_KEY_SIZE); + success = success && (file.write((uint8_t *)&c->alt_path_len, 1) == 1); + success = success && (file.write(c->alt_path, 64) == 64); if (!success) break; // write failed } @@ -115,6 +125,7 @@ ClientInfo* ClientACL::putClient(const mesh::Identity& id, uint8_t init_perms) { c->permissions = init_perms; c->id = id; c->out_path_len = OUT_PATH_UNKNOWN; + c->alt_path_len = OUT_PATH_UNKNOWN; return c; } diff --git a/src/helpers/ClientACL.h b/src/helpers/ClientACL.h index b758f706..356574de 100644 --- a/src/helpers/ClientACL.h +++ b/src/helpers/ClientACL.h @@ -10,13 +10,16 @@ #define PERM_ACL_READ_WRITE 2 #define PERM_ACL_ADMIN 3 -#define OUT_PATH_UNKNOWN 0xFF +#define OUT_PATH_FORCE_FLOOD 0xFE +#define OUT_PATH_UNKNOWN 0xFF struct ClientInfo { mesh::Identity id; uint8_t permissions; uint8_t out_path_len; uint8_t out_path[MAX_PATH_SIZE]; + uint8_t alt_path_len; + uint8_t alt_path[MAX_PATH_SIZE]; uint8_t shared_secret[PUB_KEY_SIZE]; uint32_t last_timestamp; // by THEIR clock (transient) uint32_t last_activity; // by OUR clock (transient) From c0e3860c3c321662a0e508276d49c5f7b0d2f738 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 22 May 2026 14:21:10 -0700 Subject: [PATCH 069/214] Add repeater flood text command --- docs/cli_commands.md | 11 +++++ examples/simple_repeater/MyMesh.cpp | 63 +++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index c838fd10..d200cdd1 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -115,6 +115,17 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +### Send flood text to `#repeaters` channel + +**Usage:** +- `send text.flood ` + +**Notes:** +- Sends a `PAYLOAD_TYPE_GRP_TXT` flood message using the built-in `#repeaters` channel key. +- Message format is `: `. + +--- + ### Get or set recent repeater fallback prefix/SNR **Usage:** - `get recent.repeater` diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 5071eb8c..a2fb3162 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -63,6 +63,9 @@ #define CLI_REPLY_DELAY_MILLIS 600 #define LAZY_CONTACTS_WRITE_DELAY 5000 +#ifndef REPEATERS_CHANNEL_KEY_HEX + #define REPEATERS_CHANNEL_KEY_HEX "89db441e2814dccf0dbd2e8cc5f501a3" +#endif static void formatRecentRepeaterPrefix(const SimpleMeshTables::RecentRepeaterInfo* info, char* out, size_t out_len) { if (out == NULL || out_len == 0) { @@ -541,6 +544,22 @@ static bool hasUsablePath(const uint8_t* path, uint8_t path_len) { return path != NULL && mesh::Packet::isValidPathLen(path_len) && (path_len & 63) > 0; } +static bool buildRepeatersChannel(mesh::GroupChannel& channel) { + const char* hex = REPEATERS_CHANNEL_KEY_HEX; + size_t hex_len = strlen(hex); + if (!(hex_len == 32 || hex_len == 64)) return false; + for (size_t i = 0; i < hex_len; i++) { + if (!mesh::Utils::isHexChar(hex[i])) return false; + } + + memset(channel.secret, 0, sizeof(channel.secret)); + size_t key_len = hex_len / 2; + if (!mesh::Utils::fromHex(channel.secret, key_len, hex)) return false; + + mesh::Utils::sha256(channel.hash, sizeof(channel.hash), channel.secret, key_len); + return true; +} + mesh::Packet* MyMesh::createPacketCopy(const mesh::Packet* packet, const char* caller) { if (packet == NULL) return NULL; @@ -2496,6 +2515,50 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char * reply, 160); } } + } else if (strncmp(command, "send text.flood ", 16) == 0) { + char* text = trimSpaces(command + 16); + if (*text == 0) { + strcpy(reply, "Err - usage: send text.flood "); + } else { + mesh::GroupChannel channel; + if (!buildRepeatersChannel(channel)) { + strcpy(reply, "Err - invalid #repeaters key"); + } else { + uint8_t temp[MAX_PACKET_PAYLOAD]; + uint32_t timestamp = getRTCClock()->getCurrentTimeUnique(); + memcpy(temp, ×tamp, 4); + temp[4] = (TXT_TYPE_PLAIN << 2); + + const size_t max_data_len = MAX_PACKET_PAYLOAD - CIPHER_BLOCK_SIZE; + const size_t prefix_cap = max_data_len > 5 ? max_data_len - 5 + 1 : 0; + int prefix_written = prefix_cap > 0 + ? snprintf((char*)&temp[5], prefix_cap, "%s: ", _prefs.node_name) + : -1; + if (prefix_written < 0) { + strcpy(reply, "Err - unable to create message"); + } else { + size_t prefix_len = (size_t)prefix_written; + if (prefix_len >= prefix_cap) { + prefix_len = prefix_cap - 1; + } + + size_t text_len = strlen(text); + size_t max_text_len = max_data_len - 5 - prefix_len; + if (text_len > max_text_len) { + text_len = max_text_len; + } + memcpy(&temp[5 + prefix_len], text, text_len); + + auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_TXT, channel, temp, 5 + prefix_len + text_len); + if (pkt) { + sendFloodScoped(default_scope, pkt, 0, _prefs.path_hash_mode + 1); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Err - unable to create packet"); + } + } + } + } } else if (strncmp(command, "get recent.repeater", 19) == 0 || strncmp(command, "set recent.repeater", 19) == 0 || strncmp(command, "clear recent.repeater", 21) == 0 From f693bdb2128383dbe707925ce12954669d3fa96b Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 22 May 2026 14:28:03 -0700 Subject: [PATCH 070/214] Add opt-in battery alert flood text --- docs/cli_commands.md | 17 +++ examples/simple_repeater/MyMesh.cpp | 163 +++++++++++++++++++++------- examples/simple_repeater/MyMesh.h | 5 + src/helpers/CommonCLI.cpp | 11 +- src/helpers/CommonCLI.h | 1 + 5 files changed, 158 insertions(+), 39 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index d200cdd1..e8d7a20a 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -126,6 +126,23 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +### View or change automatic low-battery alerts to `#repeaters` + +**Usage:** +- `get battery.alert` +- `set battery.alert ` + +**Parameters:** +- `state`: `on` (enable) or `off` (disable) + +**Default:** `off` + +**Notes:** +- When enabled, sends a `#repeaters` flood text warning if voltage is above `1 V` and the battery estimate is below `20%`. +- Warnings repeat every `24` hours, or every `12` hours below `10%`. + +--- + ### Get or set recent repeater fallback prefix/SNR **Usage:** - `get recent.repeater` diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index a2fb3162..6dc8bb7d 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -66,6 +66,19 @@ #ifndef REPEATERS_CHANNEL_KEY_HEX #define REPEATERS_CHANNEL_KEY_HEX "89db441e2814dccf0dbd2e8cc5f501a3" #endif +#ifndef BATT_MIN_MILLIVOLTS + #define BATT_MIN_MILLIVOLTS 3000 +#endif +#ifndef BATT_MAX_MILLIVOLTS + #define BATT_MAX_MILLIVOLTS 4200 +#endif + +#define LOW_BATTERY_MIN_VALID_MV 1000 +#define LOW_BATTERY_WARN_PERCENT 20 +#define LOW_BATTERY_CRITICAL_PERCENT 10 +#define LOW_BATTERY_CHECK_INTERVAL (60UL * 1000UL) +#define LOW_BATTERY_WARN_INTERVAL (24UL * 60UL * 60UL * 1000UL) +#define LOW_BATTERY_CRITICAL_INTERVAL (12UL * 60UL * 60UL * 1000UL) static void formatRecentRepeaterPrefix(const SimpleMeshTables::RecentRepeaterInfo* info, char* out, size_t out_len) { if (out == NULL || out_len == 0) { @@ -560,6 +573,17 @@ static bool buildRepeatersChannel(mesh::GroupChannel& channel) { return true; } +static uint8_t batteryPercentFromMilliVolts(uint16_t batt_mv) { + const int min_mv = BATT_MIN_MILLIVOLTS; + const int max_mv = BATT_MAX_MILLIVOLTS; + if (max_mv <= min_mv) return 100; + + int pct = (((int)batt_mv - min_mv) * 100) / (max_mv - min_mv); + if (pct < 0) return 0; + if (pct > 100) return 100; + return (uint8_t)pct; +} + mesh::Packet* MyMesh::createPacketCopy(const mesh::Packet* packet, const char* caller) { if (packet == NULL) return NULL; @@ -1970,6 +1994,9 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc last_millis = 0; uptime_millis = 0; next_local_advert = next_flood_advert = 0; + next_battery_alert_check = 0; + last_battery_alert_sent = 0; + battery_alert_sent = false; dirty_contacts_expiry = 0; set_radio_at = revert_radio_at = 0; _logging = false; @@ -1996,6 +2023,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_retry_path_gate = 2; _prefs.flood_retry_bridge_enabled = 0; _prefs.flood_retry_advert_enabled = 0; + _prefs.battery_alert_enabled = 0; _prefs.direct_retry_cr4_snr_x4 = DIRECT_RETRY_CR4_MIN_SNR_X4_DEFAULT; _prefs.direct_retry_cr5_snr_x4 = DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT; _prefs.direct_retry_cr7_snr_x4 = DIRECT_RETRY_CR7_MIN_SNR_X4_DEFAULT; @@ -2114,6 +2142,82 @@ void MyMesh::sendFloodScoped(const TransportKey& scope, mesh::Packet* pkt, uint3 } } +bool MyMesh::sendRepeatersFloodText(const char* text) { + if (text == NULL || *text == 0) return false; + + mesh::GroupChannel channel; + if (!buildRepeatersChannel(channel)) { + return false; + } + + uint8_t temp[MAX_PACKET_PAYLOAD]; + uint32_t timestamp = getRTCClock()->getCurrentTimeUnique(); + memcpy(temp, ×tamp, 4); + temp[4] = (TXT_TYPE_PLAIN << 2); + + const size_t max_data_len = MAX_PACKET_PAYLOAD - CIPHER_BLOCK_SIZE; + const size_t prefix_cap = max_data_len > 5 ? max_data_len - 5 + 1 : 0; + int prefix_written = prefix_cap > 0 + ? snprintf((char*)&temp[5], prefix_cap, "%s: ", _prefs.node_name) + : -1; + if (prefix_written < 0) { + return false; + } + + size_t prefix_len = (size_t)prefix_written; + if (prefix_len >= prefix_cap) { + prefix_len = prefix_cap - 1; + } + + size_t text_len = strlen(text); + size_t max_text_len = max_data_len - 5 - prefix_len; + if (text_len > max_text_len) { + text_len = max_text_len; + } + memcpy(&temp[5 + prefix_len], text, text_len); + + auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_TXT, channel, temp, 5 + prefix_len + text_len); + if (pkt == NULL) { + return false; + } + + sendFloodScoped(default_scope, pkt, 0, _prefs.path_hash_mode + 1); + return true; +} + +void MyMesh::checkBatteryAlert() { + if (!_prefs.battery_alert_enabled) { + battery_alert_sent = false; + return; + } + + if (next_battery_alert_check && !millisHasNowPassed(next_battery_alert_check)) { + return; + } + next_battery_alert_check = futureMillis(LOW_BATTERY_CHECK_INTERVAL); + + uint16_t batt_mv = board.getBattMilliVolts(); + uint8_t batt_pct = batteryPercentFromMilliVolts(batt_mv); + if (batt_mv <= LOW_BATTERY_MIN_VALID_MV || batt_pct >= LOW_BATTERY_WARN_PERCENT) { + battery_alert_sent = false; + return; + } + + unsigned long interval = batt_pct < LOW_BATTERY_CRITICAL_PERCENT + ? LOW_BATTERY_CRITICAL_INTERVAL + : LOW_BATTERY_WARN_INTERVAL; + if (battery_alert_sent && !millisHasNowPassed(last_battery_alert_sent + interval)) { + return; + } + + char text[96]; + snprintf(text, sizeof(text), "LOW BATTERY %u%% (%u mV)", (uint32_t)batt_pct, (uint32_t)batt_mv); + if (sendRepeatersFloodText(text)) { + battery_alert_sent = true; + last_battery_alert_sent = millis(); + } +} + void MyMesh::applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) { set_radio_at = futureMillis(2000); // give CLI reply some time to be sent back, before applying temp radio params pending_freq = freq; @@ -2519,45 +2623,27 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char * char* text = trimSpaces(command + 16); if (*text == 0) { strcpy(reply, "Err - usage: send text.flood "); + } else if (sendRepeatersFloodText(text)) { + strcpy(reply, "OK"); } else { - mesh::GroupChannel channel; - if (!buildRepeatersChannel(channel)) { - strcpy(reply, "Err - invalid #repeaters key"); - } else { - uint8_t temp[MAX_PACKET_PAYLOAD]; - uint32_t timestamp = getRTCClock()->getCurrentTimeUnique(); - memcpy(temp, ×tamp, 4); - temp[4] = (TXT_TYPE_PLAIN << 2); - - const size_t max_data_len = MAX_PACKET_PAYLOAD - CIPHER_BLOCK_SIZE; - const size_t prefix_cap = max_data_len > 5 ? max_data_len - 5 + 1 : 0; - int prefix_written = prefix_cap > 0 - ? snprintf((char*)&temp[5], prefix_cap, "%s: ", _prefs.node_name) - : -1; - if (prefix_written < 0) { - strcpy(reply, "Err - unable to create message"); - } else { - size_t prefix_len = (size_t)prefix_written; - if (prefix_len >= prefix_cap) { - prefix_len = prefix_cap - 1; - } - - size_t text_len = strlen(text); - size_t max_text_len = max_data_len - 5 - prefix_len; - if (text_len > max_text_len) { - text_len = max_text_len; - } - memcpy(&temp[5 + prefix_len], text, text_len); - - auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_TXT, channel, temp, 5 + prefix_len + text_len); - if (pkt) { - sendFloodScoped(default_scope, pkt, 0, _prefs.path_hash_mode + 1); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Err - unable to create packet"); - } - } - } + strcpy(reply, "Err - unable to create packet"); + } + } else if (strcmp(command, "get battery.alert") == 0) { + sprintf(reply, "> %s", _prefs.battery_alert_enabled ? "on" : "off"); + } else if (strncmp(command, "set battery.alert ", 18) == 0) { + const char* value = command + 18; + if (strcmp(value, "on") == 0) { + _prefs.battery_alert_enabled = 1; + next_battery_alert_check = 0; + savePrefs(); + strcpy(reply, "OK"); + } else if (strcmp(value, "off") == 0) { + _prefs.battery_alert_enabled = 0; + battery_alert_sent = false; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Err - usage: set battery.alert "); } } else if (strncmp(command, "get recent.repeater", 19) == 0 || strncmp(command, "set recent.repeater", 19) == 0 @@ -2759,6 +2845,7 @@ void MyMesh::loop() { #endif mesh::Mesh::loop(); + checkBatteryAlert(); if (next_flood_advert && millisHasNowPassed(next_flood_advert)) { mesh::Packet *pkt = createSelfAdvert(); diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 0e6c5f70..f55d1d40 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -85,6 +85,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { uint32_t last_millis; uint64_t uptime_millis; unsigned long next_local_advert, next_flood_advert; + unsigned long next_battery_alert_check; + unsigned long last_battery_alert_sent; + bool battery_alert_sent; bool _logging; NodePrefs _prefs; ClientACL acl; @@ -163,6 +166,8 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { uint8_t handleAnonClockReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data); int handleRequest(ClientInfo* sender, uint32_t sender_timestamp, uint8_t* payload, size_t payload_len); mesh::Packet* createSelfAdvert(); + bool sendRepeatersFloodText(const char* text); + void checkBatteryAlert(); File openAppend(const char* fname); bool isLooped(const mesh::Packet* packet, const uint8_t max_counters[]); diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index db56c558..3611c05d 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -556,6 +556,9 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { sizeof(_prefs->direct_retry_cr7_snr_x4)); // 662 retry_cr_read += file.read((uint8_t *)&_prefs->direct_retry_cr8_snr_x4, sizeof(_prefs->direct_retry_cr8_snr_x4)); // 663 + _prefs->battery_alert_enabled = 0; + size_t battery_alert_read = file.read((uint8_t *)&_prefs->battery_alert_enabled, + sizeof(_prefs->battery_alert_enabled)); // 664 // PowerSaving-only prefs stored radio_fem_rxgain at 291, before direct retry timing existed. if (radio_fem_rxgain_read != sizeof(_prefs->radio_fem_rxgain) && legacy_retry_attempts_read == sizeof(legacy_retry_attempts_or_radio_fem_rxgain) @@ -654,6 +657,11 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { } else { sanitizeDirectRetryCrThresholds(_prefs); } + if (battery_alert_read != sizeof(_prefs->battery_alert_enabled)) { + _prefs->battery_alert_enabled = 0; + } else { + _prefs->battery_alert_enabled = constrain(_prefs->battery_alert_enabled, 0, 1); + } file.close(); } @@ -739,7 +747,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->direct_retry_cr5_snr_x4, sizeof(_prefs->direct_retry_cr5_snr_x4)); // 661 file.write((uint8_t *)&_prefs->direct_retry_cr7_snr_x4, sizeof(_prefs->direct_retry_cr7_snr_x4)); // 662 file.write((uint8_t *)&_prefs->direct_retry_cr8_snr_x4, sizeof(_prefs->direct_retry_cr8_snr_x4)); // 663 - // next: 664 + file.write((uint8_t *)&_prefs->battery_alert_enabled, sizeof(_prefs->battery_alert_enabled)); // 664 + // next: 665 file.close(); } diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 104a7150..4b7d910c 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -132,6 +132,7 @@ struct NodePrefs { // persisted to file int8_t direct_retry_cr5_snr_x4; int8_t direct_retry_cr7_snr_x4; int8_t direct_retry_cr8_snr_x4; + uint8_t battery_alert_enabled; }; class CommonCLICallbacks { From c93d907e85d5dd86e3b015e58e36894523309f5d Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 22 May 2026 14:34:19 -0700 Subject: [PATCH 071/214] Make battery alert thresholds configurable --- docs/cli_commands.md | 12 +++++- examples/simple_repeater/MyMesh.cpp | 62 +++++++++++++++++++++++++++-- src/helpers/CommonCLI.cpp | 20 +++++++++- src/helpers/CommonCLI.h | 2 + 4 files changed, 88 insertions(+), 8 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index e8d7a20a..a5a55e80 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -131,15 +131,23 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Usage:** - `get battery.alert` - `set battery.alert ` +- `get battery.alert.low` +- `set battery.alert.low ` +- `get battery.alert.critical` +- `set battery.alert.critical ` **Parameters:** - `state`: `on` (enable) or `off` (disable) +- `percent`: Battery percentage threshold **Default:** `off` +**Default thresholds:** `20` for `battery.alert.low`, `10` for `battery.alert.critical` + **Notes:** -- When enabled, sends a `#repeaters` flood text warning if voltage is above `1 V` and the battery estimate is below `20%`. -- Warnings repeat every `24` hours, or every `12` hours below `10%`. +- When enabled, sends a `#repeaters` flood text warning if voltage is above `1 V` and the battery estimate is below `battery.alert.low`. +- Warnings repeat every `24` hours, or every `12` hours below `battery.alert.critical`. +- `battery.alert.critical` must be lower than `battery.alert.low`. --- diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 6dc8bb7d..22862518 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -74,8 +74,8 @@ #endif #define LOW_BATTERY_MIN_VALID_MV 1000 -#define LOW_BATTERY_WARN_PERCENT 20 -#define LOW_BATTERY_CRITICAL_PERCENT 10 +#define LOW_BATTERY_WARN_PERCENT_DEFAULT 20 +#define LOW_BATTERY_CRITICAL_PERCENT_DEFAULT 10 #define LOW_BATTERY_CHECK_INTERVAL (60UL * 1000UL) #define LOW_BATTERY_WARN_INTERVAL (24UL * 60UL * 60UL * 1000UL) #define LOW_BATTERY_CRITICAL_INTERVAL (12UL * 60UL * 60UL * 1000UL) @@ -584,6 +584,30 @@ static uint8_t batteryPercentFromMilliVolts(uint16_t batt_mv) { return (uint8_t)pct; } +static bool parseBatteryAlertPercent(const char* value, uint8_t min_value, uint8_t max_value, uint8_t& result) { + if (value == NULL || *value == 0) { + return false; + } + + uint16_t parsed = 0; + while (*value) { + if (*value < '0' || *value > '9') { + return false; + } + parsed = (uint16_t)(parsed * 10 + (*value - '0')); + if (parsed > max_value) { + return false; + } + value++; + } + if (parsed < min_value) { + return false; + } + + result = (uint8_t)parsed; + return true; +} + mesh::Packet* MyMesh::createPacketCopy(const mesh::Packet* packet, const char* caller) { if (packet == NULL) return NULL; @@ -2024,6 +2048,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_retry_bridge_enabled = 0; _prefs.flood_retry_advert_enabled = 0; _prefs.battery_alert_enabled = 0; + _prefs.battery_alert_low_percent = LOW_BATTERY_WARN_PERCENT_DEFAULT; + _prefs.battery_alert_critical_percent = LOW_BATTERY_CRITICAL_PERCENT_DEFAULT; _prefs.direct_retry_cr4_snr_x4 = DIRECT_RETRY_CR4_MIN_SNR_X4_DEFAULT; _prefs.direct_retry_cr5_snr_x4 = DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT; _prefs.direct_retry_cr7_snr_x4 = DIRECT_RETRY_CR7_MIN_SNR_X4_DEFAULT; @@ -2198,12 +2224,12 @@ void MyMesh::checkBatteryAlert() { uint16_t batt_mv = board.getBattMilliVolts(); uint8_t batt_pct = batteryPercentFromMilliVolts(batt_mv); - if (batt_mv <= LOW_BATTERY_MIN_VALID_MV || batt_pct >= LOW_BATTERY_WARN_PERCENT) { + if (batt_mv <= LOW_BATTERY_MIN_VALID_MV || batt_pct >= _prefs.battery_alert_low_percent) { battery_alert_sent = false; return; } - unsigned long interval = batt_pct < LOW_BATTERY_CRITICAL_PERCENT + unsigned long interval = batt_pct < _prefs.battery_alert_critical_percent ? LOW_BATTERY_CRITICAL_INTERVAL : LOW_BATTERY_WARN_INTERVAL; if (battery_alert_sent && !millisHasNowPassed(last_battery_alert_sent + interval)) { @@ -2630,6 +2656,10 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char * } } else if (strcmp(command, "get battery.alert") == 0) { sprintf(reply, "> %s", _prefs.battery_alert_enabled ? "on" : "off"); + } else if (strcmp(command, "get battery.alert.low") == 0) { + sprintf(reply, "> %u", (uint32_t)_prefs.battery_alert_low_percent); + } else if (strcmp(command, "get battery.alert.critical") == 0) { + sprintf(reply, "> %u", (uint32_t)_prefs.battery_alert_critical_percent); } else if (strncmp(command, "set battery.alert ", 18) == 0) { const char* value = command + 18; if (strcmp(value, "on") == 0) { @@ -2645,6 +2675,30 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char * } else { strcpy(reply, "Err - usage: set battery.alert "); } + } else if (strncmp(command, "set battery.alert.low ", 22) == 0) { + uint8_t percent; + if (!parseBatteryAlertPercent(command + 22, 1, 100, percent)) { + strcpy(reply, "Err - usage: set battery.alert.low <1-100>"); + } else if (percent <= _prefs.battery_alert_critical_percent) { + strcpy(reply, "Err - low must be greater than critical"); + } else { + _prefs.battery_alert_low_percent = percent; + next_battery_alert_check = 0; + savePrefs(); + strcpy(reply, "OK"); + } + } else if (strncmp(command, "set battery.alert.critical ", 27) == 0) { + uint8_t percent; + if (!parseBatteryAlertPercent(command + 27, 0, 99, percent)) { + strcpy(reply, "Err - usage: set battery.alert.critical <0-99>"); + } else if (percent >= _prefs.battery_alert_low_percent) { + strcpy(reply, "Err - critical must be less than low"); + } else { + _prefs.battery_alert_critical_percent = percent; + next_battery_alert_check = 0; + savePrefs(); + strcpy(reply, "OK"); + } } else if (strncmp(command, "get recent.repeater", 19) == 0 || strncmp(command, "set recent.repeater", 19) == 0 || strncmp(command, "clear recent.repeater", 21) == 0 diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 3611c05d..7e318c78 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -559,6 +559,12 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->battery_alert_enabled = 0; size_t battery_alert_read = file.read((uint8_t *)&_prefs->battery_alert_enabled, sizeof(_prefs->battery_alert_enabled)); // 664 + _prefs->battery_alert_low_percent = 20; + _prefs->battery_alert_critical_percent = 10; + size_t battery_alert_low_read = file.read((uint8_t *)&_prefs->battery_alert_low_percent, + sizeof(_prefs->battery_alert_low_percent)); // 665 + size_t battery_alert_critical_read = file.read((uint8_t *)&_prefs->battery_alert_critical_percent, + sizeof(_prefs->battery_alert_critical_percent)); // 666 // PowerSaving-only prefs stored radio_fem_rxgain at 291, before direct retry timing existed. if (radio_fem_rxgain_read != sizeof(_prefs->radio_fem_rxgain) && legacy_retry_attempts_read == sizeof(legacy_retry_attempts_or_radio_fem_rxgain) @@ -566,7 +572,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { || _prefs->direct_retry_timing_magic[1] != DIRECT_RETRY_TIMING_MAGIC_1)) { _prefs->radio_fem_rxgain = constrain(legacy_retry_attempts_or_radio_fem_rxgain, 0, 1); } - // next: 664 + // next: 667 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -662,6 +668,14 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { } else { _prefs->battery_alert_enabled = constrain(_prefs->battery_alert_enabled, 0, 1); } + if (battery_alert_low_read != sizeof(_prefs->battery_alert_low_percent) + || battery_alert_critical_read != sizeof(_prefs->battery_alert_critical_percent) + || _prefs->battery_alert_low_percent < 1 + || _prefs->battery_alert_low_percent > 100 + || _prefs->battery_alert_critical_percent >= _prefs->battery_alert_low_percent) { + _prefs->battery_alert_low_percent = 20; + _prefs->battery_alert_critical_percent = 10; + } file.close(); } @@ -748,7 +762,9 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->direct_retry_cr7_snr_x4, sizeof(_prefs->direct_retry_cr7_snr_x4)); // 662 file.write((uint8_t *)&_prefs->direct_retry_cr8_snr_x4, sizeof(_prefs->direct_retry_cr8_snr_x4)); // 663 file.write((uint8_t *)&_prefs->battery_alert_enabled, sizeof(_prefs->battery_alert_enabled)); // 664 - // next: 665 + file.write((uint8_t *)&_prefs->battery_alert_low_percent, sizeof(_prefs->battery_alert_low_percent)); // 665 + file.write((uint8_t *)&_prefs->battery_alert_critical_percent, sizeof(_prefs->battery_alert_critical_percent)); // 666 + // next: 667 file.close(); } diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 4b7d910c..112c41c0 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -133,6 +133,8 @@ struct NodePrefs { // persisted to file int8_t direct_retry_cr7_snr_x4; int8_t direct_retry_cr8_snr_x4; uint8_t battery_alert_enabled; + uint8_t battery_alert_low_percent; + uint8_t battery_alert_critical_percent; }; class CommonCLICallbacks { From c3712737e94267334e0f55df71c11693f2cf48c3 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 12 May 2026 23:50:47 -0700 Subject: [PATCH 072/214] build.sh: add optional Cascadia profile overrides --- build.sh | 329 +++++++++++++++++++++++++++- examples/simple_repeater/MyMesh.cpp | 46 +++- 2 files changed, 368 insertions(+), 7 deletions(-) diff --git a/build.sh b/build.sh index 9ef20d11..ad8f4079 100755 --- a/build.sh +++ b/build.sh @@ -4,6 +4,16 @@ ALL_PIO_ENVS=() PIO_CONFIG_JSON="" MENU_CHOICE="" SELECTED_TARGET="" +SELECTED_COMMAND_ARGS=() +MESHDEBUG_OVERRIDE="" +PACKET_LOGGING_OVERRIDE="" +RADIO_SETTINGS_API_URL="https://api.meshcore.nz/api/v1/config" +RADIO_SETTING_TITLE="" +RADIO_FREQ_OVERRIDE="" +RADIO_BW_OVERRIDE="" +RADIO_SF_OVERRIDE="" +RADIO_CR_OVERRIDE="" +FIRMWARE_PROFILE_OVERRIDE="" ENV_VARIANT_SUFFIX_PATTERN='companion_radio_serial|companion_radio_wifi|companion_radio_usb|comp_radio_usb|companion_usb|companion_radio_ble|companion_ble|repeater_bridge_rs232_serial1|repeater_bridge_rs232_serial2|repeater_bridge_rs232|repeater_bridge_espnow|terminal_chat|room_server|room_svr|kiss_modem|sensor|repeatr|repeater' BOARD_MODIFIER_WITHOUT_DISPLAY="_without_display" @@ -51,7 +61,7 @@ Examples: Build firmware for the "RAK_4631_repeater" device target $ bash build.sh build-firmware RAK_4631_repeater -Run without arguments to choose a target from an interactive menu +Run without arguments to choose an interactive build action/target, debug options, and radio settings $ bash build.sh Build all firmwares for device targets containing the string "RAK_4631" @@ -187,6 +197,277 @@ prompt_menu_choice() { done } +prompt_on_off_choice() { + local prompt_label=$1 + local default_choice=$2 + local choice + + while true; do + read -r -p "${prompt_label} [on/off] (default: ${default_choice}): " choice + choice=${choice,,} + if [ -z "$choice" ]; then + choice=$default_choice + fi + + case "$choice" in + on|off) + MENU_CHOICE="$choice" + return 0 + ;; + *) + echo "Invalid selection. Choose 'on' or 'off'." + ;; + esac + done +} + +prompt_for_build_mode() { + local options=( + "Build one firmware target" + "Build all firmwares" + "Build all repeater firmwares" + "Build all companion firmwares" + "Build all chat room server firmwares" + ) + + echo "No command provided. Select a build action:" + while true; do + print_numbered_menu "${options[@]}" + prompt_menu_choice "Build action" "${#options[@]}" + if [ "$MENU_CHOICE" == "QUIT" ]; then + echo "Cancelled." + exit 1 + fi + + case "$MENU_CHOICE" in + 1) + prompt_for_board_target + SELECTED_COMMAND_ARGS=(build-firmware "$SELECTED_TARGET") + return 0 + ;; + 2) + SELECTED_COMMAND_ARGS=(build-firmwares) + return 0 + ;; + 3) + SELECTED_COMMAND_ARGS=(build-repeater-firmwares) + return 0 + ;; + 4) + SELECTED_COMMAND_ARGS=(build-companion-firmwares) + return 0 + ;; + 5) + SELECTED_COMMAND_ARGS=(build-room-server-firmwares) + return 0 + ;; + esac + done +} + +prompt_for_debug_build_settings() { + echo "Set debug build options:" + prompt_on_off_choice "Mesh debug (MESH_DEBUG)" "off" + MESHDEBUG_OVERRIDE="$MENU_CHOICE" + + prompt_on_off_choice "Packet logging (MESH_PACKET_LOGGING)" "off" + PACKET_LOGGING_OVERRIDE="$MENU_CHOICE" + + echo "Using debug options: meshdebug=${MESHDEBUG_OVERRIDE}, packet_logging=${PACKET_LOGGING_OVERRIDE}" +} + +clear_radio_overrides() { + RADIO_SETTING_TITLE="" + RADIO_FREQ_OVERRIDE="" + RADIO_BW_OVERRIDE="" + RADIO_SF_OVERRIDE="" + RADIO_CR_OVERRIDE="" +} + +clear_firmware_profile_overrides() { + FIRMWARE_PROFILE_OVERRIDE="" +} + +set_radio_overrides() { + RADIO_SETTING_TITLE=$1 + RADIO_FREQ_OVERRIDE=$2 + RADIO_BW_OVERRIDE=$3 + RADIO_SF_OVERRIDE=$4 + RADIO_CR_OVERRIDE=$5 +} + +fetch_suggested_radio_settings() { + python3 - "$RADIO_SETTINGS_API_URL" <<'PY' +import json +import sys +import urllib.request + +url = sys.argv[1] +with urllib.request.urlopen(url, timeout=8) as response: + payload = json.load(response) + +entries = ( + payload.get("config", {}) + .get("suggested_radio_settings", {}) + .get("entries", []) +) + +for entry in entries: + title = str(entry.get("title", "")).strip() + description = str(entry.get("description", "")).strip() + freq = str(entry.get("frequency", "")).strip() + sf = str(entry.get("spreading_factor", "")).strip() + bw = str(entry.get("bandwidth", "")).strip() + cr = str(entry.get("coding_rate", "")).strip() + if title and freq and sf and bw and cr: + print("\t".join([title, description, freq, bw, sf, cr])) +PY +} + +is_valid_custom_radio_bandwidth() { + python3 - "$1" <<'PY' +import sys + +allowed = [7.81, 10.42, 15.63, 20.83, 31.25, 41.67, 62.5, 125.0, 250.0, 500.0] +try: + value = float(sys.argv[1]) +except Exception: + raise SystemExit(1) + +raise SystemExit(0 if any(abs(value - option) < 1e-6 for option in allowed) else 1) +PY +} + +prompt_for_custom_radio_setting() { + local freq + local sf + local bw + local cr + + echo + echo "Custom radio settings:" + + while true; do + read -r -p "Center frequency (MHz, e.g. 915.000): " freq + if [[ "$freq" =~ ^[0-9]+([.][0-9]+)?$ ]]; then + break + fi + echo "Please enter a numeric MHz value (e.g. 915.000)." + done + + echo "Spreading factor options: 5, 6, 7, 8, 9, 10, 11, 12" + while true; do + read -r -p "SF (5-12): " sf + if [[ "$sf" =~ ^[0-9]+$ ]] && [ "$sf" -ge 5 ] && [ "$sf" -le 12 ]; then + break + fi + echo "Please enter 5, 6, 7, 8, 9, 10, 11, or 12." + done + + echo "Bandwidth options (kHz): 7.81 10.42 15.63 20.83 31.25 41.67 62.5 125 250 500" + while true; do + read -r -p "BW (kHz): " bw + if [[ "$bw" =~ ^[0-9]+([.][0-9]+)?$ ]] && is_valid_custom_radio_bandwidth "$bw"; then + break + fi + echo "Please enter one of: 7.81 10.42 15.63 20.83 31.25 41.67 62.5 125 250 500." + done + + echo "Coding rate options: CR5, CR6, CR7, CR8" + while true; do + read -r -p "CR (5-8): " cr + if [[ "$cr" =~ ^[0-9]+$ ]] && [ "$cr" -ge 5 ] && [ "$cr" -le 8 ]; then + break + fi + echo "Please enter 5, 6, 7, or 8." + done + + set_radio_overrides "Custom" "$freq" "$bw" "$sf" "$cr" +} + +prompt_for_cascadia_profile_enable() { + clear_firmware_profile_overrides + echo + echo "Cascadia profile changes:" + echo " - rxdelay: 1" + echo " - agc.reset.interval: 8" + echo " - advert.interval: 0" + echo " - flood.advert.interval: 83" + echo " - multi.acks: 1" + echo " - path.hash.mode: 2" + echo " - loop.detect: minimal" + echo " - powersaving: on" + prompt_on_off_choice "Enable Cascadia profile overrides" "on" + if [ "$MENU_CHOICE" == "on" ]; then + FIRMWARE_PROFILE_OVERRIDE="cascadia" + echo "Using firmware profile override: ${FIRMWARE_PROFILE_OVERRIDE}" + return 0 + fi + + echo "Using target default firmware profile settings." + return 0 +} + +prompt_for_radio_build_settings() { + local -a preset_rows=() + local -a options=("Keep target defaults (no radio override)") + local row + local title + local description + local freq + local bw + local sf + local cr + local preset_index + local choice_index + local custom_index + + clear_radio_overrides + + if mapfile -t preset_rows < <(fetch_suggested_radio_settings); then + for row in "${preset_rows[@]}"; do + IFS=$'\t' read -r title description freq bw sf cr <<< "$row" + options+=("${title}: ${description}") + done + else + echo "Could not fetch radio presets from ${RADIO_SETTINGS_API_URL}." + preset_rows=() + fi + + options+=("Custom") + custom_index=${#options[@]} + + echo "Set radio build options:" + while true; do + print_numbered_menu "${options[@]}" + prompt_menu_choice "Radio setting" "${#options[@]}" + if [ "$MENU_CHOICE" == "QUIT" ]; then + echo "Cancelled." + exit 1 + fi + + choice_index=$MENU_CHOICE + if [ "$choice_index" -eq 1 ]; then + echo "Using target default radio settings." + return 0 + fi + + if [ "$choice_index" -eq "$custom_index" ]; then + prompt_for_custom_radio_setting + echo "Using radio setting: ${RADIO_SETTING_TITLE} (${RADIO_FREQ_OVERRIDE}MHz / SF${RADIO_SF_OVERRIDE} / BW${RADIO_BW_OVERRIDE} / CR${RADIO_CR_OVERRIDE})" + return 0 + fi + + preset_index=$((choice_index - 2)) + if [ "$preset_index" -ge 0 ] && [ "$preset_index" -lt "${#preset_rows[@]}" ]; then + IFS=$'\t' read -r title description freq bw sf cr <<< "${preset_rows[$preset_index]}" + set_radio_overrides "$title" "$freq" "$bw" "$sf" "$cr" + echo "Using radio setting: ${RADIO_SETTING_TITLE} (${RADIO_FREQ_OVERRIDE}MHz / SF${RADIO_SF_OVERRIDE} / BW${RADIO_BW_OVERRIDE} / CR${RADIO_CR_OVERRIDE})" + return 0 + fi + done +} + get_env_metadata() { local env_name=$1 local trimmed_env_name @@ -394,7 +675,7 @@ prompt_for_board_target() { mapfile -t boards < <(printf '%s\n' "${boards[@]}" | sort_lines_case_insensitive) - echo "No command provided. Select a board family:" + echo "Select a board family:" while true; do print_numbered_menu "${boards[@]}" prompt_menu_choice "Board selection" "${#boards[@]}" @@ -523,6 +804,40 @@ disable_debug_flags() { fi } +apply_debug_overrides() { + case "${MESHDEBUG_OVERRIDE,,}" in + on) + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -UMESH_DEBUG -DMESH_DEBUG=1" + ;; + off) + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -UMESH_DEBUG" + ;; + esac + + case "${PACKET_LOGGING_OVERRIDE,,}" in + on) + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -UMESH_PACKET_LOGGING -DMESH_PACKET_LOGGING=1" + ;; + off) + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -UMESH_PACKET_LOGGING" + ;; + esac +} + +apply_radio_overrides() { + if [ -n "$RADIO_FREQ_OVERRIDE" ] && [ -n "$RADIO_BW_OVERRIDE" ] && [ -n "$RADIO_SF_OVERRIDE" ] && [ -n "$RADIO_CR_OVERRIDE" ]; then + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -ULORA_FREQ -ULORA_BW -ULORA_SF -ULORA_CR -DLORA_FREQ=${RADIO_FREQ_OVERRIDE} -DLORA_BW=${RADIO_BW_OVERRIDE} -DLORA_SF=${RADIO_SF_OVERRIDE} -DLORA_CR=${RADIO_CR_OVERRIDE}" + fi +} + +apply_firmware_profile_overrides() { + case "${FIRMWARE_PROFILE_OVERRIDE,,}" in + cascadia) + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DCASCADIA_PROFILE=1 -DDEFAULT_RX_DELAY_BASE=1.0f -DDEFAULT_LOOP_DETECT=1 -DDEFAULT_POWERSAVING_ENABLED=1 -DDEFAULT_AGC_RESET_INTERVAL=2 -DDEFAULT_ADVERT_INTERVAL=0 -DDEFAULT_FLOOD_ADVERT_INTERVAL=83 -DDEFAULT_MULTI_ACKS=1 -DDEFAULT_PATH_HASH_MODE=2" + ;; + esac +} + copy_build_output() { local source_path=$1 local output_path=$2 @@ -616,6 +931,9 @@ build_firmware() { export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DFIRMWARE_BUILD_DATE='\"${firmware_build_date}\"' -DFIRMWARE_VERSION='\"${firmware_version_string}\"'" disable_debug_flags + apply_debug_overrides + apply_radio_overrides + apply_firmware_profile_overrides pio run -e "$env_name" collect_build_artifacts "$env_name" "$env_platform" "$firmware_filename" @@ -734,8 +1052,11 @@ main() { init_project_context if [ $# -eq 0 ]; then - prompt_for_board_target - set -- build-firmware "$SELECTED_TARGET" + prompt_for_build_mode + prompt_for_debug_build_settings + prompt_for_radio_build_settings + prompt_for_cascadia_profile_enable + set -- "${SELECTED_COMMAND_ARGS[@]}" fi prepare_output_dir diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 22862518..26534931 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -20,6 +20,31 @@ #define LORA_TX_POWER 20 #endif +#ifndef DEFAULT_RX_DELAY_BASE + #define DEFAULT_RX_DELAY_BASE 0.0f +#endif +#ifndef DEFAULT_LOOP_DETECT + #define DEFAULT_LOOP_DETECT LOOP_DETECT_OFF +#endif +#ifndef DEFAULT_POWERSAVING_ENABLED + #define DEFAULT_POWERSAVING_ENABLED 0 +#endif +#ifndef DEFAULT_AGC_RESET_INTERVAL + #define DEFAULT_AGC_RESET_INTERVAL 0 +#endif +#ifndef DEFAULT_ADVERT_INTERVAL + #define DEFAULT_ADVERT_INTERVAL 1 +#endif +#ifndef DEFAULT_FLOOD_ADVERT_INTERVAL + #define DEFAULT_FLOOD_ADVERT_INTERVAL 12 +#endif +#ifndef DEFAULT_MULTI_ACKS + #define DEFAULT_MULTI_ACKS 0 +#endif +#ifndef DEFAULT_PATH_HASH_MODE + #define DEFAULT_PATH_HASH_MODE 0 +#endif + #ifndef ADVERT_NAME #define ADVERT_NAME "repeater" #endif @@ -2034,7 +2059,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc // defaults memset(&_prefs, 0, sizeof(_prefs)); _prefs.airtime_factor = 1.0; - _prefs.rx_delay_base = 0.0f; // turn off by default, was 10.0; + _prefs.rx_delay_base = DEFAULT_RX_DELAY_BASE; _prefs.tx_delay_factor = 0.5f; // was 0.25f _prefs.direct_tx_delay_factor = 0.3f; // was 0.2 _prefs.direct_retry_recent_enabled = 1; @@ -2063,10 +2088,15 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.bw = LORA_BW; _prefs.cr = LORA_CR; _prefs.tx_power_dbm = LORA_TX_POWER; - _prefs.advert_interval = 1; // default to 2 minutes for NEW installs - _prefs.flood_advert_interval = 12; // 12 hours + _prefs.advert_interval = DEFAULT_ADVERT_INTERVAL; + _prefs.flood_advert_interval = DEFAULT_FLOOD_ADVERT_INTERVAL; _prefs.flood_max = 64; _prefs.interference_threshold = 0; // disabled + _prefs.agc_reset_interval = DEFAULT_AGC_RESET_INTERVAL; + _prefs.multi_acks = DEFAULT_MULTI_ACKS; + _prefs.path_hash_mode = DEFAULT_PATH_HASH_MODE; + _prefs.loop_detect = DEFAULT_LOOP_DETECT; + _prefs.powersaving_enabled = DEFAULT_POWERSAVING_ENABLED; // bridge defaults _prefs.bridge_enabled = 1; // enabled @@ -2105,6 +2135,16 @@ void MyMesh::begin(FILESYSTEM *fs) { _fs = fs; // load persisted prefs _cli.loadPrefs(_fs); +#ifdef CASCADIA_PROFILE + _prefs.rx_delay_base = DEFAULT_RX_DELAY_BASE; + _prefs.agc_reset_interval = DEFAULT_AGC_RESET_INTERVAL; + _prefs.advert_interval = DEFAULT_ADVERT_INTERVAL; + _prefs.flood_advert_interval = DEFAULT_FLOOD_ADVERT_INTERVAL; + _prefs.multi_acks = DEFAULT_MULTI_ACKS; + _prefs.path_hash_mode = DEFAULT_PATH_HASH_MODE; + _prefs.loop_detect = DEFAULT_LOOP_DETECT; + _prefs.powersaving_enabled = DEFAULT_POWERSAVING_ENABLED; +#endif acl.load(_fs, self_id); // TODO: key_store.begin(); region_map.load(_fs); From e1ba591a316cfc3537e4fe07c401eb28e29310f2 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 12 May 2026 16:37:26 -0700 Subject: [PATCH 073/214] build.sh: retry radio preset API fetch with browser headers --- build.sh | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/build.sh b/build.sh index ad8f4079..f8330cb5 100755 --- a/build.sh +++ b/build.sh @@ -300,11 +300,47 @@ fetch_suggested_radio_settings() { python3 - "$RADIO_SETTINGS_API_URL" <<'PY' import json import sys +import urllib.error import urllib.request url = sys.argv[1] -with urllib.request.urlopen(url, timeout=8) as response: - payload = json.load(response) +header_sets = [ + { + "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", + "Accept": "application/json,text/plain,*/*", + "Accept-Language": "en-US,en;q=0.9", + "Referer": "https://meshcore.nz/", + "Origin": "https://meshcore.nz", + }, + { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0", + "Accept": "application/json,text/plain,*/*", + "Accept-Language": "en-US,en;q=0.9", + "Referer": "https://meshcore.nz/", + "Origin": "https://meshcore.nz", + }, +] + +payload = None +errors = [] + +for index, headers in enumerate(header_sets, start=1): + req = urllib.request.Request(url, headers=headers) + try: + with urllib.request.urlopen(req, timeout=8) as response: + payload = json.load(response) + break + except urllib.error.HTTPError as exc: + errors.append(f"attempt {index}: HTTP {exc.code}") + continue + except Exception as exc: + errors.append(f"attempt {index}: {type(exc).__name__}") + continue + +if payload is None: + summary = "; ".join(errors) if errors else "unknown error" + print(f"Failed to fetch radio presets from {url} ({summary})", file=sys.stderr) + raise SystemExit(1) entries = ( payload.get("config", {}) From f47b0872ac4961481d522fa28b58fca57d140687 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 12 May 2026 16:45:44 -0700 Subject: [PATCH 074/214] build.sh: fix radio override flags for PlatformIO ordering --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index f8330cb5..2cddb134 100755 --- a/build.sh +++ b/build.sh @@ -862,7 +862,7 @@ apply_debug_overrides() { apply_radio_overrides() { if [ -n "$RADIO_FREQ_OVERRIDE" ] && [ -n "$RADIO_BW_OVERRIDE" ] && [ -n "$RADIO_SF_OVERRIDE" ] && [ -n "$RADIO_CR_OVERRIDE" ]; then - export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -ULORA_FREQ -ULORA_BW -ULORA_SF -ULORA_CR -DLORA_FREQ=${RADIO_FREQ_OVERRIDE} -DLORA_BW=${RADIO_BW_OVERRIDE} -DLORA_SF=${RADIO_SF_OVERRIDE} -DLORA_CR=${RADIO_CR_OVERRIDE}" + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DLORA_FREQ=${RADIO_FREQ_OVERRIDE} -DLORA_BW=${RADIO_BW_OVERRIDE} -DLORA_SF=${RADIO_SF_OVERRIDE} -DLORA_CR=${RADIO_CR_OVERRIDE}" fi } From 6d7dfa6814b7f566010dcfe5aae14c6db47f058d Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 12 May 2026 17:33:13 -0700 Subject: [PATCH 075/214] build.sh: add interactive Wi-Fi build settings overrides --- build.sh | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 2cddb134..dd471bab 100755 --- a/build.sh +++ b/build.sh @@ -14,6 +14,9 @@ RADIO_BW_OVERRIDE="" RADIO_SF_OVERRIDE="" RADIO_CR_OVERRIDE="" FIRMWARE_PROFILE_OVERRIDE="" +WIFI_SSID_OVERRIDE="" +WIFI_PWD_OVERRIDE="" +WIFI_DEBUG_LOGGING_OVERRIDE="" ENV_VARIANT_SUFFIX_PATTERN='companion_radio_serial|companion_radio_wifi|companion_radio_usb|comp_radio_usb|companion_usb|companion_radio_ble|companion_ble|repeater_bridge_rs232_serial1|repeater_bridge_rs232_serial2|repeater_bridge_rs232|repeater_bridge_espnow|terminal_chat|room_server|room_svr|kiss_modem|sensor|repeatr|repeater' BOARD_MODIFIER_WITHOUT_DISPLAY="_without_display" @@ -61,7 +64,7 @@ Examples: Build firmware for the "RAK_4631_repeater" device target $ bash build.sh build-firmware RAK_4631_repeater -Run without arguments to choose an interactive build action/target, debug options, and radio settings +Run without arguments to choose an interactive build action/target, debug options, radio settings, and (when applicable) Wi-Fi settings $ bash build.sh Build all firmwares for device targets containing the string "RAK_4631" @@ -504,6 +507,81 @@ prompt_for_radio_build_settings() { done } +clear_wifi_overrides() { + WIFI_SSID_OVERRIDE="" + WIFI_PWD_OVERRIDE="" + WIFI_DEBUG_LOGGING_OVERRIDE="" +} + +is_wifi_build_target() { + local env_name=$1 + local is_wifi=1 + + shopt -s nocasematch + if [[ "$env_name" == *companion_radio_wifi* ]]; then + is_wifi=0 + fi + shopt -u nocasematch + + return "$is_wifi" +} + +selected_command_uses_wifi_target() { + case "${SELECTED_COMMAND_ARGS[0]:-}" in + build-firmware) + is_wifi_build_target "${SELECTED_COMMAND_ARGS[1]:-}" + return $? + ;; + *) + return 1 + ;; + esac +} + +prompt_for_wifi_build_settings() { + local -a options=( + "Keep target defaults (no Wi-Fi override)" + "Custom Wi-Fi SSID/password" + ) + local choice_index + + clear_wifi_overrides + + echo "Set Wi-Fi build options:" + while true; do + print_numbered_menu "${options[@]}" + prompt_menu_choice "Wi-Fi setting" "${#options[@]}" + if [ "$MENU_CHOICE" == "QUIT" ]; then + echo "Cancelled." + exit 1 + fi + + choice_index=$MENU_CHOICE + case "$choice_index" in + 1) + echo "Using target default Wi-Fi settings." + return 0 + ;; + 2) + read -r -p "Wi-Fi SSID: " WIFI_SSID_OVERRIDE + read -r -p "Wi-Fi password (blank allowed): " WIFI_PWD_OVERRIDE + prompt_on_off_choice "Wi-Fi debug logging (WIFI_DEBUG_LOGGING)" "off" + WIFI_DEBUG_LOGGING_OVERRIDE="$MENU_CHOICE" + echo "Using Wi-Fi overrides: ssid='${WIFI_SSID_OVERRIDE}', wifi_debug=${WIFI_DEBUG_LOGGING_OVERRIDE}" + return 0 + ;; + esac + done +} + +escape_cpp_string_literal() { + local value=$1 + + value=${value//\\/\\\\} + value=${value//\"/\\\"} + printf '%s' "$value" +} + get_env_metadata() { local env_name=$1 local trimmed_env_name @@ -874,6 +952,37 @@ apply_firmware_profile_overrides() { esac } +apply_wifi_overrides() { + local env_name=$1 + local ssid_escaped + local pwd_escaped + + if ! is_wifi_build_target "$env_name"; then + return 0 + fi + + if [ -n "$WIFI_SSID_OVERRIDE" ]; then + ssid_escaped=$(escape_cpp_string_literal "$WIFI_SSID_OVERRIDE") + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -D WIFI_SSID='\"${ssid_escaped}\"'" + fi + + if [ -n "$WIFI_SSID_OVERRIDE" ] || [ -n "$WIFI_PWD_OVERRIDE" ]; then + pwd_escaped=$(escape_cpp_string_literal "$WIFI_PWD_OVERRIDE") + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -D WIFI_PWD='\"${pwd_escaped}\"'" + fi + + case "${WIFI_DEBUG_LOGGING_OVERRIDE,,}" in + on) + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -D WIFI_DEBUG_LOGGING=1" + ;; + off) + if [ -n "$WIFI_SSID_OVERRIDE" ] || [ -n "$WIFI_PWD_OVERRIDE" ]; then + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -D WIFI_DEBUG_LOGGING=0" + fi + ;; + esac +} + copy_build_output() { local source_path=$1 local output_path=$2 @@ -970,6 +1079,7 @@ build_firmware() { apply_debug_overrides apply_radio_overrides apply_firmware_profile_overrides + apply_wifi_overrides "$env_name" pio run -e "$env_name" collect_build_artifacts "$env_name" "$env_platform" "$firmware_filename" @@ -1092,6 +1202,11 @@ main() { prompt_for_debug_build_settings prompt_for_radio_build_settings prompt_for_cascadia_profile_enable + if selected_command_uses_wifi_target; then + prompt_for_wifi_build_settings + else + clear_wifi_overrides + fi set -- "${SELECTED_COMMAND_ARGS[@]}" fi From 815bd2c1c3784adf4ef22bed9938f8c63a1c7ab8 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 12 May 2026 22:42:18 -0700 Subject: [PATCH 076/214] build.sh: accept 1/0 for on/off prompts --- build.sh | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/build.sh b/build.sh index dd471bab..8ec35dae 100755 --- a/build.sh +++ b/build.sh @@ -203,22 +203,33 @@ prompt_menu_choice() { prompt_on_off_choice() { local prompt_label=$1 local default_choice=$2 + local normalized_default local choice + normalized_default=${default_choice,,} + case "$normalized_default" in + 1) normalized_default="on" ;; + 0) normalized_default="off" ;; + esac + while true; do - read -r -p "${prompt_label} [on/off] (default: ${default_choice}): " choice + read -r -p "${prompt_label} [on/off/1/0] (default: ${normalized_default}): " choice choice=${choice,,} if [ -z "$choice" ]; then - choice=$default_choice + choice=$normalized_default fi case "$choice" in - on|off) - MENU_CHOICE="$choice" + on|1) + MENU_CHOICE="on" + return 0 + ;; + off|0) + MENU_CHOICE="off" return 0 ;; *) - echo "Invalid selection. Choose 'on' or 'off'." + echo "Invalid selection. Choose 'on'/'off' or 1/0." ;; esac done From 1ded5b34e6f74a4742e52453bb75fe562ed65b95 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 12 May 2026 22:43:50 -0700 Subject: [PATCH 077/214] build.sh: show on-off prompts as on(1)/off(0) --- build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sh b/build.sh index 8ec35dae..f30af6da 100755 --- a/build.sh +++ b/build.sh @@ -213,7 +213,7 @@ prompt_on_off_choice() { esac while true; do - read -r -p "${prompt_label} [on/off/1/0] (default: ${normalized_default}): " choice + read -r -p "${prompt_label} [on(1)/off(0)] (default: ${normalized_default}): " choice choice=${choice,,} if [ -z "$choice" ]; then choice=$normalized_default @@ -229,7 +229,7 @@ prompt_on_off_choice() { return 0 ;; *) - echo "Invalid selection. Choose 'on'/'off' or 1/0." + echo "Invalid selection. Choose on(1) or off(0)." ;; esac done From 0b245d51bff4562f11d35524301cb4fe211f55dc Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 22 May 2026 15:12:41 -0700 Subject: [PATCH 078/214] Update Halo Keymind settings docs --- docs/halo_keymind_settings.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/halo_keymind_settings.md b/docs/halo_keymind_settings.md index 1be3ddb0..bcd31c15 100644 --- a/docs/halo_keymind_settings.md +++ b/docs/halo_keymind_settings.md @@ -75,7 +75,6 @@ set flood.retry.ignore none | Setting | What it does | How to use | Example | | --- | --- | --- | --- | | `recent.repeater` | Shows or seeds the recent repeater prefix/SNR table used by direct retry and bridge freshness checks. | `get recent.repeater`, `get recent.repeater `, `set recent.repeater ` | `set recent.repeater A1B2C3 -8.5` | -| `radio.fem.rxgain` | Controls the external LoRa FEM receive-path LNA where the board supports it. | `get radio.fem.rxgain`, `set radio.fem.rxgain on/off` | `set radio.fem.rxgain on` | ## Recent Repeater Table From bf389b34a13970e551ff5d45414801fb4fd388fa Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 22 May 2026 15:15:36 -0700 Subject: [PATCH 079/214] Document Keymind branch settings --- docs/halo_keymind_settings.md | 90 +++++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 10 deletions(-) diff --git a/docs/halo_keymind_settings.md b/docs/halo_keymind_settings.md index bcd31c15..72fd0db7 100644 --- a/docs/halo_keymind_settings.md +++ b/docs/halo_keymind_settings.md @@ -1,11 +1,10 @@ # Halo and Keymind Branch Settings -This file covers only CLI settings added by the Halo or Keymind branches. Use -`docs/cli_commands.md` for the general MeshCore CLI. +This file covers only CLI settings and helper commands added by the Halo or +Keymind branches. Use `docs/cli_commands.md` for the general MeshCore CLI. ## Quick Start - ```text set retry.preset rooftop set direct.retry.heard on @@ -25,7 +24,7 @@ get flood.retry.prefixes get flood.retry.ignore ``` -Use prefixes from the analyzer or neighbors list or `get recent.repeater` after the repeater has been online for a few hours. +Use prefixes from the analyzer or neighbors list or `get recent.repeater` after the repeater has been online for a few hours. ## Common Examples @@ -36,23 +35,23 @@ set flood.retry.advert off get flood.retry.advert ``` -Ignore a repeater as a successful flood retry echo: -Use this if you have a car repeater and a house repeater; have the house ignore the car. +Ignore a repeater as a successful flood retry echo: +Use this if you have a car repeater and a house repeater; have the house ignore the car. ```text set flood.retry.ignore 71CE82,C7618C get flood.retry.ignore ``` -Only accept specific downstream relays as flood retry success: -You're in a hole and need to hit a mountain top repeater to get out; keep trying till one you see one of these send out your packet. +Only accept specific downstream relays as flood retry success: +You're in a hole and need to hit a mountain top repeater to get out; keep trying till one you see one of these send out your packet. ```text set flood.retry.prefixes A58296,860CCA,425E5C get flood.retry.prefixes ``` -Bridge two groups of repeaters: +Bridge two groups of repeaters: ```text set flood.retry.bridge on @@ -74,7 +73,44 @@ set flood.retry.ignore none | Setting | What it does | How to use | Example | | --- | --- | --- | --- | -| `recent.repeater` | Shows or seeds the recent repeater prefix/SNR table used by direct retry and bridge freshness checks. | `get recent.repeater`, `get recent.repeater `, `set recent.repeater ` | `set recent.repeater A1B2C3 -8.5` | +| `battery.alert` | Sends opt-in low-battery warnings to `#repeaters`. | `get battery.alert`, `set battery.alert on/off` | `set battery.alert on` | +| `battery.alert.low` | Warning threshold percentage. Must be greater than `battery.alert.critical`. | `get battery.alert.low`, `set battery.alert.low <1-100>` | `set battery.alert.low 20` | +| `battery.alert.critical` | Critical threshold percentage. Critical warnings repeat more often. | `get battery.alert.critical`, `set battery.alert.critical <0-99>` | `set battery.alert.critical 10` | +| `recent.repeater` | Shows, seeds, or clears the recent repeater prefix/SNR table used by direct retry and bridge freshness checks. | `get recent.repeater`, `get recent.repeater `, `set recent.repeater `, `clear recent.repeater` | `set recent.repeater A1B2C3 -8.5` | +| `outpath` | Overrides the primary direct route used for replies to the current remote client. | `get outpath`, `set outpath `, `set outpath clear`, `set outpath flood` | `set outpath A1B2C3,D4E5F6` | +| `altpath` | Optional second direct route used for duplicate response attempts to the current remote client. | `get altpath`, `set altpath `, `set altpath clear` | `set altpath A1B2C3,D4E5F6` | + +## Other Keymind Commands + +| Command | What it does | How to use | Example | +| --- | --- | --- | --- | +| `send text.flood` | Sends a `#repeaters` flood text message formatted as `: `. | `send text.flood ` | `send text.flood checking ridge link` | + +## Battery Alerts + +Battery alerts are off by default. When enabled, the repeater checks once per +minute and sends a flood text warning to `#repeaters` when voltage is above +`1 V` and the estimated battery percent is below `battery.alert.low`. + +Warnings repeat every `24` hours, or every `12` hours when the estimate is +below `battery.alert.critical`. + +Defaults: + +| Setting | Default | +| --- | ---: | +| `battery.alert` | `off` | +| `battery.alert.low` | `20` | +| `battery.alert.critical` | `10` | + +Example: + +```text +set battery.alert.low 20 +set battery.alert.critical 10 +set battery.alert on +get battery.alert +``` ## Recent Repeater Table @@ -87,6 +123,7 @@ Show learned rows: ```text get recent.repeater get recent.repeater 2 +get recent.repeater page 3 ``` Seed or correct a prefix: @@ -95,9 +132,42 @@ Seed or correct a prefix: set recent.repeater A1B2C3 8.5 ``` +Clear learned and manually seeded rows: + +```text +clear recent.repeater +``` + Rows are sorted by prefix width, then SNR. A full direct retry failure lowers the matching row by `0.25 dB`. +Serial CLI pages contain up to `128` rows. Remote LoRa CLI pages contain up to +`7` rows. + +## Direct Path Overrides + +`outpath` and `altpath` apply to the current remote client ACL entry. They need +remote client context, so they are not useful from the local serial CLI. + +Set paths with comma-separated hop hashes. Each hop must be `2`, `4`, or `6` +hex characters, and all hops in one path must use the same width. + +```text +get outpath +set outpath A1B2C3,D4E5F6 +set outpath clear +set outpath flood + +get altpath +set altpath A1B2C3,D4E5F6 +set altpath clear +``` + +`set outpath clear` forgets the override and lets normal path discovery fill it +again. `set outpath flood` forces replies to use flood packets until the client +logs in again. `altpath` sends a duplicate reply over a second direct route; +clearing it returns replies to a single route. + ## Direct Retry Settings Direct retry applies to direct-routed packets. A queued resend is canceled when the next-hop echo is heard. From 0f0f91c9ffea4b24a005ce8a6c8435f60e37ba63 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Wed, 27 May 2026 09:41:29 +0700 Subject: [PATCH 080/214] Fixed directive and added isReadBusy for WiFi companion. --- examples/companion_radio/main.cpp | 2 +- src/helpers/esp32/SerialWifiInterface.cpp | 4 ++++ src/helpers/esp32/SerialWifiInterface.h | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index bb350fd0..a8daa0c8 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -265,7 +265,7 @@ void loop() { if (!the_mesh.hasPendingWork()) { #if defined(NRF52_PLATFORM) board.sleep(0); // nrf ignores seconds param, sleeps whenever possible -#else if defined(ESP32_PLATFORM) +#elif defined(ESP32_PLATFORM) if (!serial_interface.isReadBusy() && !serial_interface.isWriteBusy()) { // BLE is not busy vTaskDelay(pdMS_TO_TICKS(10)); // attempt to sleep } diff --git a/src/helpers/esp32/SerialWifiInterface.cpp b/src/helpers/esp32/SerialWifiInterface.cpp index 462e3ecc..bdecb1a9 100644 --- a/src/helpers/esp32/SerialWifiInterface.cpp +++ b/src/helpers/esp32/SerialWifiInterface.cpp @@ -39,6 +39,10 @@ size_t SerialWifiInterface::writeFrame(const uint8_t src[], size_t len) { return 0; } +bool SerialWifiInterface::isReadBusy() const { + return false; +} + bool SerialWifiInterface::isWriteBusy() const { return false; } diff --git a/src/helpers/esp32/SerialWifiInterface.h b/src/helpers/esp32/SerialWifiInterface.h index 19291497..1ff1d83d 100644 --- a/src/helpers/esp32/SerialWifiInterface.h +++ b/src/helpers/esp32/SerialWifiInterface.h @@ -52,6 +52,7 @@ public: bool isEnabled() const override { return _isEnabled; } bool isConnected() const override; + bool isReadBusy() const override; bool isWriteBusy() const override; size_t writeFrame(const uint8_t src[], size_t len) override; From 00d445a269451e2af0bd54365346c9a8b584f300 Mon Sep 17 00:00:00 2001 From: Sefinek Date: Thu, 28 May 2026 15:44:56 +0200 Subject: [PATCH 081/214] fix: fix typos in source code comments --- examples/companion_radio/DataStore.cpp | 2 +- src/Mesh.h | 2 +- src/Packet.h | 2 +- src/helpers/bridges/ESPNowBridge.cpp | 2 +- src/helpers/stm32/InternalFileSystem.cpp | 6 +++--- src/helpers/ui/OLEDDisplay.cpp | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index c7988bb3..dda0a84e 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -541,7 +541,7 @@ bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src uint32_t pos = 0, found_pos = 0; uint32_t min_timestamp = 0xFFFFFFFF; - // search for matching key OR evict by oldest timestmap + // search for matching key OR evict by oldest timestamp BlobRec tmp; file.seek(0); while (file.read((uint8_t *) &tmp, sizeof(tmp)) == sizeof(tmp)) { diff --git a/src/Mesh.h b/src/Mesh.h index d53d6d25..d3c1f73c 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -100,7 +100,7 @@ protected: * \param auth_code a code to authenticate the packet * \param flags zero for now * \param path_snrs single byte SNR*4 for each hop in the path - * \param path_hashes hashes if each repeater in the path + * \param path_hashes hashes of each repeater in the path * \param path_len length of the path_snrs[] and path_hashes[] arrays */ virtual void onTraceRecv(Packet* packet, uint32_t tag, uint32_t auth_code, uint8_t flags, const uint8_t* path_snrs, const uint8_t* path_hashes, uint8_t path_len) { } diff --git a/src/Packet.h b/src/Packet.h index 0886a06c..c19d9e9d 100644 --- a/src/Packet.h +++ b/src/Packet.h @@ -25,7 +25,7 @@ namespace mesh { #define PAYLOAD_TYPE_GRP_DATA 0x06 // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: data_type(uint16), data_len, blob) #define PAYLOAD_TYPE_ANON_REQ 0x07 // generic request (prefixed with dest_hash, ephemeral pub_key, MAC) (enc data: ...) #define PAYLOAD_TYPE_PATH 0x08 // returned path (prefixed with dest/src hashes, MAC) (enc data: path, extra) -#define PAYLOAD_TYPE_TRACE 0x09 // trace a path, collecting SNI for each hop +#define PAYLOAD_TYPE_TRACE 0x09 // trace a path, collecting SNR for each hop #define PAYLOAD_TYPE_MULTIPART 0x0A // packet is one of a set of packets #define PAYLOAD_TYPE_CONTROL 0x0B // a control/discovery packet //... diff --git a/src/helpers/bridges/ESPNowBridge.cpp b/src/helpers/bridges/ESPNowBridge.cpp index b9eb1c10..808e9df4 100644 --- a/src/helpers/bridges/ESPNowBridge.cpp +++ b/src/helpers/bridges/ESPNowBridge.cpp @@ -32,7 +32,7 @@ void ESPNowBridge::begin() { // Initialize WiFi in station mode WiFi.mode(WIFI_STA); - // Set wifi channel + // Set Wi-Fi channel if (esp_wifi_set_channel(_prefs->bridge_channel, WIFI_SECOND_CHAN_NONE) != ESP_OK) { BRIDGE_DEBUG_PRINTLN("Error setting WIFI channel to %d\n", _prefs->bridge_channel); return; diff --git a/src/helpers/stm32/InternalFileSystem.cpp b/src/helpers/stm32/InternalFileSystem.cpp index dc032eb9..6a7d7064 100644 --- a/src/helpers/stm32/InternalFileSystem.cpp +++ b/src/helpers/stm32/InternalFileSystem.cpp @@ -37,7 +37,7 @@ static int _internal_flash_read(const struct lfs_config *c, lfs_block_t block, l } // Program a region in a block. The block must have previously -// been erased. Negative error codes are propogated to the user. +// been erased. Negative error codes are propagated to the user. // May return LFS_ERR_CORRUPT if the block should be considered bad. static int _internal_flash_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) { @@ -62,7 +62,7 @@ static int _internal_flash_prog(const struct lfs_config *c, lfs_block_t block, l // Erase a block. A block must be erased before being programmed. // The state of an erased block is undefined. Negative error codes -// are propogated to the user. +// are propagated to the user. // May return LFS_ERR_CORRUPT if the block should be considered bad. static int _internal_flash_erase(const struct lfs_config *c, lfs_block_t block) { @@ -87,7 +87,7 @@ static int _internal_flash_erase(const struct lfs_config *c, lfs_block_t block) } // Sync the state of the underlying block device. Negative error codes -// are propogated to the user. +// are propagated to the user. static int _internal_flash_sync(const struct lfs_config *c) { return LFS_ERR_OK; // don't need sync diff --git a/src/helpers/ui/OLEDDisplay.cpp b/src/helpers/ui/OLEDDisplay.cpp index 19101344..aa11ce1a 100644 --- a/src/helpers/ui/OLEDDisplay.cpp +++ b/src/helpers/ui/OLEDDisplay.cpp @@ -1155,7 +1155,7 @@ void OLEDDisplay::setFontTableLookupFunction(FontTableLookupFunction function) { char DefaultFontTableLookup(const uint8_t ch) { // UTF-8 to font table index converter - // Code form http://playground.arduino.cc/Main/Utf8ascii + // Code from http://playground.arduino.cc/Main/Utf8ascii static uint8_t LASTCHAR; if (ch < 128) { // Standard ASCII-set 0..0x7F handling @@ -1166,7 +1166,7 @@ char DefaultFontTableLookup(const uint8_t ch) { uint8_t last = LASTCHAR; // get last char LASTCHAR = ch; - switch (last) { // conversion depnding on first UTF8-character + switch (last) { // conversion depending on first UTF8-character case 0xC2: return (uint8_t) ch; case 0xC3: return (uint8_t) (ch | 0xC0); case 0x82: if (ch == 0xAC) return (uint8_t) 0x80; // special case Euro-symbol From bbd37f53a858e3daefeafde13b54941bdde12c07 Mon Sep 17 00:00:00 2001 From: Sefinek Date: Thu, 28 May 2026 16:01:59 +0200 Subject: [PATCH 082/214] ci: update GitHub Actions and Python version, fix ruby-version typo --- .github/actions/setup-build-environment/action.yml | 6 +++--- .github/workflows/build-companion-firmwares.yml | 6 +++--- .github/workflows/build-repeater-firmwares.yml | 6 +++--- .github/workflows/build-room-server-firmwares.yml | 6 +++--- .github/workflows/github-pages.yml | 8 ++++---- .github/workflows/pr-build-check.yml | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/actions/setup-build-environment/action.yml b/.github/actions/setup-build-environment/action.yml index 2ba7617e..02aaf424 100644 --- a/.github/actions/setup-build-environment/action.yml +++ b/.github/actions/setup-build-environment/action.yml @@ -4,7 +4,7 @@ runs: steps: - name: Init Cache - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: | ~/.cache/pip @@ -12,9 +12,9 @@ runs: key: ${{ runner.os }}-pio - name: Install Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: - python-version: '3.11' + python-version: '3.13' - name: Install PlatformIO shell: bash diff --git a/.github/workflows/build-companion-firmwares.yml b/.github/workflows/build-companion-firmwares.yml index 721076a1..771fa6d5 100644 --- a/.github/workflows/build-companion-firmwares.yml +++ b/.github/workflows/build-companion-firmwares.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Clone Repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Build Environment uses: ./.github/actions/setup-build-environment @@ -27,13 +27,13 @@ jobs: run: /usr/bin/env bash build.sh build-companion-firmwares - name: Upload Workflow Artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: companion-firmwares path: out - name: Create Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@v3 if: startsWith(github.ref, 'refs/tags/') with: name: Companion Firmware ${{ env.GIT_TAG_VERSION }} diff --git a/.github/workflows/build-repeater-firmwares.yml b/.github/workflows/build-repeater-firmwares.yml index f12bd829..3185d4b2 100644 --- a/.github/workflows/build-repeater-firmwares.yml +++ b/.github/workflows/build-repeater-firmwares.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Clone Repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Build Environment uses: ./.github/actions/setup-build-environment @@ -27,13 +27,13 @@ jobs: run: /usr/bin/env bash build.sh build-repeater-firmwares - name: Upload Workflow Artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: repeater-firmwares path: out - name: Create Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@v3 if: startsWith(github.ref, 'refs/tags/') with: name: Repeater Firmware ${{ env.GIT_TAG_VERSION }} diff --git a/.github/workflows/build-room-server-firmwares.yml b/.github/workflows/build-room-server-firmwares.yml index a488af6a..127095a8 100644 --- a/.github/workflows/build-room-server-firmwares.yml +++ b/.github/workflows/build-room-server-firmwares.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Clone Repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Build Environment uses: ./.github/actions/setup-build-environment @@ -27,13 +27,13 @@ jobs: run: /usr/bin/env bash build.sh build-room-server-firmwares - name: Upload Workflow Artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: room-server-firmwares path: out - name: Create Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@v3 if: startsWith(github.ref, 'refs/tags/') with: name: Room Server Firmware ${{ env.GIT_TAG_VERSION }} diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 5fd2734b..9aa5fc0b 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -15,12 +15,12 @@ jobs: steps: - name: Checkout Repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: - ruby-version: 3.x + python-version: '3.13' - name: Build run: | @@ -28,7 +28,7 @@ jobs: mkdocs build - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} cname: docs.meshcore.io diff --git a/.github/workflows/pr-build-check.yml b/.github/workflows/pr-build-check.yml index 37f3701b..3f85faab 100644 --- a/.github/workflows/pr-build-check.yml +++ b/.github/workflows/pr-build-check.yml @@ -42,7 +42,7 @@ jobs: steps: - name: Clone Repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Build Environment uses: ./.github/actions/setup-build-environment From c67548347cafbc734049b65e5050084192f609f1 Mon Sep 17 00:00:00 2001 From: Sefinek Date: Thu, 28 May 2026 16:10:46 +0200 Subject: [PATCH 083/214] ci: pin peaceiris/actions-gh-pages to v4.1.0 for Node.js 24 support --- .github/workflows/github-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 9aa5fc0b..b01ddc93 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -28,7 +28,7 @@ jobs: mkdocs build - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v4 + uses: peaceiris/actions-gh-pages@v4.1.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} cname: docs.meshcore.io From 4bf391f5c38f4d65cc595e8ba747e1a2e5cebaa6 Mon Sep 17 00:00:00 2001 From: Sefinek Date: Thu, 28 May 2026 16:38:39 +0200 Subject: [PATCH 084/214] fix: fix typos in source code comments --- src/Mesh.h | 4 ++-- src/helpers/sensors/LPPDataHelpers.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mesh.h b/src/Mesh.h index d3c1f73c..a4967c1d 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -212,12 +212,12 @@ public: void sendDirect(Packet* packet, const uint8_t* path, uint8_t path_len, uint32_t delay_millis=0); /** - * \brief send a locally-generated Packet to just neigbor nodes (zero hops) + * \brief send a locally-generated Packet to just neighbor nodes (zero hops) */ void sendZeroHop(Packet* packet, uint32_t delay_millis=0); /** - * \brief send a locally-generated Packet to just neigbor nodes (zero hops), with specific transort codes + * \brief send a locally-generated Packet to just neighbor nodes (zero hops), with specific transport codes * \param transport_codes array of 2 codes to attach to packet */ void sendZeroHop(Packet* packet, uint16_t* transport_codes, uint32_t delay_millis=0); diff --git a/src/helpers/sensors/LPPDataHelpers.h b/src/helpers/sensors/LPPDataHelpers.h index 37b50f3f..70a036c4 100644 --- a/src/helpers/sensors/LPPDataHelpers.h +++ b/src/helpers/sensors/LPPDataHelpers.h @@ -142,7 +142,7 @@ public: case LPP_GPS: _pos += 9; break; case LPP_POLYLINE: - _pos += 8; break; // TODO: this is MINIMIUM + _pos += 8; break; // TODO: this is MINIMUM case LPP_GYROMETER: case LPP_ACCELEROMETER: _pos += 6; break; From fec6ddbfa5f9db9354e183534c3576edb001cd98 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Thu, 28 May 2026 17:28:38 -0700 Subject: [PATCH 085/214] Remove Cascadia power saving default --- build.sh | 3 +-- docs/cli_commands.md | 2 +- examples/simple_repeater/MyMesh.cpp | 40 ++++++++++++++++++++++++++--- examples/simple_repeater/MyMesh.h | 2 ++ src/helpers/ESP32Board.h | 7 +++++ 5 files changed, 48 insertions(+), 6 deletions(-) diff --git a/build.sh b/build.sh index f30af6da..f06d9109 100755 --- a/build.sh +++ b/build.sh @@ -446,7 +446,6 @@ prompt_for_cascadia_profile_enable() { echo " - multi.acks: 1" echo " - path.hash.mode: 2" echo " - loop.detect: minimal" - echo " - powersaving: on" prompt_on_off_choice "Enable Cascadia profile overrides" "on" if [ "$MENU_CHOICE" == "on" ]; then FIRMWARE_PROFILE_OVERRIDE="cascadia" @@ -958,7 +957,7 @@ apply_radio_overrides() { apply_firmware_profile_overrides() { case "${FIRMWARE_PROFILE_OVERRIDE,,}" in cascadia) - export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DCASCADIA_PROFILE=1 -DDEFAULT_RX_DELAY_BASE=1.0f -DDEFAULT_LOOP_DETECT=1 -DDEFAULT_POWERSAVING_ENABLED=1 -DDEFAULT_AGC_RESET_INTERVAL=2 -DDEFAULT_ADVERT_INTERVAL=0 -DDEFAULT_FLOOD_ADVERT_INTERVAL=83 -DDEFAULT_MULTI_ACKS=1 -DDEFAULT_PATH_HASH_MODE=2" + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DCASCADIA_PROFILE=1 -DDEFAULT_RX_DELAY_BASE=1.0f -DDEFAULT_LOOP_DETECT=1 -DDEFAULT_AGC_RESET_INTERVAL=2 -DDEFAULT_ADVERT_INTERVAL=0 -DDEFAULT_FLOOD_ADVERT_INTERVAL=83 -DDEFAULT_MULTI_ACKS=1 -DDEFAULT_PATH_HASH_MODE=2" ;; esac } diff --git a/docs/cli_commands.md b/docs/cli_commands.md index a5a55e80..cfbbe25a 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -498,7 +498,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore - `on`: enable power saving - `off`: disable power saving -**Default:** `on` +**Default:** `off` **Note:** When enabled, device enters sleep mode between radio transmissions diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 26534931..d6e8a0c1 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -2143,7 +2143,6 @@ void MyMesh::begin(FILESYSTEM *fs) { _prefs.multi_acks = DEFAULT_MULTI_ACKS; _prefs.path_hash_mode = DEFAULT_PATH_HASH_MODE; _prefs.loop_detect = DEFAULT_LOOP_DETECT; - _prefs.powersaving_enabled = DEFAULT_POWERSAVING_ENABLED; #endif acl.load(_fs, self_id); // TODO: key_store.begin(); @@ -2208,6 +2207,34 @@ void MyMesh::sendFloodScoped(const TransportKey& scope, mesh::Packet* pkt, uint3 } } +void MyMesh::sendFloodScopedWithSelfPath(const TransportKey& scope, mesh::Packet* pkt, + uint32_t delay_millis, uint8_t path_hash_size) { + if (pkt == NULL) { + return; + } + if (path_hash_size == 0 || path_hash_size > MAX_ROUTE_HASH_BYTES) { + MESH_DEBUG_PRINTLN("%s MyMesh::sendFloodScopedWithSelfPath(): invalid path_hash_size", getLogDateTime()); + return; + } + + pkt->header &= ~PH_ROUTE_MASK; + if (scope.isNull()) { + pkt->header |= ROUTE_TYPE_FLOOD; + } else { + uint16_t codes[2]; + codes[0] = scope.calcTransportCode(pkt); + codes[1] = 0; + pkt->header |= ROUTE_TYPE_TRANSPORT_FLOOD; + pkt->transport_codes[0] = codes[0]; + pkt->transport_codes[1] = codes[1]; + } + + pkt->setPathHashSizeAndCount(path_hash_size, 1); + self_id.copyHashTo(pkt->path, path_hash_size); + getTables()->markSent(pkt); + sendPacket(pkt, 1, delay_millis); +} + bool MyMesh::sendRepeatersFloodText(const char* text) { if (text == NULL || *text == 0) return false; @@ -2223,8 +2250,15 @@ bool MyMesh::sendRepeatersFloodText(const char* text) { const size_t max_data_len = MAX_PACKET_PAYLOAD - CIPHER_BLOCK_SIZE; const size_t prefix_cap = max_data_len > 5 ? max_data_len - 5 + 1 : 0; + char sender_name[sizeof(_prefs.node_name)]; + StrHelper::strncpy(sender_name, _prefs.node_name, sizeof(sender_name)); + for (char* p = sender_name; *p; p++) { + if (*p == ':') { + *p = ';'; + } + } int prefix_written = prefix_cap > 0 - ? snprintf((char*)&temp[5], prefix_cap, "%s: ", _prefs.node_name) + ? snprintf((char*)&temp[5], prefix_cap, "%s: ", sender_name) : -1; if (prefix_written < 0) { return false; @@ -2247,7 +2281,7 @@ bool MyMesh::sendRepeatersFloodText(const char* text) { return false; } - sendFloodScoped(default_scope, pkt, 0, _prefs.path_hash_mode + 1); + sendFloodScopedWithSelfPath(default_scope, pkt, 0, _prefs.path_hash_mode + 1); return true; } diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index f55d1d40..5c1f09cb 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -242,6 +242,8 @@ protected: const uint8_t* path, uint8_t path_len, const uint8_t* alt_path, uint8_t alt_path_len, uint32_t delay_millis); + void sendFloodScopedWithSelfPath(const TransportKey& scope, mesh::Packet* pkt, + uint32_t delay_millis, uint8_t path_hash_size); public: MyMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables); diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index fe986593..e99752d9 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -73,6 +73,13 @@ public: return; } + #if defined(ARDUINO_USB_CDC_ON_BOOT) && ARDUINO_USB_CDC_ON_BOOT + if (Serial) { + delay(1); + return; + } + #endif + // Set GPIO wakeup gpio_num_t wakeupPin = (gpio_num_t)getIRQGpio(); From 4f9b8bf1efd019f3b0d347fb25167001df456417 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Thu, 28 May 2026 17:33:33 -0700 Subject: [PATCH 086/214] Handle direct route path consumption --- src/Mesh.cpp | 110 +++++++++++++++++++++++++++++++++++++++++---------- src/Mesh.h | 3 +- 2 files changed, 91 insertions(+), 22 deletions(-) diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 8b088b31..dbdbbf24 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -254,26 +254,35 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { } } - if ((self_id.isHashMatch(pkt->path, pkt->getPathHashSize()) || maybeShortCircuitDirect(pkt)) && allowPacketForward(pkt)) { - if (pkt->getPayloadType() == PAYLOAD_TYPE_MULTIPART) { - return forwardMultipartDirect(pkt); - } else if (pkt->getPayloadType() == PAYLOAD_TYPE_ACK) { - if (!_tables->hasSeen(pkt)) { // don't retransmit! - removeSelfFromPath(pkt); - routeDirectRecvAcks(pkt, 0); + if (canDecodeDirectPayloadForSelf(pkt)) { + // Some path sources include the final node hash, and some packets are + // heard before all planned hops are consumed. Only stop forwarding once + // this node proves it can decrypt the payload. + removePathPrefix(pkt, pkt->getPathHashCount()); + } else if (self_id.isHashMatch(pkt->path, pkt->getPathHashSize()) || maybeShortCircuitDirect(pkt)) { + if (allowPacketForward(pkt)) { + if (pkt->getPayloadType() == PAYLOAD_TYPE_MULTIPART) { + return forwardMultipartDirect(pkt); + } else if (pkt->getPayloadType() == PAYLOAD_TYPE_ACK) { + if (!_tables->hasSeen(pkt)) { // don't retransmit! + removePathPrefix(pkt, 1); + routeDirectRecvAcks(pkt, 0); + } + return ACTION_RELEASE; } - return ACTION_RELEASE; - } - if (!_tables->hasSeen(pkt)) { - removeSelfFromPath(pkt); + if (!_tables->hasSeen(pkt)) { + removePathPrefix(pkt, 1); - uint32_t d = getDirectRetransmitDelay(pkt); - maybeScheduleDirectRetry(pkt, 0); - return ACTION_RETRANSMIT_DELAYED(0, d); // Routed traffic is HIGHEST priority + uint32_t d = getDirectRetransmitDelay(pkt); + maybeScheduleDirectRetry(pkt, 0); + return ACTION_RETRANSMIT_DELAYED(0, d); // Routed traffic is HIGHEST priority + } } } - return ACTION_RELEASE; // this node is NOT the next hop (OR this packet has already been forwarded), so discard. + if (pkt->getPathHashCount() > 0) { + return ACTION_RELEASE; // this node is NOT the next hop (OR this packet has already been forwarded), so discard. + } } if (pkt->isRouteFlood() && filterRecvFloodPacket(pkt)) return ACTION_RELEASE; @@ -487,13 +496,16 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { return action; } -void Mesh::removeSelfFromPath(Packet* pkt) { - // remove our hash from 'path' - pkt->setPathHashCount(pkt->getPathHashCount() - 1); // decrement the count +void Mesh::removePathPrefix(Packet* pkt, uint8_t prefix_count) { + uint8_t hash_count = pkt->getPathHashCount(); + if (prefix_count == 0 || hash_count == 0) return; + if (prefix_count > hash_count) prefix_count = hash_count; + pkt->setPathHashCount(hash_count - prefix_count); uint8_t sz = pkt->getPathHashSize(); - for (int k = 0; k < pkt->getPathHashCount()*sz; k += sz) { // shuffle path by 1 'entry' - memcpy(&pkt->path[k], &pkt->path[k + sz], sz); + uint8_t prefix_bytes = prefix_count * sz; + for (int k = 0; k < pkt->getPathHashCount()*sz; k += sz) { + memmove(&pkt->path[k], &pkt->path[k + prefix_bytes], sz); } } @@ -526,7 +538,7 @@ DispatcherAction Mesh::forwardMultipartDirect(Packet* pkt) { memcpy(tmp.payload, &pkt->payload[1], tmp.payload_len); if (!_tables->hasSeen(&tmp)) { // don't retransmit! - removeSelfFromPath(&tmp); + removePathPrefix(&tmp, 1); routeDirectRecvAcks(&tmp, ((uint32_t)remaining + 1) * 300); // expect multipart ACKs 300ms apart (x2) } } @@ -839,6 +851,62 @@ bool Mesh::getDirectRetryTarget(const Packet* packet, const uint8_t*& next_hop_h } } +bool Mesh::canDecodeDirectPayloadForSelf(const Packet* packet) { + if (packet == NULL || !packet->isRouteDirect() || packet->getPathHashCount() == 0 || packet->payload_len < 1) { + return false; + } + + switch (packet->getPayloadType()) { + case PAYLOAD_TYPE_PATH: + case PAYLOAD_TYPE_REQ: + case PAYLOAD_TYPE_RESPONSE: + case PAYLOAD_TYPE_TXT_MSG: { + if (packet->payload_len < 2) { + return false; + } + + int i = 0; + uint8_t dest_hash = packet->payload[i++]; + uint8_t src_hash = packet->payload[i++]; + if (i + CIPHER_MAC_SIZE >= packet->payload_len || !self_id.isHashMatch(&dest_hash)) { + return false; + } + + int num = searchPeersByHash(&src_hash); + for (int j = 0; j < num; j++) { + uint8_t secret[PUB_KEY_SIZE]; + getPeerSharedSecret(secret, j); + + uint8_t data[MAX_PACKET_PAYLOAD]; + if (Utils::MACThenDecrypt(secret, data, &packet->payload[i], packet->payload_len - i) > 0) { + return true; + } + } + return false; + } + + case PAYLOAD_TYPE_ANON_REQ: { + int i = 0; + uint8_t dest_hash = packet->payload[i++]; + if (i + PUB_KEY_SIZE + CIPHER_MAC_SIZE >= packet->payload_len || !self_id.isHashMatch(&dest_hash)) { + return false; + } + + Identity sender(&packet->payload[i]); + i += PUB_KEY_SIZE; + + uint8_t secret[PUB_KEY_SIZE]; + self_id.calcSharedSecret(secret, sender); + + uint8_t data[MAX_PACKET_PAYLOAD]; + return Utils::MACThenDecrypt(secret, data, &packet->payload[i], packet->payload_len - i) > 0; + } + + default: + return false; + } +} + void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { const uint8_t* next_hop_hash; uint8_t next_hop_hash_len; diff --git a/src/Mesh.h b/src/Mesh.h index 483d4826..6ae26642 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -75,7 +75,7 @@ class Mesh : public Dispatcher { DirectRetryEntry _direct_retries[MAX_DIRECT_RETRY_SLOTS]; FloodRetryEntry _flood_retries[MAX_FLOOD_RETRY_SLOTS]; - void removeSelfFromPath(Packet* packet); + void removePathPrefix(Packet* packet, uint8_t prefix_count); void routeDirectRecvAcks(Packet* packet, uint32_t delay_millis); void clearDirectRetrySlot(int idx); bool isDirectRetryQueued(const Packet* packet) const; @@ -85,6 +85,7 @@ class Mesh : public Dispatcher { void clearPendingDirectRetryOnSendFail(const Packet* packet); bool getDirectRetryTarget(const Packet* packet, const uint8_t*& next_hop_hash, uint8_t& next_hop_hash_len, uint8_t& progress_marker, bool& expect_path_growth) const; + bool canDecodeDirectPayloadForSelf(const Packet* packet); void maybeScheduleDirectRetry(const Packet* packet, uint8_t priority); void clearFloodRetrySlot(int idx); bool isFloodRetryQueued(const Packet* packet) const; From 618c2b6773263e6e8c7c2d3a118834165c6a5d31 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 2 Jun 2026 16:22:12 -0700 Subject: [PATCH 087/214] Support direct outpath override --- docs/cli_commands.md | 2 ++ docs/halo_keymind_settings.md | 12 +++++++----- examples/simple_repeater/MyMesh.cpp | 10 +++++++++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index cfbbe25a..d1cba8f9 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -943,6 +943,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Usage:** - `get outpath` - `set outpath ` +- `set outpath direct` - `set outpath clear` - `set outpath flood` - `get altpath` @@ -956,6 +957,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore - These commands require remote client context; they target the caller's ACL entry. - The path hash size is inferred from the hop hash width. - `outpath` overrides the primary direct route used for replies to the caller. +- `direct` sets a zero-hop direct route for a caller reachable without repeaters. - `clear` forgets the current direct path and allows normal path discovery to repopulate it. - `flood` forces replies to use flood packets until the client logs in again. - `altpath` is an optional second direct route used for duplicate response attempts. diff --git a/docs/halo_keymind_settings.md b/docs/halo_keymind_settings.md index 72fd0db7..ca2c0803 100644 --- a/docs/halo_keymind_settings.md +++ b/docs/halo_keymind_settings.md @@ -77,7 +77,7 @@ set flood.retry.ignore none | `battery.alert.low` | Warning threshold percentage. Must be greater than `battery.alert.critical`. | `get battery.alert.low`, `set battery.alert.low <1-100>` | `set battery.alert.low 20` | | `battery.alert.critical` | Critical threshold percentage. Critical warnings repeat more often. | `get battery.alert.critical`, `set battery.alert.critical <0-99>` | `set battery.alert.critical 10` | | `recent.repeater` | Shows, seeds, or clears the recent repeater prefix/SNR table used by direct retry and bridge freshness checks. | `get recent.repeater`, `get recent.repeater `, `set recent.repeater `, `clear recent.repeater` | `set recent.repeater A1B2C3 -8.5` | -| `outpath` | Overrides the primary direct route used for replies to the current remote client. | `get outpath`, `set outpath `, `set outpath clear`, `set outpath flood` | `set outpath A1B2C3,D4E5F6` | +| `outpath` | Overrides the primary direct route used for replies to the current remote client. | `get outpath`, `set outpath `, `set outpath direct`, `set outpath clear`, `set outpath flood` | `set outpath A1B2C3,D4E5F6` | | `altpath` | Optional second direct route used for duplicate response attempts to the current remote client. | `get altpath`, `set altpath `, `set altpath clear` | `set altpath A1B2C3,D4E5F6` | ## Other Keymind Commands @@ -155,6 +155,7 @@ hex characters, and all hops in one path must use the same width. ```text get outpath set outpath A1B2C3,D4E5F6 +set outpath direct set outpath clear set outpath flood @@ -163,10 +164,11 @@ set altpath A1B2C3,D4E5F6 set altpath clear ``` -`set outpath clear` forgets the override and lets normal path discovery fill it -again. `set outpath flood` forces replies to use flood packets until the client -logs in again. `altpath` sends a duplicate reply over a second direct route; -clearing it returns replies to a single route. +`set outpath direct` sets a zero-hop direct route for a client reachable without +repeaters. `set outpath clear` forgets the override and lets normal path +discovery fill it again. `set outpath flood` forces replies to use flood packets +until the client logs in again. `altpath` sends a duplicate reply over a second +direct route; clearing it returns replies to a single route. ## Direct Retry Settings diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index d6e8a0c1..46508c32 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -579,7 +579,7 @@ static bool pathsEqual(const uint8_t* a, uint8_t a_len, const uint8_t* b, uint8_ } static bool hasUsablePath(const uint8_t* path, uint8_t path_len) { - return path != NULL && mesh::Packet::isValidPathLen(path_len) && (path_len & 63) > 0; + return path != NULL && mesh::Packet::isValidPathLen(path_len); } static bool buildRepeatersChannel(mesh::GroupChannel& channel) { @@ -2529,6 +2529,10 @@ static bool parsePathCommand(char* raw, uint8_t* out_path, uint8_t& out_path_len out_path_len = OUT_PATH_FORCE_FLOOD; return true; } + if (strcmp(spec, "direct") == 0) { + out_path_len = 0; + return true; + } uint8_t hash_size = 0; uint8_t hop_count = 0; @@ -2586,6 +2590,10 @@ static void formatPathReply(const uint8_t* path, uint8_t path_len, char* out, si snprintf(out, out_len, "> invalid"); return; } + if ((path_len & 63) == 0) { + snprintf(out, out_len, "> direct"); + return; + } uint8_t hash_size = (path_len >> 6) + 1; uint8_t hop_count = path_len & 63; From 2af7b75d70dd64031398352e1d503e058059aa6b Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 21 Apr 2026 16:24:00 -0700 Subject: [PATCH 088/214] For packets with a path set; auto try again if no echo was heard --- docs/cli_commands.md | 28 +++ examples/simple_repeater/MyMesh.cpp | 90 +++++++++- examples/simple_repeater/MyMesh.h | 7 + src/Dispatcher.cpp | 8 +- src/Dispatcher.h | 2 + src/Mesh.cpp | 270 +++++++++++++++++++++++++++- src/Mesh.h | 41 +++++ src/helpers/CommonCLI.cpp | 55 +++++- src/helpers/CommonCLI.h | 4 +- src/helpers/SimpleMeshTables.h | 174 +++++++++++++++--- 10 files changed, 643 insertions(+), 36 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 6b4f6157..c6a7fac0 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -405,6 +405,34 @@ --- +#### View or change whether direct retries can fall back to the recently-heard repeater list +**Usage:** +- `get direct.retry.heard` +- `set direct.retry.heard ` + +**Parameters:** +- `state`: `on`|`off` + +**Default:** `off` + +**Note:** When enabled, a repeater can use recently-heard non-duplicate repeater prefixes as a fallback for direct retry eligibility when no suitable neighbor entry is available. + +--- + +#### View or change the SNR margin used for direct retry eligibility +**Usage:** +- `get direct.retry.margin` +- `set direct.retry.margin ` + +**Parameters:** +- `value`: Margin in dB above the SF-specific receive floor (minimum `0`, default `5`) + +**Default:** `5` + +**Note:** The retry gate uses the active SF floor of `SF5=-2.5`, `SF6=-5`, `SF7=-7.5`, `SF8=-10`, `SF9=-12.5`, `SF10=-15`, `SF11=-17.5`, `SF12=-20`, then adds this margin. + +--- + #### [Experimental] View or change the processing delay for received traffic **Usage:** - `get rxdelay` diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 6d957cc0..f268679c 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -40,6 +40,9 @@ #ifndef TXT_ACK_DELAY #define TXT_ACK_DELAY 200 #endif +#ifndef HALO_DIRECT_RETRY_DELAY_MIN + #define HALO_DIRECT_RETRY_DELAY_MIN 200 +#endif #define FIRMWARE_VER_LEVEL 2 @@ -60,6 +63,20 @@ #define LAZY_CONTACTS_WRITE_DELAY 5000 +const NeighbourInfo* MyMesh::findNeighbourByHash(const uint8_t* hash, uint8_t hash_len) const { +#if MAX_NEIGHBOURS + for (int i = 0; i < MAX_NEIGHBOURS; i++) { + if (neighbours[i].heard_timestamp > 0 && neighbours[i].id.isHashMatch(hash, hash_len)) { + return &neighbours[i]; + } + } +#else + (void)hash; + (void)hash_len; +#endif + return NULL; +} + void MyMesh::putNeighbour(const mesh::Identity &id, uint32_t timestamp, float snr) { #if MAX_NEIGHBOURS // check if neighbours enabled // find existing neighbour, else use least recently updated @@ -383,6 +400,25 @@ File MyMesh::openAppend(const char *fname) { #endif } +static uint8_t max_loop_minimal[] = { 0, /* 1-byte */ 4, /* 2-byte */ 2, /* 3-byte */ 1 }; +static uint8_t max_loop_moderate[] = { 0, /* 1-byte */ 2, /* 2-byte */ 1, /* 3-byte */ 1 }; +static uint8_t max_loop_strict[] = { 0, /* 1-byte */ 1, /* 2-byte */ 1, /* 3-byte */ 1 }; +// SF5..SF12 receive floors, scaled by 4 so we can keep the retry gate in int8_t quarter-dB units. +static const int8_t direct_retry_floor_x4[] = { -10, -20, -30, -40, -50, -60, -70, -80 }; + +bool MyMesh::isLooped(const mesh::Packet* packet, const uint8_t max_counters[]) { + uint8_t hash_size = packet->getPathHashSize(); + uint8_t hash_count = packet->getPathHashCount(); + uint8_t n = 0; + const uint8_t* path = packet->path; + while (hash_count > 0) { // count how many times this node is already in the path + if (self_id.isHashMatch(path, hash_size)) n++; + hash_count--; + path += hash_size; + } + return n >= max_counters[hash_size]; +} + bool MyMesh::allowPacketForward(const mesh::Packet *packet) { if (_prefs.disable_fwd) return false; if (packet->isRouteFlood() && packet->path_len >= _prefs.flood_max) return false; @@ -487,6 +523,44 @@ uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) { uint32_t t = (_radio->getEstAirtimeFor(packet->path_len + packet->payload_len + 2) * _prefs.direct_tx_delay_factor); return getRNG()->nextInt(0, 5*t + 1); } +int8_t MyMesh::getDirectRetryMinSNRX4() const { + // Use the live SF so `tempradio` changes immediately affect the retry threshold. + uint8_t sf = constrain(active_sf, (uint8_t)5, (uint8_t)12); + int16_t threshold = direct_retry_floor_x4[sf - 5] + ((int16_t)_prefs.direct_retry_snr_margin_db * 4); + return (int8_t)constrain(threshold, -128, 127); +} +bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const { + if (_prefs.disable_fwd) { + return false; + } + + int8_t min_snr_x4 = getDirectRetryMinSNRX4(); + const NeighbourInfo* neighbour = findNeighbourByHash(next_hop_hash, next_hop_hash_len); + // Prefer the explicit neighbor table first; it is the strongest signal that this hop is still reachable. + if (neighbour != NULL && neighbour->snr >= min_snr_x4) { + return true; + } + + if (!_prefs.direct_retry_recent_enabled) { + return false; + } + + // If no neighbor entry exists, fall back to the recent-heard repeater cache keyed by the same path prefix. + const auto* recent = ((const SimpleMeshTables *)getTables())->findRecentRepeaterByHash(next_hop_hash, next_hop_hash_len); + return recent != NULL && recent->snr_x4 >= min_snr_x4; +} +uint32_t MyMesh::getDirectRetryEchoDelay(const mesh::Packet* packet) const { + // Approximate LoRa line rate in kilobits/sec from the live radio params the repeater is using now. + float kbps = (((float) active_sf) * active_bw * ((float) active_cr)) / ((float) (1UL << active_sf)); + if (kbps <= 0.0f) { + return HALO_DIRECT_RETRY_DELAY_MIN; + } + + // Wait roughly long enough for our transmission, the next hop's receive/forward window, and its echo back. + uint32_t bits = ((uint32_t) packet->getRawLength()) * 8; + uint32_t scaled_wait_millis = (uint32_t) ((((float) bits) * 4.0f) / kbps); + return max((uint32_t) HALO_DIRECT_RETRY_DELAY_MIN, scaled_wait_millis); +} bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) { // just try to determine region for packet (apply later in allowPacketForward()) @@ -771,7 +845,9 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.airtime_factor = 1.0; // one half _prefs.rx_delay_base = 0.0f; // turn off by default, was 10.0; _prefs.tx_delay_factor = 0.5f; // was 0.25f - _prefs.direct_tx_delay_factor = 0.2f; // was zero + _prefs.direct_tx_delay_factor = 0.3f; // was 0.2 + _prefs.direct_retry_recent_enabled = 0; + _prefs.direct_retry_snr_margin_db = 5; StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); _prefs.node_lat = ADVERT_LAT; _prefs.node_lon = ADVERT_LON; @@ -801,6 +877,9 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.advert_loc_policy = ADVERT_LOC_PREFS; _prefs.adc_multiplier = 0.0f; // 0.0f means use default board multiplier + active_bw = _prefs.bw; + active_sf = _prefs.sf; + active_cr = _prefs.cr; } void MyMesh::begin(FILESYSTEM *fs) { @@ -819,6 +898,9 @@ void MyMesh::begin(FILESYSTEM *fs) { #endif radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); + active_bw = _prefs.bw; + active_sf = _prefs.sf; + active_cr = _prefs.cr; radio_set_tx_power(_prefs.tx_power_dbm); updateAdvertTimer(); @@ -1196,12 +1278,18 @@ void MyMesh::loop() { if (set_radio_at && millisHasNowPassed(set_radio_at)) { // apply pending (temporary) radio params set_radio_at = 0; // clear timer radio_set_params(pending_freq, pending_bw, pending_sf, pending_cr); + active_bw = pending_bw; + active_sf = pending_sf; + active_cr = pending_cr; MESH_DEBUG_PRINTLN("Temp radio params"); } if (revert_radio_at && millisHasNowPassed(revert_radio_at)) { // revert radio params to orig revert_radio_at = 0; // clear timer radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); + active_bw = _prefs.bw; + active_sf = _prefs.sf; + active_cr = _prefs.cr; MESH_DEBUG_PRINTLN("Radio params restored"); } diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 0d5cd28a..31528829 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -106,8 +106,11 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { unsigned long set_radio_at, revert_radio_at; float pending_freq; float pending_bw; + float active_bw; // live BW, including temporary radio overrides uint8_t pending_sf; + uint8_t active_sf; // live SF, including temporary radio overrides uint8_t pending_cr; + uint8_t active_cr; // live CR, including temporary radio overrides int matching_peer_indexes[MAX_CLIENTS]; #if defined(WITH_RS232_BRIDGE) RS232Bridge bridge; @@ -115,6 +118,8 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { ESPNowBridge bridge; #endif + const NeighbourInfo* findNeighbourByHash(const uint8_t* hash, uint8_t hash_len) const; + int8_t getDirectRetryMinSNRX4() const; void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr); uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood); uint8_t handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data); @@ -141,6 +146,8 @@ protected: uint32_t getRetransmitDelay(const mesh::Packet* packet) override; uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override; + bool allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const override; + uint32_t getDirectRetryEchoDelay(const mesh::Packet* packet) const override; int getInterferenceThreshold() const override { return _prefs.interference_threshold; diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index 0a154985..d9dd00a3 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -68,7 +68,8 @@ void Dispatcher::loop() { next_tx_time = futureMillis(t * getAirtimeBudgetFactor()); _radio->onSendFinished(); - logTx(outbound, 2 + outbound->path_len + outbound->payload_len); + logTx(outbound, 2 + outbound->getPathByteLen() + outbound->payload_len); + onSendComplete(outbound); if (outbound->isRouteFlood()) { n_sent_flood++; } else { @@ -80,7 +81,8 @@ void Dispatcher::loop() { MESH_DEBUG_PRINTLN("%s Dispatcher::loop(): WARNING: outbound packed send timed out!", getLogDateTime()); _radio->onSendFinished(); - logTxFail(outbound, 2 + outbound->path_len + outbound->payload_len); + logTxFail(outbound, 2 + outbound->getPathByteLen() + outbound->payload_len); + onSendFail(outbound); releasePacket(outbound); // return to pool outbound = NULL; @@ -330,4 +332,4 @@ unsigned long Dispatcher::futureMillis(int millis_from_now) const { return _ms->getMillis() + millis_from_now; } -} \ No newline at end of file +} diff --git a/src/Dispatcher.h b/src/Dispatcher.h index 25a41d82..29460061 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -151,6 +151,8 @@ protected: virtual void logRx(Packet* packet, int len, float score) { } // hooks for custom logging virtual void logTx(Packet* packet, int len) { } virtual void logTxFail(Packet* packet, int len) { } + virtual void onSendComplete(Packet* packet) { } + virtual void onSendFail(Packet* packet) { } virtual const char* getLogDateTime() { return ""; } virtual float getAirtimeBudgetFactor() const; diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 0548c907..082b6bb4 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -4,11 +4,32 @@ namespace mesh { void Mesh::begin() { + for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { + _direct_retries[i].packet = NULL; + _direct_retries[i].trigger_packet = NULL; + _direct_retries[i].retry_at = 0; + _direct_retries[i].retry_delay = 0; + _direct_retries[i].priority = 0; + _direct_retries[i].progress_marker = 0; + _direct_retries[i].expect_path_growth = false; + _direct_retries[i].queued = false; + _direct_retries[i].active = false; + } Dispatcher::begin(); } void Mesh::loop() { Dispatcher::loop(); + + for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { + if (!_direct_retries[i].active || !_direct_retries[i].queued || !millisHasNowPassed(_direct_retries[i].retry_at)) { + continue; + } + + if (!isDirectRetryQueued(_direct_retries[i].packet)) { + clearDirectRetrySlot(i); + } + } } bool Mesh::allowPacketForward(const mesh::Packet* packet) { @@ -22,10 +43,25 @@ uint32_t Mesh::getRetransmitDelay(const mesh::Packet* packet) { uint32_t Mesh::getDirectRetransmitDelay(const Packet* packet) { return 0; // by default, no delay } +bool Mesh::allowDirectRetry(const Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const { + return false; +} +uint32_t Mesh::getDirectRetryEchoDelay(const Packet* packet) const { + // Keep the base fallback aligned with the repeater's minimum retry wait. + return 200; +} uint8_t Mesh::getExtraAckTransmitCount() const { return 0; } +void Mesh::onSendComplete(Packet* packet) { + armDirectRetryOnSendComplete(packet); +} + +void Mesh::onSendFail(Packet* packet) { + clearPendingDirectRetryOnSendFail(packet); +} + uint32_t Mesh::getCADFailRetryDelay() const { return _rng->nextInt(1, 4)*120; } @@ -44,6 +80,10 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { return ACTION_RELEASE; } + if (pkt->isRouteDirect()) { + cancelDirectRetryOnEcho(pkt); + } + if (pkt->isRouteDirect() && pkt->getPayloadType() == PAYLOAD_TYPE_TRACE) { if (pkt->path_len < MAX_PATH_SIZE) { uint8_t i = 0; @@ -63,6 +103,7 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { pkt->path[pkt->path_len++] = (int8_t) (pkt->getSNR()*4); uint32_t d = getDirectRetransmitDelay(pkt); + maybeScheduleDirectRetry(pkt, 5); return ACTION_RETRANSMIT_DELAYED(5, d); // schedule with priority 5 (for now), maybe make configurable? } } @@ -103,6 +144,7 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { removeSelfFromPath(pkt); uint32_t d = getDirectRetransmitDelay(pkt); + maybeScheduleDirectRetry(pkt, 0); return ACTION_RETRANSMIT_DELAYED(0, d); // Routed traffic is HIGHEST priority } } @@ -379,6 +421,7 @@ void Mesh::routeDirectRecvAcks(Packet* packet, uint32_t delay_millis) { memcpy(a1->path, packet->path, a1->path_len = packet->path_len); a1->header &= ~PH_ROUTE_MASK; a1->header |= ROUTE_TYPE_DIRECT; + maybeScheduleDirectRetry(a1, 0); sendPacket(a1, 0, delay_millis); } extra--; @@ -389,11 +432,225 @@ void Mesh::routeDirectRecvAcks(Packet* packet, uint32_t delay_millis) { memcpy(a2->path, packet->path, a2->path_len = packet->path_len); a2->header &= ~PH_ROUTE_MASK; a2->header |= ROUTE_TYPE_DIRECT; + maybeScheduleDirectRetry(a2, 0); sendPacket(a2, 0, delay_millis); } } } +void Mesh::clearDirectRetrySlot(int idx) { + _direct_retries[idx].packet = NULL; + _direct_retries[idx].trigger_packet = NULL; + _direct_retries[idx].retry_at = 0; + _direct_retries[idx].retry_delay = 0; + _direct_retries[idx].priority = 0; + _direct_retries[idx].progress_marker = 0; + _direct_retries[idx].expect_path_growth = false; + _direct_retries[idx].queued = false; + _direct_retries[idx].active = false; +} + +bool Mesh::isDirectRetryQueued(const Packet* packet) const { + for (int i = 0; i < _mgr->getOutboundTotal(); i++) { + if (_mgr->getOutboundByIdx(i) == packet) { + return true; + } + } + return false; +} + +void Mesh::calculateDirectRetryKey(const Packet* packet, uint8_t* dest_key) const { + uint8_t type = packet->getPayloadType(); + Utils::sha256(dest_key, MAX_HASH_SIZE, &type, 1, packet->payload, packet->payload_len); +} + +bool Mesh::cancelDirectRetryOnEcho(const Packet* packet) { + uint8_t recv_key[MAX_HASH_SIZE]; + calculateDirectRetryKey(packet, recv_key); + + bool cleared = false; + for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { + if (!_direct_retries[i].active || memcmp(recv_key, _direct_retries[i].retry_key, MAX_HASH_SIZE) != 0) { + continue; + } + + bool is_echo = _direct_retries[i].expect_path_growth + ? packet->path_len > _direct_retries[i].progress_marker + : packet->getPathHashCount() < _direct_retries[i].progress_marker; + if (!is_echo) { + continue; + } + + if (_direct_retries[i].queued) { + for (int j = 0; j < _mgr->getOutboundTotal(); j++) { + if (_mgr->getOutboundByIdx(j) == _direct_retries[i].packet) { + Packet* pending = _mgr->removeOutboundByIdx(j); + if (pending) { + releasePacket(pending); + } + break; + } + } + clearDirectRetrySlot(i); + } else { + clearDirectRetrySlot(i); + } + cleared = true; + } + + return cleared; +} + +void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { + for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { + if (!_direct_retries[i].active) { + continue; + } + + if (_direct_retries[i].queued) { + if (_direct_retries[i].packet == packet) { + // The retry packet itself just finished transmitting; Dispatcher will release it after this hook. + clearDirectRetrySlot(i); + } + continue; + } + + if (_direct_retries[i].trigger_packet != packet) { + continue; + } + + // Allocate the retry packet only after TX-complete so busy repeaters do not reserve pool slots early. + Packet* retry = obtainNewPacket(); + if (retry == NULL) { + clearDirectRetrySlot(i); + continue; + } + + *retry = *packet; + + // Start the echo wait only after the initial direct transmission actually completed. + sendPacket(retry, _direct_retries[i].priority, _direct_retries[i].retry_delay); + if (isDirectRetryQueued(retry)) { + _direct_retries[i].packet = retry; + _direct_retries[i].trigger_packet = NULL; + _direct_retries[i].queued = true; + _direct_retries[i].retry_at = futureMillis(_direct_retries[i].retry_delay); + } else { + clearDirectRetrySlot(i); + } + } +} + +void Mesh::clearPendingDirectRetryOnSendFail(const Packet* packet) { + for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { + if (!_direct_retries[i].active) { + continue; + } + + if (_direct_retries[i].queued) { + if (_direct_retries[i].packet == packet) { + // The queued retry itself failed; Dispatcher will release it after this hook. + clearDirectRetrySlot(i); + } + continue; + } + + if (_direct_retries[i].trigger_packet == packet) { + clearDirectRetrySlot(i); + } + } +} + +bool Mesh::getDirectRetryTarget(const Packet* packet, const uint8_t*& next_hop_hash, uint8_t& next_hop_hash_len, + uint8_t& progress_marker, bool& expect_path_growth) const { + switch (packet->getPayloadType()) { + case PAYLOAD_TYPE_ACK: + case PAYLOAD_TYPE_PATH: + case PAYLOAD_TYPE_REQ: + case PAYLOAD_TYPE_RESPONSE: + case PAYLOAD_TYPE_TXT_MSG: + case PAYLOAD_TYPE_ANON_REQ: + if (packet->getPathHashCount() <= 1) { + return false; + } + next_hop_hash = packet->path; + next_hop_hash_len = packet->getPathHashSize(); + progress_marker = packet->getPathHashCount(); + expect_path_growth = false; + return true; + + case PAYLOAD_TYPE_MULTIPART: + if (packet->payload_len < 1 || (packet->payload[0] & 0x0F) != PAYLOAD_TYPE_ACK || packet->getPathHashCount() <= 1) { + return false; + } + next_hop_hash = packet->path; + next_hop_hash_len = packet->getPathHashSize(); + progress_marker = packet->getPathHashCount(); + expect_path_growth = false; + return true; + + case PAYLOAD_TYPE_TRACE: { + if (packet->payload_len < 9) { + return false; + } + + uint8_t hash_size = 1 << (packet->payload[8] & 0x03); + uint8_t route_bytes = packet->payload_len - 9; + uint8_t offset = packet->path_len * hash_size; + if (offset + hash_size > route_bytes) { + return false; + } + if (offset + (2 * hash_size) > route_bytes) { + return false; // no downstream repeater means there will be no forward echo to overhear. + } + + next_hop_hash = &packet->payload[9 + offset]; + next_hop_hash_len = hash_size; + progress_marker = packet->path_len; + expect_path_growth = true; + return true; + } + + default: + return false; + } +} + +void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { + const uint8_t* next_hop_hash; + uint8_t next_hop_hash_len; + uint8_t progress_marker; + bool expect_path_growth; + if (!getDirectRetryTarget(packet, next_hop_hash, next_hop_hash_len, progress_marker, expect_path_growth) + || !allowDirectRetry(packet, next_hop_hash, next_hop_hash_len)) { + return; + } + + int slot_idx = -1; + for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { + if (!_direct_retries[i].active) { + slot_idx = i; + break; + } + } + if (slot_idx < 0) { + return; + } + + // Only store retry metadata here; allocate the retry packet after the initial TX really completes. + uint32_t retry_delay = getDirectRetryEchoDelay(packet); + calculateDirectRetryKey(packet, _direct_retries[slot_idx].retry_key); + _direct_retries[slot_idx].packet = NULL; + _direct_retries[slot_idx].trigger_packet = const_cast(packet); + _direct_retries[slot_idx].retry_at = 0; + _direct_retries[slot_idx].retry_delay = retry_delay; + _direct_retries[slot_idx].priority = priority; + _direct_retries[slot_idx].progress_marker = progress_marker; + _direct_retries[slot_idx].expect_path_growth = expect_path_growth; + _direct_retries[slot_idx].queued = false; + _direct_retries[slot_idx].active = true; +} + Packet* Mesh::createAdvert(const LocalIdentity& id, const uint8_t* app_data, size_t app_data_len) { if (app_data_len > MAX_ADVERT_DATA_SIZE) return NULL; @@ -634,7 +891,7 @@ void Mesh::sendFlood(Packet* packet, uint32_t delay_millis) { packet->header |= ROUTE_TYPE_FLOOD; packet->path_len = 0; - _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us + _tables->markSent(packet); // mark this packet as already sent in case it is rebroadcast back to us uint8_t pri; if (packet->getPayloadType() == PAYLOAD_TYPE_PATH) { @@ -659,7 +916,7 @@ void Mesh::sendFlood(Packet* packet, uint16_t* transport_codes, uint32_t delay_m packet->transport_codes[1] = transport_codes[1]; packet->path_len = 0; - _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us + _tables->markSent(packet); // mark this packet as already sent in case it is rebroadcast back to us uint8_t pri; if (packet->getPayloadType() == PAYLOAD_TYPE_PATH) { @@ -692,7 +949,8 @@ void Mesh::sendDirect(Packet* packet, const uint8_t* path, uint8_t path_len, uin pri = 0; } } - _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us + _tables->markSent(packet); // mark this packet as already sent in case it is rebroadcast back to us + maybeScheduleDirectRetry(packet, pri); sendPacket(packet, pri, delay_millis); } @@ -702,7 +960,7 @@ void Mesh::sendZeroHop(Packet* packet, uint32_t delay_millis) { packet->path_len = 0; // path_len of zero means Zero Hop - _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us + _tables->markSent(packet); // mark this packet as already sent in case it is rebroadcast back to us sendPacket(packet, 0, delay_millis); } @@ -715,9 +973,9 @@ void Mesh::sendZeroHop(Packet* packet, uint16_t* transport_codes, uint32_t delay packet->path_len = 0; // path_len of zero means Zero Hop - _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us + _tables->markSent(packet); // mark this packet as already sent in case it is rebroadcast back to us sendPacket(packet, 0, delay_millis); } -} \ No newline at end of file +} diff --git a/src/Mesh.h b/src/Mesh.h index 00f7ed00..874336b0 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -4,6 +4,10 @@ namespace mesh { +#ifndef MAX_DIRECT_RETRY_SLOTS + #define MAX_DIRECT_RETRY_SLOTS 6 +#endif + class GroupChannel { public: uint8_t hash[PATH_HASH_SIZE]; @@ -16,6 +20,7 @@ public: class MeshTables { public: virtual bool hasSeen(const Packet* packet) = 0; + virtual void markSent(const Packet* packet) = 0; virtual void clear(const Packet* packet) = 0; // remove this packet hash from table }; @@ -24,17 +29,42 @@ public: * and provides virtual methods for sub-classes on handling incoming, and also preparing outbound Packets. */ class Mesh : public Dispatcher { + struct DirectRetryEntry { + Packet* packet; + Packet* trigger_packet; + unsigned long retry_at; + uint32_t retry_delay; + uint8_t retry_key[MAX_HASH_SIZE]; + uint8_t priority; + uint8_t progress_marker; + bool expect_path_growth; + bool queued; + bool active; + }; + RTCClock* _rtc; RNG* _rng; MeshTables* _tables; + DirectRetryEntry _direct_retries[MAX_DIRECT_RETRY_SLOTS]; void removeSelfFromPath(Packet* packet); void routeDirectRecvAcks(Packet* packet, uint32_t delay_millis); + void clearDirectRetrySlot(int idx); + bool isDirectRetryQueued(const Packet* packet) const; + void calculateDirectRetryKey(const Packet* packet, uint8_t* dest_key) const; + bool cancelDirectRetryOnEcho(const Packet* packet); + void armDirectRetryOnSendComplete(const Packet* packet); + void clearPendingDirectRetryOnSendFail(const Packet* packet); + bool getDirectRetryTarget(const Packet* packet, const uint8_t*& next_hop_hash, uint8_t& next_hop_hash_len, + uint8_t& progress_marker, bool& expect_path_growth) const; + void maybeScheduleDirectRetry(const Packet* packet, uint8_t priority); //void routeRecvAcks(Packet* packet, uint32_t delay_millis); DispatcherAction forwardMultipartDirect(Packet* pkt); protected: DispatcherAction onRecvPacket(Packet* pkt) override; + void onSendComplete(Packet* packet) override; + void onSendFail(Packet* packet) override; virtual uint32_t getCADFailRetryDelay() const override; @@ -65,6 +95,17 @@ protected: */ virtual uint32_t getDirectRetransmitDelay(const Packet* packet); + /** + * \brief Decide whether a DIRECT packet should get one delayed retry if the next hop echo is not overheard. + * Sub-classes can use neighbour tables or other link-quality data to opt in selectively. + */ + virtual bool allowDirectRetry(const Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const; + + /** + * \returns milliseconds to wait for the next-hop echo before queueing one retry of the DIRECT packet. + */ + virtual uint32_t getDirectRetryEchoDelay(const Packet* packet) const; + /** * \returns number of extra (Direct) ACK transmissions wanted. */ diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 10ab8669..0f954efc 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -4,6 +4,17 @@ #include "AdvertDataHelpers.h" #include +#ifndef BRIDGE_MAX_BAUD +#define BRIDGE_MAX_BAUD 115200 +#endif + +// These bytes used to be reserved/unused in persisted prefs, so keep a marker before trusting them. +#define DIRECT_RETRY_PREFS_MAGIC_0 0xD4 +#define DIRECT_RETRY_PREFS_MAGIC_1 0x52 +#define DIRECT_RETRY_RECENT_DEFAULT 0 +#define DIRECT_RETRY_SNR_MARGIN_DB_DEFAULT 5 +#define DIRECT_RETRY_SNR_MARGIN_DB_MAX 40 + // Believe it or not, this std C function is busted on some platforms! static uint32_t _atoi(const char* sp) { uint32_t n = 0; @@ -56,7 +67,9 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->tx_delay_factor, sizeof(_prefs->tx_delay_factor)); // 84 file.read((uint8_t *)&_prefs->guest_password[0], sizeof(_prefs->guest_password)); // 88 file.read((uint8_t *)&_prefs->direct_tx_delay_factor, sizeof(_prefs->direct_tx_delay_factor)); // 104 - file.read(pad, 4); // 108 + file.read((uint8_t *)&_prefs->direct_retry_recent_enabled, sizeof(_prefs->direct_retry_recent_enabled)); // 108 + file.read((uint8_t *)&_prefs->direct_retry_snr_margin_db, sizeof(_prefs->direct_retry_snr_margin_db)); // 109 + file.read((uint8_t *)&_prefs->direct_retry_prefs_magic[0], sizeof(_prefs->direct_retry_prefs_magic)); // 110 file.read((uint8_t *)&_prefs->sf, sizeof(_prefs->sf)); // 112 file.read((uint8_t *)&_prefs->cr, sizeof(_prefs->cr)); // 113 file.read((uint8_t *)&_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114 @@ -95,6 +108,15 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->tx_power_dbm = constrain(_prefs->tx_power_dbm, 1, 30); _prefs->multi_acks = constrain(_prefs->multi_acks, 0, 1); _prefs->adc_multiplier = constrain(_prefs->adc_multiplier, 0.0f, 10.0f); + // Old firmware left offset 108..111 undefined, so require the marker before using the new retry prefs. + if (_prefs->direct_retry_prefs_magic[0] != DIRECT_RETRY_PREFS_MAGIC_0 + || _prefs->direct_retry_prefs_magic[1] != DIRECT_RETRY_PREFS_MAGIC_1) { + _prefs->direct_retry_recent_enabled = DIRECT_RETRY_RECENT_DEFAULT; + _prefs->direct_retry_snr_margin_db = DIRECT_RETRY_SNR_MARGIN_DB_DEFAULT; + } else { + _prefs->direct_retry_recent_enabled = constrain(_prefs->direct_retry_recent_enabled, 0, 1); + _prefs->direct_retry_snr_margin_db = constrain(_prefs->direct_retry_snr_margin_db, 0, DIRECT_RETRY_SNR_MARGIN_DB_MAX); + } // sanitise bad bridge pref values _prefs->bridge_enabled = constrain(_prefs->bridge_enabled, 0, 1); @@ -140,7 +162,11 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->tx_delay_factor, sizeof(_prefs->tx_delay_factor)); // 84 file.write((uint8_t *)&_prefs->guest_password[0], sizeof(_prefs->guest_password)); // 88 file.write((uint8_t *)&_prefs->direct_tx_delay_factor, sizeof(_prefs->direct_tx_delay_factor)); // 104 - file.write(pad, 4); // 108 + file.write((uint8_t *)&_prefs->direct_retry_recent_enabled, sizeof(_prefs->direct_retry_recent_enabled)); // 108 + file.write((uint8_t *)&_prefs->direct_retry_snr_margin_db, sizeof(_prefs->direct_retry_snr_margin_db)); // 109 + // Persist a marker so later loads can distinguish real values from legacy garbage in this reserved slot. + uint8_t retry_magic[2] = { DIRECT_RETRY_PREFS_MAGIC_0, DIRECT_RETRY_PREFS_MAGIC_1 }; + file.write(retry_magic, sizeof(retry_magic)); // 110 file.write((uint8_t *)&_prefs->sf, sizeof(_prefs->sf)); // 112 file.write((uint8_t *)&_prefs->cr, sizeof(_prefs->cr)); // 113 file.write((uint8_t *)&_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114 @@ -316,6 +342,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch sprintf(reply, "> %d", (uint32_t)_prefs->flood_max); } else if (memcmp(config, "direct.txdelay", 14) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor)); + } else if (memcmp(config, "direct.retry.heard", 18) == 0) { + sprintf(reply, "> %s", _prefs->direct_retry_recent_enabled ? "on" : "off"); + } else if (memcmp(config, "direct.retry.margin", 19) == 0) { + sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_snr_margin_db); } else if (memcmp(config, "owner.info", 10) == 0) { *reply++ = '>'; *reply++ = ' '; @@ -535,6 +565,27 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch } else { strcpy(reply, "Error, cannot be negative"); } + } else if (memcmp(config, "direct.retry.heard ", 19) == 0) { + if (memcmp(&config[19], "on", 2) == 0) { + _prefs->direct_retry_recent_enabled = 1; + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(&config[19], "off", 3) == 0) { + _prefs->direct_retry_recent_enabled = 0; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be on or off"); + } + } else if (memcmp(config, "direct.retry.margin ", 20) == 0) { + int db = atoi(&config[20]); + if (db >= 0 && db <= DIRECT_RETRY_SNR_MARGIN_DB_MAX) { + _prefs->direct_retry_snr_margin_db = (uint8_t)db; + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error, min 0 and max %d", DIRECT_RETRY_SNR_MARGIN_DB_MAX); + } } else if (memcmp(config, "owner.info ", 11) == 0) { config += 11; char *dp = _prefs->owner_info; diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 8661d1e6..d0f26bfe 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -27,7 +27,9 @@ struct NodePrefs { // persisted to file float tx_delay_factor; char guest_password[16]; float direct_tx_delay_factor; - uint32_t guard; + uint8_t direct_retry_recent_enabled; + uint8_t direct_retry_snr_margin_db; + uint8_t direct_retry_prefs_magic[2]; uint8_t sf; uint8_t cr; uint8_t allow_read_only; diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index 2f8af52a..217fd5a0 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -8,13 +8,103 @@ #define MAX_PACKET_HASHES 128 #define MAX_PACKET_ACKS 64 +#define MAX_RECENT_REPEATERS 64 +#define MAX_ROUTE_HASH_BYTES 3 class SimpleMeshTables : public mesh::MeshTables { +public: + struct RecentRepeaterInfo { + // Just enough identity to match a next-hop path prefix plus the SNR that heard it. + uint8_t prefix[MAX_ROUTE_HASH_BYTES]; + uint8_t prefix_len; + int8_t snr_x4; + }; + +private: uint8_t _hashes[MAX_PACKET_HASHES*MAX_HASH_SIZE]; int _next_idx; uint32_t _acks[MAX_PACKET_ACKS]; int _next_ack_idx; uint32_t _direct_dups, _flood_dups; + RecentRepeaterInfo _recent_repeaters[MAX_RECENT_REPEATERS]; + int _next_recent_repeater_idx; + + bool hasSeenAck(uint32_t ack) const { + for (int i = 0; i < MAX_PACKET_ACKS; i++) { + if (ack == _acks[i]) { + return true; + } + } + return false; + } + + void storeAck(uint32_t ack) { + _acks[_next_ack_idx] = ack; + _next_ack_idx = (_next_ack_idx + 1) % MAX_PACKET_ACKS; + } + + bool hasSeenHash(const uint8_t* hash) const { + const uint8_t* sp = _hashes; + for (int i = 0; i < MAX_PACKET_HASHES; i++, sp += MAX_HASH_SIZE) { + if (memcmp(hash, sp, MAX_HASH_SIZE) == 0) { + return true; + } + } + return false; + } + + void storeHash(const uint8_t* hash) { + memcpy(&_hashes[_next_idx*MAX_HASH_SIZE], hash, MAX_HASH_SIZE); + _next_idx = (_next_idx + 1) % MAX_PACKET_HASHES; + } + + bool extractRecentRepeater(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const { + // Learn repeater prefixes only from packet shapes that expose a trustworthy repeater ID. + if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->payload_len >= PUB_KEY_SIZE) { + memcpy(prefix, packet->payload, MAX_ROUTE_HASH_BYTES); + prefix_len = MAX_ROUTE_HASH_BYTES; + return true; + } + + if (packet->getPayloadType() == PAYLOAD_TYPE_CONTROL + && packet->isRouteDirect() + && packet->getPathHashCount() == 0 + && packet->payload_len >= 6 + MAX_ROUTE_HASH_BYTES + && (packet->payload[0] & 0xF0) == 0x90) { + memcpy(prefix, &packet->payload[6], MAX_ROUTE_HASH_BYTES); + prefix_len = MAX_ROUTE_HASH_BYTES; + return true; + } + + if (packet->isRouteFlood() && packet->getPathHashCount() > 0) { + prefix_len = packet->getPathHashSize(); + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + + const uint8_t* last_hop = &packet->path[(packet->getPathHashCount() - 1) * packet->getPathHashSize()]; + memcpy(prefix, last_hop, prefix_len); + return true; + } + + return false; + } + + void recordRecentRepeater(const mesh::Packet* packet) { + uint8_t prefix[MAX_ROUTE_HASH_BYTES] = {0}; + uint8_t prefix_len = 0; + if (!extractRecentRepeater(packet, prefix, prefix_len) || prefix_len == 0) { + return; + } + + // Ring buffer is enough here; retry fallback only needs a recent prefix->SNR observation. + RecentRepeaterInfo& slot = _recent_repeaters[_next_recent_repeater_idx]; + memset(slot.prefix, 0, sizeof(slot.prefix)); + memcpy(slot.prefix, prefix, prefix_len); + slot.prefix_len = prefix_len; + slot.snr_x4 = packet->_snr; + _next_recent_repeater_idx = (_next_recent_repeater_idx + 1) % MAX_RECENT_REPEATERS; + } public: SimpleMeshTables() { @@ -23,6 +113,8 @@ public: memset(_acks, 0, sizeof(_acks)); _next_ack_idx = 0; _direct_dups = _flood_dups = 0; + memset(_recent_repeaters, 0, sizeof(_recent_repeaters)); + _next_recent_repeater_idx = 0; } #ifdef ESP32 @@ -31,12 +123,16 @@ public: f.read((uint8_t *) &_next_idx, sizeof(_next_idx)); f.read((uint8_t *) &_acks[0], sizeof(_acks)); f.read((uint8_t *) &_next_ack_idx, sizeof(_next_ack_idx)); + f.read((uint8_t *) &_recent_repeaters[0], sizeof(_recent_repeaters)); + f.read((uint8_t *) &_next_recent_repeater_idx, sizeof(_next_recent_repeater_idx)); } void saveTo(File f) { f.write(_hashes, sizeof(_hashes)); f.write((const uint8_t *) &_next_idx, sizeof(_next_idx)); f.write((const uint8_t *) &_acks[0], sizeof(_acks)); f.write((const uint8_t *) &_next_ack_idx, sizeof(_next_ack_idx)); + f.write((const uint8_t *) &_recent_repeaters[0], sizeof(_recent_repeaters)); + f.write((const uint8_t *) &_next_recent_repeater_idx, sizeof(_next_recent_repeater_idx)); } #endif @@ -44,28 +140,8 @@ public: if (packet->getPayloadType() == PAYLOAD_TYPE_ACK) { uint32_t ack; memcpy(&ack, packet->payload, 4); - for (int i = 0; i < MAX_PACKET_ACKS; i++) { - if (ack == _acks[i]) { - if (packet->isRouteDirect()) { - _direct_dups++; // keep some stats - } else { - _flood_dups++; - } - return true; - } - } - - _acks[_next_ack_idx] = ack; - _next_ack_idx = (_next_ack_idx + 1) % MAX_PACKET_ACKS; // cyclic table - return false; - } - uint8_t hash[MAX_HASH_SIZE]; - packet->calculatePacketHash(hash); - - const uint8_t* sp = _hashes; - for (int i = 0; i < MAX_PACKET_HASHES; i++, sp += MAX_HASH_SIZE) { - if (memcmp(hash, sp, MAX_HASH_SIZE) == 0) { + if (hasSeenAck(ack)) { if (packet->isRouteDirect()) { _direct_dups++; // keep some stats } else { @@ -73,13 +149,46 @@ public: } return true; } + + storeAck(ack); + return false; } - memcpy(&_hashes[_next_idx*MAX_HASH_SIZE], hash, MAX_HASH_SIZE); - _next_idx = (_next_idx + 1) % MAX_PACKET_HASHES; // cyclic table + uint8_t hash[MAX_HASH_SIZE]; + packet->calculatePacketHash(hash); + + if (hasSeenHash(hash)) { + if (packet->isRouteDirect()) { + _direct_dups++; // keep some stats + } else { + _flood_dups++; + } + return true; + } + + storeHash(hash); + recordRecentRepeater(packet); return false; } + void markSent(const mesh::Packet* packet) override { + // Outbound packets must be marked as already-sent without teaching the recent-heard cache about ourselves. + if (packet->getPayloadType() == PAYLOAD_TYPE_ACK) { + uint32_t ack; + memcpy(&ack, packet->payload, 4); + if (!hasSeenAck(ack)) { + storeAck(ack); + } + return; + } + + uint8_t hash[MAX_HASH_SIZE]; + packet->calculatePacketHash(hash); + if (!hasSeenHash(hash)) { + storeHash(hash); + } + } + void clear(const mesh::Packet* packet) override { if (packet->getPayloadType() == PAYLOAD_TYPE_ACK) { uint32_t ack; @@ -107,5 +216,24 @@ public: uint32_t getNumDirectDups() const { return _direct_dups; } uint32_t getNumFloodDups() const { return _flood_dups; } + const RecentRepeaterInfo* findRecentRepeaterByHash(const uint8_t* hash, uint8_t hash_len) const { + if (hash == NULL || hash_len == 0) { + return NULL; + } + + // Search newest-to-oldest so the retry gate prefers the freshest SNR sample for a prefix. + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + int idx = (_next_recent_repeater_idx - 1 - i + MAX_RECENT_REPEATERS) % MAX_RECENT_REPEATERS; + const RecentRepeaterInfo* info = &_recent_repeaters[idx]; + if (info->prefix_len < hash_len || info->prefix_len == 0) { + continue; + } + if (memcmp(info->prefix, hash, hash_len) == 0) { + return info; + } + } + return NULL; + } + void resetStats() { _direct_dups = _flood_dups = 0; } }; From dbc4dda0ca67a84f2bfccbd950e0608527f8e0ab Mon Sep 17 00:00:00 2001 From: mikecarper Date: Thu, 23 Apr 2026 16:25:51 -0700 Subject: [PATCH 089/214] Retry 3 times with a 200ms,300ms,400ms backoff. --- docs/cli_commands.md | 13 +++++ examples/simple_repeater/MyMesh.cpp | 85 ++++++++++++++++++++++++++++ examples/simple_repeater/MyMesh.h | 2 + src/Dispatcher.h | 2 + src/Mesh.cpp | 55 +++++++++++++++++- src/Mesh.h | 6 ++ src/helpers/SimpleMeshTables.h | 86 +++++++++++++++++++++++++---- 7 files changed, 236 insertions(+), 13 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index c6a7fac0..9fa175a2 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -98,6 +98,19 @@ --- +### Get or set recent repeater fallback prefix/SNR +**Usage:** +- `recent.repeater` +- `recent.repeater ` + +**Parameters:** +- `prefix_hex`: 1-3 bytes of next-hop prefix (hex) +- `snr_db`: SNR in dB (supports decimals; stored at x4 precision) + +**Note:** `set` is rejected when the prefix already exists in neighbors. + +--- + ## Statistics ### Clear Stats diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index f268679c..9d9e7d03 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -77,6 +77,15 @@ const NeighbourInfo* MyMesh::findNeighbourByHash(const uint8_t* hash, uint8_t ha return NULL; } +bool MyMesh::allowRecentRepeaterPrefixStore(const uint8_t* prefix, uint8_t prefix_len, void* ctx) { + if (ctx == NULL || prefix == NULL || prefix_len == 0) { + return true; + } + + const MyMesh* self = (const MyMesh*) ctx; + return self->findNeighbourByHash(prefix, prefix_len) == NULL; +} + void MyMesh::putNeighbour(const mesh::Identity &id, uint32_t timestamp, float snr) { #if MAX_NEIGHBOURS // check if neighbours enabled // find existing neighbour, else use least recently updated @@ -510,6 +519,34 @@ void MyMesh::logTxFail(mesh::Packet *pkt, int len) { } } +void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis) { + if (packet == NULL) { + return; + } + + MESH_DEBUG_PRINTLN("%s direct retry %s (type=%d, route=%s, payload_len=%d, delay=%lu)", + getLogDateTime(), + event, + (uint32_t)packet->getPayloadType(), + packet->isRouteDirect() ? "D" : "F", + (uint32_t)packet->payload_len, + (unsigned long)delay_millis); + + if (_logging) { + File f = openAppend(PACKET_LOG_FILE); + if (f) { + f.print(getLogDateTime()); + f.printf(": DIRECT RETRY %s (type=%d, route=%s, payload_len=%d, delay=%lu)\n", + event, + (uint32_t)packet->getPayloadType(), + packet->isRouteDirect() ? "D" : "F", + (uint32_t)packet->payload_len, + (unsigned long)delay_millis); + f.close(); + } + } +} + int MyMesh::calcRxDelay(float score, uint32_t air_time) const { if (_prefs.rx_delay_base <= 0.0f) return 0; return (int)((pow(_prefs.rx_delay_base, 0.85f - score) - 1.0) * air_time); @@ -880,6 +917,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc active_bw = _prefs.bw; active_sf = _prefs.sf; active_cr = _prefs.cr; + + ((SimpleMeshTables *)getTables())->setRecentRepeaterAllowFilter(&MyMesh::allowRecentRepeaterPrefixStore, this); } void MyMesh::begin(FILESYSTEM *fs) { @@ -901,6 +940,7 @@ void MyMesh::begin(FILESYSTEM *fs) { active_bw = _prefs.bw; active_sf = _prefs.sf; active_cr = _prefs.cr; + ((SimpleMeshTables *)getTables())->setRecentRepeaterMinSNRX4(getDirectRetryMinSNRX4()); radio_set_tx_power(_prefs.tx_power_dbm); updateAdvertTimer(); @@ -1250,6 +1290,48 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } else { strcpy(reply, "Err - ??"); } + } else if (memcmp(command, "recent.repeater", 15) == 0) { + const char* sub = command + 15; + while (*sub == ' ') sub++; + auto* tables = (SimpleMeshTables*)getTables(); + if (*sub == 0) { + const auto* info = tables->getLatestRecentRepeater(); + if (info == NULL) { + strcpy(reply, "> none"); + } else { + char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; + mesh::Utils::toHex(hex, info->prefix, info->prefix_len); + sprintf(reply, "> %s,%s", hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); + } + } else { + char* params = (char*) sub; + char* arg_snr = strchr(params, ' '); + if (arg_snr == NULL) { + strcpy(reply, "Err - usage: recent.repeater "); + } else { + *arg_snr++ = 0; + while (*arg_snr == ' ') arg_snr++; + if (*arg_snr == 0) { + strcpy(reply, "Err - usage: recent.repeater "); + } else { + int hex_len = strlen(params); + int prefix_len = hex_len / 2; + uint8_t prefix[MAX_ROUTE_HASH_BYTES] = {0}; + if ((hex_len % 2) != 0 || prefix_len <= 0 || prefix_len > MAX_ROUTE_HASH_BYTES || !mesh::Utils::fromHex(prefix, prefix_len, params)) { + strcpy(reply, "Err - prefix must be 1-3 bytes hex"); + } else { + float snr_db = strtof(arg_snr, nullptr); + int snr_x4 = (int)(snr_db * 4.0f + (snr_db >= 0.0f ? 0.5f : -0.5f)); + snr_x4 = constrain(snr_x4, -128, 127); + if (tables->setRecentRepeater(prefix, (uint8_t)prefix_len, (int8_t)snr_x4)) { + strcpy(reply, "OK"); + } else { + strcpy(reply, "Err - prefix is already in neighbors"); + } + } + } + } + } } else{ _cli.handleCommand(sender_timestamp, command, reply); // common CLI commands } @@ -1293,6 +1375,9 @@ void MyMesh::loop() { MESH_DEBUG_PRINTLN("Radio params restored"); } + // Keep recent-prefix learning aligned with the live retry SNR gate. + ((SimpleMeshTables *)getTables())->setRecentRepeaterMinSNRX4(getDirectRetryMinSNRX4()); + // is pending dirty contacts write needed? if (dirty_contacts_expiry && millisHasNowPassed(dirty_contacts_expiry)) { acl.save(_fs); diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 31528829..4b5d7b1f 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -119,6 +119,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { #endif const NeighbourInfo* findNeighbourByHash(const uint8_t* hash, uint8_t hash_len) const; + static bool allowRecentRepeaterPrefixStore(const uint8_t* prefix, uint8_t prefix_len, void* ctx); int8_t getDirectRetryMinSNRX4() const; void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr); uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood); @@ -148,6 +149,7 @@ protected: uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override; bool allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const override; uint32_t getDirectRetryEchoDelay(const mesh::Packet* packet) const override; + void onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis) override; int getInterferenceThreshold() const override { return _prefs.interference_threshold; diff --git a/src/Dispatcher.h b/src/Dispatcher.h index 29460061..970e1975 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -161,6 +161,8 @@ protected: virtual uint32_t getCADFailMaxDuration() const; virtual int getInterferenceThreshold() const { return 0; } // disabled by default virtual int getAGCResetInterval() const { return 0; } // disabled by default + virtual unsigned long getDutyCycleWindowMs() const { return 3600000; } + const Packet* getOutboundInFlight() const { return outbound; } public: void begin(); diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 082b6bb4..b4710b8c 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -3,12 +3,16 @@ namespace mesh { +static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS = 3; +static const uint32_t DIRECT_RETRY_BACKOFF_MS[DIRECT_RETRY_MAX_ATTEMPTS] = { 200, 300, 400 }; + void Mesh::begin() { for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { _direct_retries[i].packet = NULL; _direct_retries[i].trigger_packet = NULL; _direct_retries[i].retry_at = 0; _direct_retries[i].retry_delay = 0; + _direct_retries[i].retry_attempts_sent = 0; _direct_retries[i].priority = 0; _direct_retries[i].progress_marker = 0; _direct_retries[i].expect_path_growth = false; @@ -27,6 +31,9 @@ void Mesh::loop() { } if (!isDirectRetryQueued(_direct_retries[i].packet)) { + if (_direct_retries[i].packet == getOutboundInFlight()) { + continue; // currently transmitting; keep slot until onSendComplete/onSendFail emits event + } clearDirectRetrySlot(i); } } @@ -443,6 +450,7 @@ void Mesh::clearDirectRetrySlot(int idx) { _direct_retries[idx].trigger_packet = NULL; _direct_retries[idx].retry_at = 0; _direct_retries[idx].retry_delay = 0; + _direct_retries[idx].retry_attempts_sent = 0; _direct_retries[idx].priority = 0; _direct_retries[idx].progress_marker = 0; _direct_retries[idx].expect_path_growth = false; @@ -491,8 +499,12 @@ bool Mesh::cancelDirectRetryOnEcho(const Packet* packet) { break; } } + onDirectRetryEvent("canceled_echo", _direct_retries[i].packet, 0); + onDirectRetryEvent("good", _direct_retries[i].packet, 0); clearDirectRetrySlot(i); } else { + onDirectRetryEvent("canceled_echo", _direct_retries[i].trigger_packet, 0); + onDirectRetryEvent("good", _direct_retries[i].trigger_packet, 0); clearDirectRetrySlot(i); } cleared = true; @@ -510,7 +522,35 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { if (_direct_retries[i].queued) { if (_direct_retries[i].packet == packet) { // The retry packet itself just finished transmitting; Dispatcher will release it after this hook. - clearDirectRetrySlot(i); + onDirectRetryEvent("resent", packet, 0); + _direct_retries[i].retry_attempts_sent++; + if (_direct_retries[i].retry_attempts_sent >= DIRECT_RETRY_MAX_ATTEMPTS) { + onDirectRetryEvent("failure", packet, 0); + clearDirectRetrySlot(i); + continue; + } + + Packet* retry = obtainNewPacket(); + if (retry == NULL) { + onDirectRetryEvent("dropped_no_packet", packet, 0); + onDirectRetryEvent("failure", packet, 0); + clearDirectRetrySlot(i); + continue; + } + + *retry = *packet; + uint32_t retry_delay = DIRECT_RETRY_BACKOFF_MS[_direct_retries[i].retry_attempts_sent]; + sendPacket(retry, _direct_retries[i].priority, retry_delay); + if (isDirectRetryQueued(retry)) { + _direct_retries[i].packet = retry; + _direct_retries[i].retry_delay = retry_delay; + _direct_retries[i].retry_at = futureMillis(retry_delay); + onDirectRetryEvent("queued", retry, retry_delay); + } else { + onDirectRetryEvent("dropped_queue_full", retry, retry_delay); + onDirectRetryEvent("failure", retry, 0); + clearDirectRetrySlot(i); + } } continue; } @@ -522,6 +562,8 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { // Allocate the retry packet only after TX-complete so busy repeaters do not reserve pool slots early. Packet* retry = obtainNewPacket(); if (retry == NULL) { + onDirectRetryEvent("dropped_no_packet", packet, _direct_retries[i].retry_delay); + onDirectRetryEvent("failure", packet, 0); clearDirectRetrySlot(i); continue; } @@ -535,7 +577,10 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { _direct_retries[i].trigger_packet = NULL; _direct_retries[i].queued = true; _direct_retries[i].retry_at = futureMillis(_direct_retries[i].retry_delay); + onDirectRetryEvent("queued", retry, _direct_retries[i].retry_delay); } else { + onDirectRetryEvent("dropped_queue_full", retry, _direct_retries[i].retry_delay); + onDirectRetryEvent("failure", retry, 0); clearDirectRetrySlot(i); } } @@ -550,12 +595,16 @@ void Mesh::clearPendingDirectRetryOnSendFail(const Packet* packet) { if (_direct_retries[i].queued) { if (_direct_retries[i].packet == packet) { // The queued retry itself failed; Dispatcher will release it after this hook. + onDirectRetryEvent("dropped_send_fail", packet, 0); + onDirectRetryEvent("failure", packet, 0); clearDirectRetrySlot(i); } continue; } if (_direct_retries[i].trigger_packet == packet) { + onDirectRetryEvent("dropped_send_fail", packet, 0); + onDirectRetryEvent("failure", packet, 0); clearDirectRetrySlot(i); } } @@ -638,17 +687,19 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { } // Only store retry metadata here; allocate the retry packet after the initial TX really completes. - uint32_t retry_delay = getDirectRetryEchoDelay(packet); + uint32_t retry_delay = DIRECT_RETRY_BACKOFF_MS[0]; calculateDirectRetryKey(packet, _direct_retries[slot_idx].retry_key); _direct_retries[slot_idx].packet = NULL; _direct_retries[slot_idx].trigger_packet = const_cast(packet); _direct_retries[slot_idx].retry_at = 0; _direct_retries[slot_idx].retry_delay = retry_delay; + _direct_retries[slot_idx].retry_attempts_sent = 0; _direct_retries[slot_idx].priority = priority; _direct_retries[slot_idx].progress_marker = progress_marker; _direct_retries[slot_idx].expect_path_growth = expect_path_growth; _direct_retries[slot_idx].queued = false; _direct_retries[slot_idx].active = true; + onDirectRetryEvent("armed", packet, retry_delay); } Packet* Mesh::createAdvert(const LocalIdentity& id, const uint8_t* app_data, size_t app_data_len) { diff --git a/src/Mesh.h b/src/Mesh.h index 874336b0..89b6bed4 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -34,6 +34,7 @@ class Mesh : public Dispatcher { Packet* trigger_packet; unsigned long retry_at; uint32_t retry_delay; + uint8_t retry_attempts_sent; uint8_t retry_key[MAX_HASH_SIZE]; uint8_t priority; uint8_t progress_marker; @@ -111,6 +112,11 @@ protected: */ virtual uint8_t getExtraAckTransmitCount() const; + /** + * \brief Optional hook for logging direct-retry lifecycle events. + */ + virtual void onDirectRetryEvent(const char* event, const Packet* packet, uint32_t delay_millis) { } + /** * \brief Perform search of local DB of peers/contacts. * \returns Number of peers with matching hash diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index 217fd5a0..f5da272b 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -13,6 +13,8 @@ class SimpleMeshTables : public mesh::MeshTables { public: + typedef bool (*RecentRepeaterAllowFn)(const uint8_t* prefix, uint8_t prefix_len, void* ctx); + struct RecentRepeaterInfo { // Just enough identity to match a next-hop path prefix plus the SNR that heard it. uint8_t prefix[MAX_ROUTE_HASH_BYTES]; @@ -28,6 +30,9 @@ private: uint32_t _direct_dups, _flood_dups; RecentRepeaterInfo _recent_repeaters[MAX_RECENT_REPEATERS]; int _next_recent_repeater_idx; + int8_t _recent_repeater_min_snr_x4; + RecentRepeaterAllowFn _recent_repeater_allow_fn; + void* _recent_repeater_allow_ctx; bool hasSeenAck(uint32_t ack) const { for (int i = 0; i < MAX_PACKET_ACKS; i++) { @@ -58,6 +63,11 @@ private: _next_idx = (_next_idx + 1) % MAX_PACKET_HASHES; } + bool prefixesOverlap(const uint8_t* a, uint8_t a_len, const uint8_t* b, uint8_t b_len) const { + uint8_t n = a_len < b_len ? a_len : b_len; + return n > 0 && memcmp(a, b, n) == 0; + } + bool extractRecentRepeater(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const { // Learn repeater prefixes only from packet shapes that expose a trustworthy repeater ID. if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->payload_len >= PUB_KEY_SIZE) { @@ -96,14 +106,10 @@ private: if (!extractRecentRepeater(packet, prefix, prefix_len) || prefix_len == 0) { return; } - - // Ring buffer is enough here; retry fallback only needs a recent prefix->SNR observation. - RecentRepeaterInfo& slot = _recent_repeaters[_next_recent_repeater_idx]; - memset(slot.prefix, 0, sizeof(slot.prefix)); - memcpy(slot.prefix, prefix, prefix_len); - slot.prefix_len = prefix_len; - slot.snr_x4 = packet->_snr; - _next_recent_repeater_idx = (_next_recent_repeater_idx + 1) % MAX_RECENT_REPEATERS; + if (packet->_snr < _recent_repeater_min_snr_x4) { + return; + } + setRecentRepeater(prefix, prefix_len, packet->_snr); } public: @@ -115,6 +121,9 @@ public: _direct_dups = _flood_dups = 0; memset(_recent_repeaters, 0, sizeof(_recent_repeaters)); _next_recent_repeater_idx = 0; + _recent_repeater_min_snr_x4 = -128; + _recent_repeater_allow_fn = NULL; + _recent_repeater_allow_ctx = NULL; } #ifdef ESP32 @@ -216,19 +225,74 @@ public: uint32_t getNumDirectDups() const { return _direct_dups; } uint32_t getNumFloodDups() const { return _flood_dups; } + void setRecentRepeaterMinSNRX4(int8_t min_snr_x4) { + _recent_repeater_min_snr_x4 = min_snr_x4; + } + void setRecentRepeaterAllowFilter(RecentRepeaterAllowFn fn, void* ctx) { + _recent_repeater_allow_fn = fn; + _recent_repeater_allow_ctx = ctx; + } + bool setRecentRepeater(const uint8_t* prefix, uint8_t prefix_len, int8_t snr_x4) { + if (prefix == NULL || prefix_len == 0) { + return false; + } + + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + + if (_recent_repeater_allow_fn != NULL && !_recent_repeater_allow_fn(prefix, prefix_len, _recent_repeater_allow_ctx)) { + return false; + } + + // Keep one slot for overlapping prefixes so 1/2/3-byte paths share the same entry. + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + RecentRepeaterInfo& existing = _recent_repeaters[i]; + if (existing.prefix_len == 0 || !prefixesOverlap(existing.prefix, existing.prefix_len, prefix, prefix_len)) { + continue; + } + if (prefix_len > existing.prefix_len) { + memset(existing.prefix, 0, sizeof(existing.prefix)); + memcpy(existing.prefix, prefix, prefix_len); + existing.prefix_len = prefix_len; + } + existing.snr_x4 = snr_x4; + return true; + } + + // Ring buffer is enough here; retry fallback only needs a recent prefix->SNR observation. + RecentRepeaterInfo& slot = _recent_repeaters[_next_recent_repeater_idx]; + memset(slot.prefix, 0, sizeof(slot.prefix)); + memcpy(slot.prefix, prefix, prefix_len); + slot.prefix_len = prefix_len; + slot.snr_x4 = snr_x4; + _next_recent_repeater_idx = (_next_recent_repeater_idx + 1) % MAX_RECENT_REPEATERS; + return true; + } + const RecentRepeaterInfo* getLatestRecentRepeater() const { + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + int idx = (_next_recent_repeater_idx - 1 - i + MAX_RECENT_REPEATERS) % MAX_RECENT_REPEATERS; + const RecentRepeaterInfo* info = &_recent_repeaters[idx]; + if (info->prefix_len > 0) { + return info; + } + } + return NULL; + } + const RecentRepeaterInfo* findRecentRepeaterByHash(const uint8_t* hash, uint8_t hash_len) const { if (hash == NULL || hash_len == 0) { return NULL; } - // Search newest-to-oldest so the retry gate prefers the freshest SNR sample for a prefix. + // Search newest-to-oldest and allow 1/2/3-byte prefixes to overlap-match. for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { int idx = (_next_recent_repeater_idx - 1 - i + MAX_RECENT_REPEATERS) % MAX_RECENT_REPEATERS; const RecentRepeaterInfo* info = &_recent_repeaters[idx]; - if (info->prefix_len < hash_len || info->prefix_len == 0) { + if (info->prefix_len == 0) { continue; } - if (memcmp(info->prefix, hash, hash_len) == 0) { + if (prefixesOverlap(info->prefix, info->prefix_len, hash, hash_len)) { return info; } } From b125d0a0d1e9d4cef975ee091f193bf32a7ee2ab Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 24 Apr 2026 00:08:10 -0700 Subject: [PATCH 090/214] Max retries is now a var that can be set between 1 to 15 --- docs/cli_commands.md | 39 +++++++- examples/simple_repeater/MyMesh.cpp | 136 ++++++++++++++++++++++++---- examples/simple_repeater/MyMesh.h | 1 + src/Mesh.cpp | 30 ++++-- src/Mesh.h | 10 ++ src/helpers/CommonCLI.cpp | 55 ++++++++++- src/helpers/CommonCLI.h | 3 + src/helpers/SimpleMeshTables.h | 45 +++++++++ 8 files changed, 287 insertions(+), 32 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 9fa175a2..bbd28693 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -100,14 +100,21 @@ ### Get or set recent repeater fallback prefix/SNR **Usage:** -- `recent.repeater` -- `recent.repeater ` +- `get recent.repeater` +- `get recent.repeater all` +- `get recent.repeater first ` +- `get recent.repeater last ` +- `set recent.repeater ` **Parameters:** - `prefix_hex`: 1-3 bytes of next-hop prefix (hex) - `snr_db`: SNR in dB (supports decimals; stored at x4 precision) +- `count`: number of entries to print -**Note:** `set` is rejected when the prefix already exists in neighbors. +**Notes:** +- `set` is rejected when the prefix already exists in neighbors. +- `all` prints oldest to newest; `first` prints the oldest N; `last` prints the newest N. +- Remote CLI replies include rows too, but may truncate when the packet payload limit is reached. --- @@ -446,6 +453,32 @@ --- +#### View or change the number of direct retry attempts +**Usage:** +- `get direct.retry.count` +- `set direct.retry.count ` + +**Parameters:** +- `value`: Retry attempts after initial TX (`1`-`15`) + +**Default:** `3` + +--- + +#### View or change the base direct retry wait (milliseconds) +**Usage:** +- `get direct.retry.base` +- `set direct.retry.base ` + +**Parameters:** +- `value`: Base wait in milliseconds (`10`-`5000`) + +**Default:** `200` + +**Note:** The actual first retry wait is `base + computed_echo_wait_from_live_phy`. + +--- + #### [Experimental] View or change the processing delay for received traffic **Usage:** - `get rxdelay` diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 9d9e7d03..3e8985e8 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -587,16 +587,21 @@ bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_ho return recent != NULL && recent->snr_x4 >= min_snr_x4; } uint32_t MyMesh::getDirectRetryEchoDelay(const mesh::Packet* packet) const { + uint32_t base_wait_millis = constrain((uint32_t)_prefs.direct_retry_base_ms, (uint32_t)10, (uint32_t)5000); // Approximate LoRa line rate in kilobits/sec from the live radio params the repeater is using now. float kbps = (((float) active_sf) * active_bw * ((float) active_cr)) / ((float) (1UL << active_sf)); if (kbps <= 0.0f) { - return HALO_DIRECT_RETRY_DELAY_MIN; + return base_wait_millis; } // Wait roughly long enough for our transmission, the next hop's receive/forward window, and its echo back. uint32_t bits = ((uint32_t) packet->getRawLength()) * 8; uint32_t scaled_wait_millis = (uint32_t) ((((float) bits) * 4.0f) / kbps); - return max((uint32_t) HALO_DIRECT_RETRY_DELAY_MIN, scaled_wait_millis); + return base_wait_millis + scaled_wait_millis; +} +uint8_t MyMesh::getDirectRetryMaxAttempts(const mesh::Packet* packet) const { + (void)packet; + return constrain(_prefs.direct_retry_attempts, (uint8_t)1, (uint8_t)15); } bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) { @@ -885,6 +890,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.direct_tx_delay_factor = 0.3f; // was 0.2 _prefs.direct_retry_recent_enabled = 0; _prefs.direct_retry_snr_margin_db = 5; + _prefs.direct_retry_attempts = 3; + _prefs.direct_retry_base_ms = 200; StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); _prefs.node_lat = ADVERT_LAT; _prefs.node_lon = ADVERT_LON; @@ -1290,29 +1297,36 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } else { strcpy(reply, "Err - ??"); } - } else if (memcmp(command, "recent.repeater", 15) == 0) { - const char* sub = command + 15; - while (*sub == ' ') sub++; - auto* tables = (SimpleMeshTables*)getTables(); - if (*sub == 0) { - const auto* info = tables->getLatestRecentRepeater(); - if (info == NULL) { - strcpy(reply, "> none"); - } else { - char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; - mesh::Utils::toHex(hex, info->prefix, info->prefix_len); - sprintf(reply, "> %s,%s", hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); - } + } else if (strncmp(command, "get recent.repeater", 19) == 0 + || strncmp(command, "set recent.repeater", 19) == 0 + || strncmp(command, "recent.repeater", 15) == 0) { + bool is_get = false; + bool is_set = false; + const char* sub = command; + + if (strncmp(command, "get recent.repeater", 19) == 0) { + is_get = true; + sub = command + 19; + } else if (strncmp(command, "set recent.repeater", 19) == 0) { + is_set = true; + sub = command + 19; } else { + sub = command + 15; // legacy command format + } + while (*sub == ' ') sub++; + + auto* tables = (SimpleMeshTables*)getTables(); + + if (is_set || (!is_get && *sub != 0 && strcmp(sub, "all") != 0 && strncmp(sub, "first ", 6) != 0 && strncmp(sub, "last ", 5) != 0)) { char* params = (char*) sub; char* arg_snr = strchr(params, ' '); if (arg_snr == NULL) { - strcpy(reply, "Err - usage: recent.repeater "); + strcpy(reply, "Err - usage: set recent.repeater "); } else { *arg_snr++ = 0; while (*arg_snr == ' ') arg_snr++; if (*arg_snr == 0) { - strcpy(reply, "Err - usage: recent.repeater "); + strcpy(reply, "Err - usage: set recent.repeater "); } else { int hex_len = strlen(params); int prefix_len = hex_len / 2; @@ -1331,6 +1345,94 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } } } + } else if (*sub == 0) { + const auto* info = tables->getLatestRecentRepeater(); + if (info == NULL) { + strcpy(reply, "> none"); + } else { + char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; + mesh::Utils::toHex(hex, info->prefix, info->prefix_len); + sprintf(reply, "> %s,%s", hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); + } + } else if (strcmp(sub, "all") == 0 || strncmp(sub, "first ", 6) == 0 || strncmp(sub, "last ", 5) == 0) { + int total = tables->getRecentRepeaterCount(); + if (total <= 0) { + strcpy(reply, "> none"); + } else { + bool newest_first = false; + int limit = total; + const char* mode = "all"; + if (strncmp(sub, "first ", 6) == 0 || strncmp(sub, "last ", 5) == 0) { + const char* nstr = sub + (sub[0] == 'f' ? 6 : 5); + while (*nstr == ' ') nstr++; + if (*nstr == 0) { + strcpy(reply, "Err - usage: get recent.repeater first|last "); + return; + } + char* end_ptr = NULL; + long parsed = strtol(nstr, &end_ptr, 10); + while (end_ptr != NULL && *end_ptr == ' ') end_ptr++; + if (end_ptr == NULL || *end_ptr != 0 || parsed <= 0) { + strcpy(reply, "Err - count must be > 0"); + return; + } + limit = (int)parsed; + if (sub[0] == 'l') { + newest_first = true; + mode = "last"; + } else { + mode = "first"; + } + } + if (limit > total) { + limit = total; + } + + if (sender_timestamp == 0) { + Serial.printf("Recent repeater table (%s %d/%d):\n", mode, limit, total); + for (int i = 0; i < limit; i++) { + const auto* info = newest_first ? tables->getRecentRepeaterNewestByIdx(i) : tables->getRecentRepeaterOldestByIdx(i); + if (info == NULL) { + continue; + } + char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; + mesh::Utils::toHex(hex, info->prefix, info->prefix_len); + Serial.printf("%02d: %s,%s\n", i + 1, hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); + } + sprintf(reply, "> showing %d/%d (%s)", limit, total, mode); + } else { + // Remote CLI replies are packet-bound, so include as many rows as fit. + int written = snprintf(reply, 160, "> showing %d/%d (%s)", limit, total, mode); + bool truncated = false; + if (written < 0) { + reply[0] = 0; + written = 0; + } + for (int i = 0; i < limit; i++) { + const auto* info = newest_first ? tables->getRecentRepeaterNewestByIdx(i) : tables->getRecentRepeaterOldestByIdx(i); + if (info == NULL) { + continue; + } + if (written >= 154) { + truncated = true; + break; + } + char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; + mesh::Utils::toHex(hex, info->prefix, info->prefix_len); + int n = snprintf(reply + written, 160 - written, "\n%02d:%s,%s", i + 1, hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); + if (n < 0 || n >= (160 - written)) { + truncated = true; + break; + } + written += n; + } + if (truncated && written < 156) { + snprintf(reply + written, 160 - written, "\n..."); + } + } + } + } else { + strcpy(reply, "Err - usage: get recent.repeater [all|first |last ]"); } } else{ _cli.handleCommand(sender_timestamp, command, reply); // common CLI commands diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 4b5d7b1f..3ecbfc81 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -149,6 +149,7 @@ protected: uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override; bool allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const override; uint32_t getDirectRetryEchoDelay(const mesh::Packet* packet) const override; + uint8_t getDirectRetryMaxAttempts(const mesh::Packet* packet) const override; void onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis) override; int getInterferenceThreshold() const override { diff --git a/src/Mesh.cpp b/src/Mesh.cpp index b4710b8c..be2c81f1 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -3,8 +3,8 @@ namespace mesh { -static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS = 3; -static const uint32_t DIRECT_RETRY_BACKOFF_MS[DIRECT_RETRY_MAX_ATTEMPTS] = { 200, 300, 400 }; +static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT = 3; +static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX = 15; void Mesh::begin() { for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { @@ -57,6 +57,14 @@ uint32_t Mesh::getDirectRetryEchoDelay(const Packet* packet) const { // Keep the base fallback aligned with the repeater's minimum retry wait. return 200; } +uint8_t Mesh::getDirectRetryMaxAttempts(const Packet* packet) const { + return DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT; +} +uint32_t Mesh::getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx) const { + uint32_t base = getDirectRetryEchoDelay(packet); + // Keep the historical linear spacing while allowing the base wait to vary by platform/profile. + return base + ((uint32_t)attempt_idx * 100UL); +} uint8_t Mesh::getExtraAckTransmitCount() const { return 0; } @@ -524,7 +532,13 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { // The retry packet itself just finished transmitting; Dispatcher will release it after this hook. onDirectRetryEvent("resent", packet, 0); _direct_retries[i].retry_attempts_sent++; - if (_direct_retries[i].retry_attempts_sent >= DIRECT_RETRY_MAX_ATTEMPTS) { + uint8_t max_attempts = getDirectRetryMaxAttempts(packet); + if (max_attempts < 1) { + max_attempts = 1; + } else if (max_attempts > DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX) { + max_attempts = DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX; + } + if (_direct_retries[i].retry_attempts_sent >= max_attempts) { onDirectRetryEvent("failure", packet, 0); clearDirectRetrySlot(i); continue; @@ -539,7 +553,7 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { } *retry = *packet; - uint32_t retry_delay = DIRECT_RETRY_BACKOFF_MS[_direct_retries[i].retry_attempts_sent]; + uint32_t retry_delay = getDirectRetryAttemptDelay(packet, _direct_retries[i].retry_attempts_sent); sendPacket(retry, _direct_retries[i].priority, retry_delay); if (isDirectRetryQueued(retry)) { _direct_retries[i].packet = retry; @@ -619,7 +633,9 @@ bool Mesh::getDirectRetryTarget(const Packet* packet, const uint8_t*& next_hop_h case PAYLOAD_TYPE_RESPONSE: case PAYLOAD_TYPE_TXT_MSG: case PAYLOAD_TYPE_ANON_REQ: - if (packet->getPathHashCount() <= 1) { + // Allow retries even when only one downstream hop remains so fixed direct paths + // (e.g. remote admin/login over 2-hop chains) use the same retry policy. + if (packet->getPathHashCount() == 0) { return false; } next_hop_hash = packet->path; @@ -629,7 +645,7 @@ bool Mesh::getDirectRetryTarget(const Packet* packet, const uint8_t*& next_hop_h return true; case PAYLOAD_TYPE_MULTIPART: - if (packet->payload_len < 1 || (packet->payload[0] & 0x0F) != PAYLOAD_TYPE_ACK || packet->getPathHashCount() <= 1) { + if (packet->payload_len < 1 || (packet->payload[0] & 0x0F) != PAYLOAD_TYPE_ACK || packet->getPathHashCount() == 0) { return false; } next_hop_hash = packet->path; @@ -687,7 +703,7 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { } // Only store retry metadata here; allocate the retry packet after the initial TX really completes. - uint32_t retry_delay = DIRECT_RETRY_BACKOFF_MS[0]; + uint32_t retry_delay = getDirectRetryAttemptDelay(packet, 0); calculateDirectRetryKey(packet, _direct_retries[slot_idx].retry_key); _direct_retries[slot_idx].packet = NULL; _direct_retries[slot_idx].trigger_packet = const_cast(packet); diff --git a/src/Mesh.h b/src/Mesh.h index 89b6bed4..dd58d754 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -107,6 +107,16 @@ protected: */ virtual uint32_t getDirectRetryEchoDelay(const Packet* packet) const; + /** + * \returns maximum number of retry transmissions after the initial direct TX. + */ + virtual uint8_t getDirectRetryMaxAttempts(const Packet* packet) const; + + /** + * \returns delay before a specific retry attempt, where attempt_idx=0 is the first retry. + */ + virtual uint32_t getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx) const; + /** * \returns number of extra (Direct) ACK transmissions wanted. */ diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 0f954efc..bae389ba 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -14,6 +14,14 @@ #define DIRECT_RETRY_RECENT_DEFAULT 0 #define DIRECT_RETRY_SNR_MARGIN_DB_DEFAULT 5 #define DIRECT_RETRY_SNR_MARGIN_DB_MAX 40 +#define DIRECT_RETRY_TIMING_MAGIC_0 0xD5 +#define DIRECT_RETRY_TIMING_MAGIC_1 0x54 +#define DIRECT_RETRY_COUNT_DEFAULT 3 +#define DIRECT_RETRY_COUNT_MIN 1 +#define DIRECT_RETRY_COUNT_MAX 15 +#define DIRECT_RETRY_BASE_MS_DEFAULT 200 +#define DIRECT_RETRY_BASE_MS_MIN 10 +#define DIRECT_RETRY_BASE_MS_MAX 5000 // Believe it or not, this std C function is busted on some platforms! static uint32_t _atoi(const char* sp) { @@ -92,9 +100,12 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->gps_interval, sizeof(_prefs->gps_interval)); // 157 file.read((uint8_t *)&_prefs->advert_loc_policy, sizeof (_prefs->advert_loc_policy)); // 161 file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162 - file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 - file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 - // 290 + file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 + file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 + file.read((uint8_t *)&_prefs->direct_retry_attempts, sizeof(_prefs->direct_retry_attempts)); // 290 + file.read((uint8_t *)&_prefs->direct_retry_base_ms, sizeof(_prefs->direct_retry_base_ms)); // 291 + file.read((uint8_t *)&_prefs->direct_retry_timing_magic[0], sizeof(_prefs->direct_retry_timing_magic)); // 293 + // next: 295 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -117,6 +128,14 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->direct_retry_recent_enabled = constrain(_prefs->direct_retry_recent_enabled, 0, 1); _prefs->direct_retry_snr_margin_db = constrain(_prefs->direct_retry_snr_margin_db, 0, DIRECT_RETRY_SNR_MARGIN_DB_MAX); } + if (_prefs->direct_retry_timing_magic[0] != DIRECT_RETRY_TIMING_MAGIC_0 + || _prefs->direct_retry_timing_magic[1] != DIRECT_RETRY_TIMING_MAGIC_1) { + _prefs->direct_retry_attempts = DIRECT_RETRY_COUNT_DEFAULT; + _prefs->direct_retry_base_ms = DIRECT_RETRY_BASE_MS_DEFAULT; + } else { + _prefs->direct_retry_attempts = constrain(_prefs->direct_retry_attempts, DIRECT_RETRY_COUNT_MIN, DIRECT_RETRY_COUNT_MAX); + _prefs->direct_retry_base_ms = constrain(_prefs->direct_retry_base_ms, DIRECT_RETRY_BASE_MS_MIN, DIRECT_RETRY_BASE_MS_MAX); + } // sanitise bad bridge pref values _prefs->bridge_enabled = constrain(_prefs->bridge_enabled, 0, 1); @@ -190,8 +209,12 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->advert_loc_policy, sizeof(_prefs->advert_loc_policy)); // 161 file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162 file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 - file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 - // 290 + file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 + file.write((uint8_t *)&_prefs->direct_retry_attempts, sizeof(_prefs->direct_retry_attempts)); // 290 + file.write((uint8_t *)&_prefs->direct_retry_base_ms, sizeof(_prefs->direct_retry_base_ms)); // 291 + uint8_t retry_timing_magic[2] = { DIRECT_RETRY_TIMING_MAGIC_0, DIRECT_RETRY_TIMING_MAGIC_1 }; + file.write(retry_timing_magic, sizeof(retry_timing_magic)); // 293 + // next: 295 file.close(); } @@ -346,6 +369,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch sprintf(reply, "> %s", _prefs->direct_retry_recent_enabled ? "on" : "off"); } else if (memcmp(config, "direct.retry.margin", 19) == 0) { sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_snr_margin_db); + } else if (memcmp(config, "direct.retry.count", 18) == 0) { + sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_attempts); + } else if (memcmp(config, "direct.retry.base", 17) == 0) { + sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_base_ms); } else if (memcmp(config, "owner.info", 10) == 0) { *reply++ = '>'; *reply++ = ' '; @@ -586,6 +613,24 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch } else { sprintf(reply, "Error, min 0 and max %d", DIRECT_RETRY_SNR_MARGIN_DB_MAX); } + } else if (memcmp(config, "direct.retry.count ", 19) == 0) { + int count = atoi(&config[19]); + if (count >= DIRECT_RETRY_COUNT_MIN && count <= DIRECT_RETRY_COUNT_MAX) { + _prefs->direct_retry_attempts = (uint8_t)count; + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error, min %d and max %d", DIRECT_RETRY_COUNT_MIN, DIRECT_RETRY_COUNT_MAX); + } + } else if (memcmp(config, "direct.retry.base ", 18) == 0) { + int delay_ms = atoi(&config[18]); + if (delay_ms >= DIRECT_RETRY_BASE_MS_MIN && delay_ms <= DIRECT_RETRY_BASE_MS_MAX) { + _prefs->direct_retry_base_ms = (uint16_t)delay_ms; + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error, min %d and max %d", DIRECT_RETRY_BASE_MS_MIN, DIRECT_RETRY_BASE_MS_MAX); + } } else if (memcmp(config, "owner.info ", 11) == 0) { config += 11; char *dp = _prefs->owner_info; diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index d0f26bfe..ab8e058a 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -54,6 +54,9 @@ struct NodePrefs { // persisted to file uint32_t discovery_mod_timestamp; float adc_multiplier; char owner_info[120]; + uint8_t direct_retry_attempts; + uint16_t direct_retry_base_ms; + uint8_t direct_retry_timing_magic[2]; }; class CommonCLICallbacks { diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index f5da272b..705869ad 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -279,6 +279,51 @@ public: } return NULL; } + int getRecentRepeaterCount() const { + int count = 0; + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + if (_recent_repeaters[i].prefix_len > 0) { + count++; + } + } + return count; + } + const RecentRepeaterInfo* getRecentRepeaterNewestByIdx(int idx_wanted) const { + if (idx_wanted < 0) { + return NULL; + } + int idx_seen = 0; + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + int idx = (_next_recent_repeater_idx - 1 - i + MAX_RECENT_REPEATERS) % MAX_RECENT_REPEATERS; + const RecentRepeaterInfo* info = &_recent_repeaters[idx]; + if (info->prefix_len == 0) { + continue; + } + if (idx_seen == idx_wanted) { + return info; + } + idx_seen++; + } + return NULL; + } + const RecentRepeaterInfo* getRecentRepeaterOldestByIdx(int idx_wanted) const { + if (idx_wanted < 0) { + return NULL; + } + int idx_seen = 0; + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + int idx = (_next_recent_repeater_idx + i) % MAX_RECENT_REPEATERS; + const RecentRepeaterInfo* info = &_recent_repeaters[idx]; + if (info->prefix_len == 0) { + continue; + } + if (idx_seen == idx_wanted) { + return info; + } + idx_seen++; + } + return NULL; + } const RecentRepeaterInfo* findRecentRepeaterByHash(const uint8_t* hash, uint8_t hash_len) const { if (hash == NULL || hash_len == 0) { From d028c69d46d9807495173b6e78a7b7cddc4ab53e Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 24 Apr 2026 14:41:07 -0700 Subject: [PATCH 091/214] Fix issue with packet prefixes getting added to the table. --- docs/cli_commands.md | 6 +- examples/simple_repeater/MyMesh.cpp | 93 ++++++++++++++++++++++------- src/helpers/SimpleMeshTables.h | 24 ++++---- 3 files changed, 91 insertions(+), 32 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index bbd28693..14d68a06 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -102,19 +102,23 @@ **Usage:** - `get recent.repeater` - `get recent.repeater all` +- `get recent.repeater all ` - `get recent.repeater first ` +- `get recent.repeater first ` - `get recent.repeater last ` +- `get recent.repeater last ` - `set recent.repeater ` **Parameters:** - `prefix_hex`: 1-3 bytes of next-hop prefix (hex) - `snr_db`: SNR in dB (supports decimals; stored at x4 precision) - `count`: number of entries to print +- `offset`: zero-based row offset into the selected order **Notes:** - `set` is rejected when the prefix already exists in neighbors. - `all` prints oldest to newest; `first` prints the oldest N; `last` prints the newest N. -- Remote CLI replies include rows too, but may truncate when the packet payload limit is reached. +- Over LoRa remote CLI, replies are packet-size limited; use `offset` to page through all rows. --- diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 3e8985e8..bf160b22 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1317,7 +1317,11 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply auto* tables = (SimpleMeshTables*)getTables(); - if (is_set || (!is_get && *sub != 0 && strcmp(sub, "all") != 0 && strncmp(sub, "first ", 6) != 0 && strncmp(sub, "last ", 5) != 0)) { + bool is_all = (strcmp(sub, "all") == 0 || strncmp(sub, "all ", 4) == 0); + bool is_first = (strncmp(sub, "first ", 6) == 0); + bool is_last = (strncmp(sub, "last ", 5) == 0); + + if (is_set || (!is_get && *sub != 0 && !is_all && !is_first && !is_last)) { char* params = (char*) sub; char* arg_snr = strchr(params, ' '); if (arg_snr == NULL) { @@ -1354,62 +1358,111 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply mesh::Utils::toHex(hex, info->prefix, info->prefix_len); sprintf(reply, "> %s,%s", hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); } - } else if (strcmp(sub, "all") == 0 || strncmp(sub, "first ", 6) == 0 || strncmp(sub, "last ", 5) == 0) { + } else if (is_all || is_first || is_last) { int total = tables->getRecentRepeaterCount(); if (total <= 0) { strcpy(reply, "> none"); } else { bool newest_first = false; int limit = total; + int offset = 0; const char* mode = "all"; - if (strncmp(sub, "first ", 6) == 0 || strncmp(sub, "last ", 5) == 0) { - const char* nstr = sub + (sub[0] == 'f' ? 6 : 5); + + if (is_first || is_last) { + const char* nstr = sub + (is_first ? 6 : 5); while (*nstr == ' ') nstr++; if (*nstr == 0) { - strcpy(reply, "Err - usage: get recent.repeater first|last "); + strcpy(reply, "Err - usage: get recent.repeater first|last [offset]"); return; } + char* end_ptr = NULL; - long parsed = strtol(nstr, &end_ptr, 10); + long parsed_count = strtol(nstr, &end_ptr, 10); while (end_ptr != NULL && *end_ptr == ' ') end_ptr++; - if (end_ptr == NULL || *end_ptr != 0 || parsed <= 0) { + if (end_ptr == NULL || parsed_count <= 0) { strcpy(reply, "Err - count must be > 0"); return; } - limit = (int)parsed; - if (sub[0] == 'l') { + + if (*end_ptr != 0) { + char* end_ptr2 = NULL; + long parsed_offset = strtol(end_ptr, &end_ptr2, 10); + while (end_ptr2 != NULL && *end_ptr2 == ' ') end_ptr2++; + if (end_ptr2 == NULL || *end_ptr2 != 0 || parsed_offset < 0) { + strcpy(reply, "Err - offset must be >= 0"); + return; + } + offset = (int)parsed_offset; + } + + limit = (int)parsed_count; + if (is_last) { newest_first = true; mode = "last"; } else { mode = "first"; } + } else if (strncmp(sub, "all ", 4) == 0) { + const char* arg = sub + 4; + while (*arg == ' ') arg++; + if (*arg == 0) { + strcpy(reply, "Err - usage: get recent.repeater all "); + return; + } + + char* end_ptr = NULL; + long parsed_a = strtol(arg, &end_ptr, 10); + while (end_ptr != NULL && *end_ptr == ' ') end_ptr++; + if (end_ptr == NULL || parsed_a <= 0) { + strcpy(reply, "Err - count must be > 0"); + return; + } + + char* end_ptr2 = NULL; + long parsed_b = strtol(end_ptr, &end_ptr2, 10); + while (end_ptr2 != NULL && *end_ptr2 == ' ') end_ptr2++; + if (end_ptr2 == NULL || *end_ptr2 != 0 || parsed_b < 0) { + strcpy(reply, "Err - usage: get recent.repeater all "); + return; + } + limit = (int)parsed_a; + offset = (int)parsed_b; } - if (limit > total) { - limit = total; + + if (offset >= total) { + sprintf(reply, "> none (%s off=%d/%d)", mode, offset, total); + return; + } + + int available = total - offset; + if (limit > available) { + limit = available; } if (sender_timestamp == 0) { - Serial.printf("Recent repeater table (%s %d/%d):\n", mode, limit, total); + Serial.printf("Recent repeater table (%s %d/%d, off=%d):\n", mode, limit, total, offset); for (int i = 0; i < limit; i++) { - const auto* info = newest_first ? tables->getRecentRepeaterNewestByIdx(i) : tables->getRecentRepeaterOldestByIdx(i); + int idx = offset + i; + const auto* info = newest_first ? tables->getRecentRepeaterNewestByIdx(idx) : tables->getRecentRepeaterOldestByIdx(idx); if (info == NULL) { continue; } char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; mesh::Utils::toHex(hex, info->prefix, info->prefix_len); - Serial.printf("%02d: %s,%s\n", i + 1, hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); + Serial.printf("%02d: %s,%s\n", idx + 1, hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); } - sprintf(reply, "> showing %d/%d (%s)", limit, total, mode); + sprintf(reply, "> %s off=%d n=%d/%d", mode, offset, limit, total); } else { // Remote CLI replies are packet-bound, so include as many rows as fit. - int written = snprintf(reply, 160, "> showing %d/%d (%s)", limit, total, mode); + int written = snprintf(reply, 160, "> %s off=%d n=%d/%d", mode, offset, limit, total); bool truncated = false; if (written < 0) { reply[0] = 0; written = 0; } for (int i = 0; i < limit; i++) { - const auto* info = newest_first ? tables->getRecentRepeaterNewestByIdx(i) : tables->getRecentRepeaterOldestByIdx(i); + int idx = offset + i; + const auto* info = newest_first ? tables->getRecentRepeaterNewestByIdx(idx) : tables->getRecentRepeaterOldestByIdx(idx); if (info == NULL) { continue; } @@ -1419,7 +1472,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; mesh::Utils::toHex(hex, info->prefix, info->prefix_len); - int n = snprintf(reply + written, 160 - written, "\n%02d:%s,%s", i + 1, hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); + int n = snprintf(reply + written, 160 - written, "\n%02d:%s,%s", idx + 1, hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); if (n < 0 || n >= (160 - written)) { truncated = true; break; @@ -1427,12 +1480,12 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply written += n; } if (truncated && written < 156) { - snprintf(reply + written, 160 - written, "\n..."); + snprintf(reply + written, 160 - written, "\n... use offset"); } } } } else { - strcpy(reply, "Err - usage: get recent.repeater [all|first |last ]"); + strcpy(reply, "Err - usage: get recent.repeater [all|all |first [offset]|last [offset]]"); } } else{ _cli.handleCommand(sender_timestamp, command, reply); // common CLI commands diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index 705869ad..539bd5b6 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -70,6 +70,19 @@ private: bool extractRecentRepeater(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const { // Learn repeater prefixes only from packet shapes that expose a trustworthy repeater ID. + // For flood traffic, the last path entry is the repeater we directly heard. + if (packet->isRouteFlood() && packet->getPathHashCount() > 0) { + prefix_len = packet->getPathHashSize(); + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + + const uint8_t* last_hop = &packet->path[(packet->getPathHashCount() - 1) * packet->getPathHashSize()]; + memcpy(prefix, last_hop, prefix_len); + return true; + } + + // If there is no flood path to inspect, fall back to payload-derived identities. if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->payload_len >= PUB_KEY_SIZE) { memcpy(prefix, packet->payload, MAX_ROUTE_HASH_BYTES); prefix_len = MAX_ROUTE_HASH_BYTES; @@ -86,17 +99,6 @@ private: return true; } - if (packet->isRouteFlood() && packet->getPathHashCount() > 0) { - prefix_len = packet->getPathHashSize(); - if (prefix_len > MAX_ROUTE_HASH_BYTES) { - prefix_len = MAX_ROUTE_HASH_BYTES; - } - - const uint8_t* last_hop = &packet->path[(packet->getPathHashCount() - 1) * packet->getPathHashSize()]; - memcpy(prefix, last_hop, prefix_len); - return true; - } - return false; } From d1d50967cbc99c0204dc7b7cf4428ad77b276a9e Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 24 Apr 2026 15:19:48 -0700 Subject: [PATCH 092/214] Round up the SNR vs replacement to get a weighted average. --- src/helpers/SimpleMeshTables.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index 539bd5b6..effea219 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -68,6 +68,21 @@ private: return n > 0 && memcmp(a, b, n) == 0; } + int8_t avgSnrX4RoundUp(int8_t curr_snr_x4, int8_t new_snr_x4) const { + int16_t sum = (int16_t)curr_snr_x4 + (int16_t)new_snr_x4; + int16_t avg = sum / 2; // truncates toward zero + // "Round up" means ceil(), which only differs from truncation for positive odd sums. + if (sum > 0 && (sum & 1)) { + avg++; + } + if (avg > 127) { + avg = 127; + } else if (avg < -128) { + avg = -128; + } + return (int8_t)avg; + } + bool extractRecentRepeater(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const { // Learn repeater prefixes only from packet shapes that expose a trustworthy repeater ID. // For flood traffic, the last path entry is the repeater we directly heard. @@ -258,7 +273,7 @@ public: memcpy(existing.prefix, prefix, prefix_len); existing.prefix_len = prefix_len; } - existing.snr_x4 = snr_x4; + existing.snr_x4 = avgSnrX4RoundUp(existing.snr_x4, snr_x4); return true; } From 399939f41babbe7cacbdae95c33daa9fd0f95d14 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 27 Apr 2026 16:33:39 -0700 Subject: [PATCH 093/214] Refine direct retry SNR handling and recent repeater controls --- docs/cli_commands.md | 28 +- examples/simple_repeater/MyMesh.cpp | 448 +++++++++++++++++++--------- examples/simple_repeater/MyMesh.h | 1 + src/Mesh.cpp | 15 +- src/helpers/CommonCLI.cpp | 28 +- src/helpers/CommonCLI.h | 2 +- src/helpers/SimpleMeshTables.h | 194 ++++++++++-- 7 files changed, 538 insertions(+), 178 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 14d68a06..78f8626d 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -101,24 +101,20 @@ ### Get or set recent repeater fallback prefix/SNR **Usage:** - `get recent.repeater` -- `get recent.repeater all` -- `get recent.repeater all ` -- `get recent.repeater first ` -- `get recent.repeater first ` -- `get recent.repeater last ` -- `get recent.repeater last ` -- `set recent.repeater ` +- `get recent.repeater ` +- `get recent.repeater page ` +- `set recent.repeater ` **Parameters:** -- `prefix_hex`: 1-3 bytes of next-hop prefix (hex) +- `prefix_hex_6`: Exactly 3 bytes of next-hop prefix in hex (6 chars) - `snr_db`: SNR in dB (supports decimals; stored at x4 precision) -- `count`: number of entries to print -- `offset`: zero-based row offset into the selected order +- `page`: 1-based page number **Notes:** - `set` is rejected when the prefix already exists in neighbors. -- `all` prints oldest to newest; `first` prints the oldest N; `last` prints the newest N. -- Over LoRa remote CLI, replies are packet-size limited; use `offset` to page through all rows. +- Rows are shown newest-first. +- Serial CLI prints all rows (no paging). +- Over LoRa remote CLI, page size is fixed at `4` rows; choose page with `get recent.repeater `. --- @@ -437,7 +433,7 @@ **Parameters:** - `state`: `on`|`off` -**Default:** `off` +**Default:** `on` **Note:** When enabled, a repeater can use recently-heard non-duplicate repeater prefixes as a fallback for direct retry eligibility when no suitable neighbor entry is available. @@ -449,9 +445,9 @@ - `set direct.retry.margin ` **Parameters:** -- `value`: Margin in dB above the SF-specific receive floor (minimum `0`, default `5`) +- `value`: Margin in dB above the SF-specific receive floor (minimum `0`, maximum `40`, quarter-dB precision, default `2.5`) -**Default:** `5` +**Default:** `2.5` **Note:** The retry gate uses the active SF floor of `SF5=-2.5`, `SF6=-5`, `SF7=-7.5`, `SF8=-10`, `SF9=-12.5`, `SF10=-15`, `SF11=-17.5`, `SF12=-20`, then adds this margin. @@ -465,7 +461,7 @@ **Parameters:** - `value`: Retry attempts after initial TX (`1`-`15`) -**Default:** `3` +**Default:** `15` --- diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index bf160b22..10be2212 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -86,6 +86,64 @@ bool MyMesh::allowRecentRepeaterPrefixStore(const uint8_t* prefix, uint8_t prefi return self->findNeighbourByHash(prefix, prefix_len) == NULL; } +static void formatRecentRepeaterPrefix(const SimpleMeshTables::RecentRepeaterInfo* info, char* out, size_t out_len) { + if (out == NULL || out_len == 0) { + return; + } + out[0] = 0; + if (info == NULL) { + return; + } + + uint8_t prefix_len = info->prefix_len; + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + if (prefix_len > 0) { + mesh::Utils::toHex(out, info->prefix, prefix_len); + } + + size_t used = strlen(out); + const size_t target_len = MAX_ROUTE_HASH_BYTES * 2; + while (used < target_len && used + 1 < out_len) { + out[used++] = ' '; + } + out[used] = 0; +} + +static void formatRecentRepeaterSnrX4(int8_t snr_x4, char* out, size_t out_len) { + if (out == NULL || out_len == 0) { + return; + } + + const char* snr_text = StrHelper::ftoa(((float)snr_x4) / 4.0f); + if (snr_text[0] == '-') { + snprintf(out, out_len, "%s", snr_text); + } else { + snprintf(out, out_len, " %s", snr_text); + } +} + +static uint8_t decodeTraceHashSize(uint8_t flags, uint8_t route_bytes) { + uint8_t code = flags & 0x03; + uint8_t size_pow2 = (uint8_t)(1U << code); // legacy TRACE interpretation + uint8_t size_linear = (uint8_t)(code + 1U); // packed-size interpretation (1..4) + + bool pow2_ok = size_pow2 > 0 && (route_bytes % size_pow2) == 0; + bool linear_ok = size_linear > 0 && (route_bytes % size_linear) == 0; + + if (pow2_ok && !linear_ok) { + return size_pow2; + } + if (linear_ok && !pow2_ok) { + return size_linear; + } + if (pow2_ok) { + return size_pow2; + } + return size_linear; +} + void MyMesh::putNeighbour(const mesh::Identity &id, uint32_t timestamp, float snr) { #if MAX_NEIGHBOURS // check if neighbours enabled // find existing neighbour, else use least recently updated @@ -430,7 +488,31 @@ bool MyMesh::isLooped(const mesh::Packet* packet, const uint8_t max_counters[]) bool MyMesh::allowPacketForward(const mesh::Packet *packet) { if (_prefs.disable_fwd) return false; - if (packet->isRouteFlood() && packet->path_len >= _prefs.flood_max) return false; + + if (packet->isRouteDirect() && packet->getPayloadType() == PAYLOAD_TYPE_TRACE && packet->payload_len >= 9) { + auto* tables = (SimpleMeshTables *)getTables(); + uint8_t route_bytes = packet->payload_len - 9; + uint8_t hash_size = decodeTraceHashSize(packet->payload[8], route_bytes); + uint16_t offset = (uint16_t)packet->path_len * (uint16_t)hash_size; + uint8_t sf = constrain(active_sf, (uint8_t)5, (uint8_t)12); + int16_t fallback_snr_x4 = direct_retry_floor_x4[sf - 5] + 40; // fixed +10 dB above SF floor + + // A successful TRACE forward reveals the downstream next-hop hash. Seed/update the recent table immediately. + if (hash_size > 0 && offset + (2U * hash_size) <= route_bytes) { + uint8_t prefix_len = hash_size; + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + const uint8_t* next_hop_prefix = &packet->payload[9 + offset + hash_size]; + const auto* existing = tables->findRecentRepeaterByHash(next_hop_prefix, prefix_len); + // This point only proves we can forward TO next_hop; packet->_snr is upstream RX and not a + // trustworthy metric for next_hop. Seed with existing table value or fallback only. + int8_t trace_snr_x4 = (existing != NULL) ? existing->snr_x4 : (int8_t)constrain(fallback_snr_x4, -128, 127); + tables->setRecentRepeater(next_hop_prefix, prefix_len, trace_snr_x4, false, true); + } + } + + if (packet->isRouteFlood() && packet->getPathHashCount() >= _prefs.flood_max) return false; if (packet->isRouteFlood() && recv_pkt_region == NULL) { MESH_DEBUG_PRINTLN("allowPacketForward: unknown transport code, or wildcard not allowed for FLOOD packet"); return false; @@ -524,23 +606,102 @@ void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, u return; } - MESH_DEBUG_PRINTLN("%s direct retry %s (type=%d, route=%s, payload_len=%d, delay=%lu)", + uint8_t prefix[MAX_ROUTE_HASH_BYTES] = {0}; + uint8_t prefix_len = 0; + bool has_prefix = extractDirectRetryPrefix(packet, prefix, prefix_len); + auto* tables = (SimpleMeshTables *)getTables(); + const auto* existing = has_prefix ? tables->findRecentRepeaterByHash(prefix, prefix_len) : NULL; + char next_hop_hex[(MAX_ROUTE_HASH_BYTES * 2) + 1] = {0}; + if (has_prefix && prefix_len > 0) { + mesh::Utils::toHex(next_hop_hex, prefix, prefix_len); + } + const char* next_hop = (has_prefix && prefix_len > 0) ? next_hop_hex : "unknown"; + // Direct-retry events are TX-side and usually have no trustworthy RX SNR. + // Cap event SNR at fixed SF floor + 10 dB so trace-start retries can't inflate table SNR. + uint8_t sf = constrain(active_sf, (uint8_t)5, (uint8_t)12); + int16_t fallback_snr_x4_raw = direct_retry_floor_x4[sf - 5] + 40; + int8_t fallback_snr_x4 = (int8_t)constrain(fallback_snr_x4_raw, -128, 127); + bool is_success_event = (strcmp(event, "good") == 0 || strcmp(event, "canceled_echo") == 0); + int8_t retry_event_snr_x4; + const char* snr_src; + if (is_success_event && packet->_snr != 0) { + // On success, Mesh.cpp injects echo RX SNR for TRACE retries. + retry_event_snr_x4 = packet->_snr; + snr_src = "packet"; + } else if (existing != NULL) { + retry_event_snr_x4 = existing->snr_x4; + snr_src = "table"; + } else { + retry_event_snr_x4 = fallback_snr_x4; + snr_src = "fallback"; + } + char snr_used_text[12]; + char snr_pkt_text[12]; + char snr_table_text[12]; + snprintf(snr_used_text, sizeof(snr_used_text), "%s", StrHelper::ftoa(((float)retry_event_snr_x4) / 4.0f)); + snprintf(snr_pkt_text, sizeof(snr_pkt_text), "%s", StrHelper::ftoa(((float)packet->_snr) / 4.0f)); + if (existing != NULL) { + snprintf(snr_table_text, sizeof(snr_table_text), "%s", StrHelper::ftoa(((float)existing->snr_x4) / 4.0f)); + } else { + snprintf(snr_table_text, sizeof(snr_table_text), "na"); + } + + if (has_prefix && is_success_event) { + // Refresh SNR only on successful echo/progress events, not on queued/resent bookkeeping. + tables->setRecentRepeater(prefix, prefix_len, retry_event_snr_x4, false, true); + } + + if (strcmp(event, "resent") == 0) { + if (has_prefix) { + // Retry stats should be visible even when the prefix was never learned into recent.repeater. + tables->incrementRecentRepeaterRetryCount(prefix, prefix_len, true, retry_event_snr_x4, true); + } + } else if (strcmp(event, "failed_all_tries") == 0) { + if (has_prefix) { + // A failed_all_tries event means all retry attempts for this packet failed. + // Count failures by retry-attempts so fail% reflects failed retries, not just failed sessions. + uint8_t give_up_retries = getDirectRetryMaxAttempts(packet); + uint8_t failed_retries = give_up_retries; + if (failed_retries < 1) { + failed_retries = 1; + } + for (uint8_t i = 0; i < failed_retries; i++) { + tables->incrementRecentRepeaterFailCount(prefix, prefix_len, true, retry_event_snr_x4, true); + } + if (failed_retries >= give_up_retries && give_up_retries > 0) { + // If all configured retry attempts still fail, slightly degrade stored path quality. + tables->decrementRecentRepeaterSnrX4(prefix, prefix_len, 1); + } + } + } + + MESH_DEBUG_PRINTLN("%s direct retry %s (type=%d, route=%s, payload_len=%d, next_hop=%s, snr=%s, snr_src=%s, pkt_snr=%s, table_snr=%s, delay=%lu)", getLogDateTime(), event, (uint32_t)packet->getPayloadType(), packet->isRouteDirect() ? "D" : "F", (uint32_t)packet->payload_len, + next_hop, + snr_used_text, + snr_src, + snr_pkt_text, + snr_table_text, (unsigned long)delay_millis); if (_logging) { File f = openAppend(PACKET_LOG_FILE); if (f) { f.print(getLogDateTime()); - f.printf(": DIRECT RETRY %s (type=%d, route=%s, payload_len=%d, delay=%lu)\n", + f.printf(": DIRECT RETRY %s (type=%d, route=%s, payload_len=%d, next_hop=%s, snr=%s, snr_src=%s, pkt_snr=%s, table_snr=%s, delay=%lu)\n", event, (uint32_t)packet->getPayloadType(), packet->isRouteDirect() ? "D" : "F", (uint32_t)packet->payload_len, + next_hop, + snr_used_text, + snr_src, + snr_pkt_text, + snr_table_text, (unsigned long)delay_millis); f.close(); } @@ -563,28 +724,59 @@ uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) { int8_t MyMesh::getDirectRetryMinSNRX4() const { // Use the live SF so `tempradio` changes immediately affect the retry threshold. uint8_t sf = constrain(active_sf, (uint8_t)5, (uint8_t)12); - int16_t threshold = direct_retry_floor_x4[sf - 5] + ((int16_t)_prefs.direct_retry_snr_margin_db * 4); + int16_t threshold = direct_retry_floor_x4[sf - 5] + (int16_t)_prefs.direct_retry_snr_margin_db; return (int8_t)constrain(threshold, -128, 127); } +bool MyMesh::extractDirectRetryPrefix(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const { + if (packet == NULL || prefix == NULL) { + return false; + } + + // TRACE direct routes encode repeater hashes in payload; packet->path carries SNR trail bytes. + if (packet->isRouteDirect() && packet->getPayloadType() == PAYLOAD_TYPE_TRACE && packet->payload_len >= 9) { + uint8_t route_bytes = packet->payload_len - 9; + uint8_t hash_size = decodeTraceHashSize(packet->payload[8], route_bytes); + uint8_t offset = packet->path_len * hash_size; + if (hash_size > 0 && offset + hash_size <= route_bytes) { + prefix_len = hash_size; + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + memcpy(prefix, &packet->payload[9 + offset], prefix_len); + return true; + } + } + + if (packet->isRouteDirect() && packet->getPathHashCount() > 0) { + prefix_len = packet->getPathHashSize(); + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + if (prefix_len == 0) { + return false; + } + memcpy(prefix, packet->path, prefix_len); + return true; + } + + return false; +} bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const { if (_prefs.disable_fwd) { return false; } int8_t min_snr_x4 = getDirectRetryMinSNRX4(); + if (_prefs.direct_retry_recent_enabled) { + // Prefer the 64-entry recent-prefix cache first, then fall back to neighbours. + const auto* recent = ((const SimpleMeshTables *)getTables())->findRecentRepeaterByHash(next_hop_hash, next_hop_hash_len); + if (recent != NULL && recent->snr_x4 >= min_snr_x4) { + return true; + } + } + const NeighbourInfo* neighbour = findNeighbourByHash(next_hop_hash, next_hop_hash_len); - // Prefer the explicit neighbor table first; it is the strongest signal that this hop is still reachable. - if (neighbour != NULL && neighbour->snr >= min_snr_x4) { - return true; - } - - if (!_prefs.direct_retry_recent_enabled) { - return false; - } - - // If no neighbor entry exists, fall back to the recent-heard repeater cache keyed by the same path prefix. - const auto* recent = ((const SimpleMeshTables *)getTables())->findRecentRepeaterByHash(next_hop_hash, next_hop_hash_len); - return recent != NULL && recent->snr_x4 >= min_snr_x4; + return neighbour != NULL && neighbour->snr >= min_snr_x4; } uint32_t MyMesh::getDirectRetryEchoDelay(const mesh::Packet* packet) const { uint32_t base_wait_millis = constrain((uint32_t)_prefs.direct_retry_base_ms, (uint32_t)10, (uint32_t)5000); @@ -888,9 +1080,9 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.rx_delay_base = 0.0f; // turn off by default, was 10.0; _prefs.tx_delay_factor = 0.5f; // was 0.25f _prefs.direct_tx_delay_factor = 0.3f; // was 0.2 - _prefs.direct_retry_recent_enabled = 0; - _prefs.direct_retry_snr_margin_db = 5; - _prefs.direct_retry_attempts = 3; + _prefs.direct_retry_recent_enabled = 1; + _prefs.direct_retry_snr_margin_db = 10; // 2.5 dB stored in x4 units + _prefs.direct_retry_attempts = 15; _prefs.direct_retry_base_ms = 200; StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); _prefs.node_lat = ADVERT_LAT; @@ -1299,9 +1491,11 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } } else if (strncmp(command, "get recent.repeater", 19) == 0 || strncmp(command, "set recent.repeater", 19) == 0 + || strncmp(command, "clear recent.repeater", 21) == 0 || strncmp(command, "recent.repeater", 15) == 0) { bool is_get = false; bool is_set = false; + bool is_clear = false; const char* sub = command; if (strncmp(command, "get recent.repeater", 19) == 0) { @@ -1310,38 +1504,55 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } else if (strncmp(command, "set recent.repeater", 19) == 0) { is_set = true; sub = command + 19; + } else if (strncmp(command, "clear recent.repeater", 21) == 0) { + is_clear = true; + sub = command + 21; } else { sub = command + 15; // legacy command format } while (*sub == ' ') sub++; auto* tables = (SimpleMeshTables*)getTables(); + if (!is_get && !is_set && !is_clear && strncmp(sub, "clear", 5) == 0 && (sub[5] == 0 || sub[5] == ' ')) { + is_clear = true; + sub += 5; + while (*sub == ' ') sub++; + } - bool is_all = (strcmp(sub, "all") == 0 || strncmp(sub, "all ", 4) == 0); - bool is_first = (strncmp(sub, "first ", 6) == 0); - bool is_last = (strncmp(sub, "last ", 5) == 0); - - if (is_set || (!is_get && *sub != 0 && !is_all && !is_first && !is_last)) { + if (is_clear) { + if (*sub != 0) { + strcpy(reply, "Err - usage: clear recent.repeater"); + } else { + tables->clearRecentRepeaters(); + strcpy(reply, "OK"); + } + } else if (is_set) { char* params = (char*) sub; char* arg_snr = strchr(params, ' '); if (arg_snr == NULL) { - strcpy(reply, "Err - usage: set recent.repeater "); + strcpy(reply, "Err - usage: set recent.repeater "); } else { *arg_snr++ = 0; while (*arg_snr == ' ') arg_snr++; if (*arg_snr == 0) { - strcpy(reply, "Err - usage: set recent.repeater "); + strcpy(reply, "Err - usage: set recent.repeater "); } else { - int hex_len = strlen(params); - int prefix_len = hex_len / 2; uint8_t prefix[MAX_ROUTE_HASH_BYTES] = {0}; - if ((hex_len % 2) != 0 || prefix_len <= 0 || prefix_len > MAX_ROUTE_HASH_BYTES || !mesh::Utils::fromHex(prefix, prefix_len, params)) { - strcpy(reply, "Err - prefix must be 1-3 bytes hex"); + int hex_len = strlen(params); + if (hex_len != (MAX_ROUTE_HASH_BYTES * 2) || !mesh::Utils::fromHex(prefix, MAX_ROUTE_HASH_BYTES, params)) { + strcpy(reply, "Err - prefix must be exactly 3 bytes hex (6 chars)"); } else { - float snr_db = strtof(arg_snr, nullptr); + char* end_snr = NULL; + float snr_db = strtof(arg_snr, &end_snr); + while (end_snr != NULL && *end_snr == ' ') end_snr++; + if (end_snr == arg_snr || (end_snr != NULL && *end_snr != 0)) { + strcpy(reply, "Err - snr must be numeric"); + return; + } + int snr_x4 = (int)(snr_db * 4.0f + (snr_db >= 0.0f ? 0.5f : -0.5f)); snr_x4 = constrain(snr_x4, -128, 127); - if (tables->setRecentRepeater(prefix, (uint8_t)prefix_len, (int8_t)snr_x4)) { + if (tables->setRecentRepeater(prefix, MAX_ROUTE_HASH_BYTES, (int8_t)snr_x4, true)) { strcpy(reply, "OK"); } else { strcpy(reply, "Err - prefix is already in neighbors"); @@ -1349,120 +1560,82 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } } } - } else if (*sub == 0) { - const auto* info = tables->getLatestRecentRepeater(); - if (info == NULL) { - strcpy(reply, "> none"); - } else { - char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; - mesh::Utils::toHex(hex, info->prefix, info->prefix_len); - sprintf(reply, "> %s,%s", hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); - } - } else if (is_all || is_first || is_last) { + } else { int total = tables->getRecentRepeaterCount(); if (total <= 0) { strcpy(reply, "> none"); } else { - bool newest_first = false; - int limit = total; - int offset = 0; - const char* mode = "all"; - - if (is_first || is_last) { - const char* nstr = sub + (is_first ? 6 : 5); - while (*nstr == ' ') nstr++; - if (*nstr == 0) { - strcpy(reply, "Err - usage: get recent.repeater first|last [offset]"); - return; - } - - char* end_ptr = NULL; - long parsed_count = strtol(nstr, &end_ptr, 10); - while (end_ptr != NULL && *end_ptr == ' ') end_ptr++; - if (end_ptr == NULL || parsed_count <= 0) { - strcpy(reply, "Err - count must be > 0"); - return; - } - - if (*end_ptr != 0) { - char* end_ptr2 = NULL; - long parsed_offset = strtol(end_ptr, &end_ptr2, 10); - while (end_ptr2 != NULL && *end_ptr2 == ' ') end_ptr2++; - if (end_ptr2 == NULL || *end_ptr2 != 0 || parsed_offset < 0) { - strcpy(reply, "Err - offset must be >= 0"); - return; - } - offset = (int)parsed_offset; - } - - limit = (int)parsed_count; - if (is_last) { - newest_first = true; - mode = "last"; - } else { - mode = "first"; - } - } else if (strncmp(sub, "all ", 4) == 0) { - const char* arg = sub + 4; - while (*arg == ' ') arg++; - if (*arg == 0) { - strcpy(reply, "Err - usage: get recent.repeater all "); - return; - } - - char* end_ptr = NULL; - long parsed_a = strtol(arg, &end_ptr, 10); - while (end_ptr != NULL && *end_ptr == ' ') end_ptr++; - if (end_ptr == NULL || parsed_a <= 0) { - strcpy(reply, "Err - count must be > 0"); - return; - } - - char* end_ptr2 = NULL; - long parsed_b = strtol(end_ptr, &end_ptr2, 10); - while (end_ptr2 != NULL && *end_ptr2 == ' ') end_ptr2++; - if (end_ptr2 == NULL || *end_ptr2 != 0 || parsed_b < 0) { - strcpy(reply, "Err - usage: get recent.repeater all "); - return; - } - limit = (int)parsed_a; - offset = (int)parsed_b; - } - - if (offset >= total) { - sprintf(reply, "> none (%s off=%d/%d)", mode, offset, total); - return; - } - - int available = total - offset; - if (limit > available) { - limit = available; - } - if (sender_timestamp == 0) { - Serial.printf("Recent repeater table (%s %d/%d, off=%d):\n", mode, limit, total, offset); - for (int i = 0; i < limit; i++) { - int idx = offset + i; - const auto* info = newest_first ? tables->getRecentRepeaterNewestByIdx(idx) : tables->getRecentRepeaterOldestByIdx(idx); + // Serial CLI: print all entries (no paging). + Serial.printf("Recent repeater table (newest first, total=%d):\n", total); + for (int i = 0; i < total; i++) { + const auto* info = tables->getRecentRepeaterNewestByIdx(i); if (info == NULL) { continue; } + char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; - mesh::Utils::toHex(hex, info->prefix, info->prefix_len); - Serial.printf("%02d: %s,%s\n", idx + 1, hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); + formatRecentRepeaterPrefix(info, hex, sizeof(hex)); + char snr_text[12]; + formatRecentRepeaterSnrX4(info->snr_x4, snr_text, sizeof(snr_text)); + uint32_t fail_pct_x10 = 0; + if (info->retry_count > 0) { + fail_pct_x10 = (((uint32_t)info->fail_count * 1000UL) + (info->retry_count / 2U)) / (uint32_t)info->retry_count; + } + Serial.printf("%03d: %s,%s,fp=%lu.%01lu%%,r=%u,f=%u%s\n", + i + 1, + hex, + snr_text, + (unsigned long)(fail_pct_x10 / 10U), + (unsigned long)(fail_pct_x10 % 10U), + (uint32_t)info->retry_count, + (uint32_t)info->fail_count, + info->snr_locked ? ",l" : ""); } - sprintf(reply, "> %s off=%d n=%d/%d", mode, offset, limit, total); + sprintf(reply, "> n=%d/%d", total, total); } else { - // Remote CLI replies are packet-bound, so include as many rows as fit. - int written = snprintf(reply, 160, "> %s off=%d n=%d/%d", mode, offset, limit, total); + // Remote CLI: page by fixed size to fit packet-limited reply payload. + long page_num = 1; + const long page_size = 4; + const char* arg = sub; + + if (strncmp(arg, "page ", 5) == 0) { + arg += 5; + while (*arg == ' ') arg++; + } + + if (*arg != 0) { + char* end_ptr = NULL; + page_num = strtol(arg, &end_ptr, 10); + while (end_ptr != NULL && *end_ptr == ' ') end_ptr++; + if (end_ptr == NULL || page_num <= 0 || (end_ptr != NULL && *end_ptr != 0)) { + strcpy(reply, "Err - usage: get recent.repeater [page]"); + return; + } + } + + int total_pages = (total + (int)page_size - 1) / (int)page_size; + if (page_num > total_pages) { + sprintf(reply, "> none (page=%ld/%d)", page_num, total_pages); + return; + } + + int offset = ((int)page_num - 1) * (int)page_size; + int limit = total - offset; + if (limit > (int)page_size) { + limit = (int)page_size; + } + + int written = snprintf(reply, 160, "> page=%ld/%d n=%d/%d", page_num, total_pages, limit, total); bool truncated = false; if (written < 0) { reply[0] = 0; written = 0; } + for (int i = 0; i < limit; i++) { int idx = offset + i; - const auto* info = newest_first ? tables->getRecentRepeaterNewestByIdx(idx) : tables->getRecentRepeaterOldestByIdx(idx); + const auto* info = tables->getRecentRepeaterNewestByIdx(idx); if (info == NULL) { continue; } @@ -1470,9 +1643,20 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply truncated = true; break; } + char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1]; - mesh::Utils::toHex(hex, info->prefix, info->prefix_len); - int n = snprintf(reply + written, 160 - written, "\n%02d:%s,%s", idx + 1, hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f)); + formatRecentRepeaterPrefix(info, hex, sizeof(hex)); + char snr_text[12]; + formatRecentRepeaterSnrX4(info->snr_x4, snr_text, sizeof(snr_text)); + int n = snprintf(reply + written, + 160 - written, + "\n%03d:%s,%s,r=%u,f=%u%s", + idx + 1, + hex, + snr_text, + (uint32_t)info->retry_count, + (uint32_t)info->fail_count, + info->snr_locked ? ",l" : ""); if (n < 0 || n >= (160 - written)) { truncated = true; break; @@ -1480,12 +1664,10 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply written += n; } if (truncated && written < 156) { - snprintf(reply + written, 160 - written, "\n... use offset"); + snprintf(reply + written, 160 - written, "\n... next page"); } } } - } else { - strcpy(reply, "Err - usage: get recent.repeater [all|all |first [offset]|last [offset]]"); } } else{ _cli.handleCommand(sender_timestamp, command, reply); // common CLI commands diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 3ecbfc81..d873a6b4 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -119,6 +119,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { #endif const NeighbourInfo* findNeighbourByHash(const uint8_t* hash, uint8_t hash_len) const; + bool extractDirectRetryPrefix(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const; static bool allowRecentRepeaterPrefixStore(const uint8_t* prefix, uint8_t prefix_len, void* ctx); int8_t getDirectRetryMinSNRX4() const; void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr); diff --git a/src/Mesh.cpp b/src/Mesh.cpp index be2c81f1..bc34c5ee 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -3,7 +3,7 @@ namespace mesh { -static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT = 3; +static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT = 15; static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX = 15; void Mesh::begin() { @@ -498,6 +498,12 @@ bool Mesh::cancelDirectRetryOnEcho(const Packet* packet) { } if (_direct_retries[i].queued) { + if (_direct_retries[i].expect_path_growth + && _direct_retries[i].packet != NULL + && _direct_retries[i].progress_marker < packet->path_len) { + // For retry-good quality, use the received echo packet SNR (return-link quality). + _direct_retries[i].packet->_snr = packet->_snr; + } for (int j = 0; j < _mgr->getOutboundTotal(); j++) { if (_mgr->getOutboundByIdx(j) == _direct_retries[i].packet) { Packet* pending = _mgr->removeOutboundByIdx(j); @@ -511,6 +517,12 @@ bool Mesh::cancelDirectRetryOnEcho(const Packet* packet) { onDirectRetryEvent("good", _direct_retries[i].packet, 0); clearDirectRetrySlot(i); } else { + if (_direct_retries[i].expect_path_growth + && _direct_retries[i].trigger_packet != NULL + && _direct_retries[i].progress_marker < packet->path_len) { + // For retry-good quality, use the received echo packet SNR (return-link quality). + _direct_retries[i].trigger_packet->_snr = packet->_snr; + } onDirectRetryEvent("canceled_echo", _direct_retries[i].trigger_packet, 0); onDirectRetryEvent("good", _direct_retries[i].trigger_packet, 0); clearDirectRetrySlot(i); @@ -539,6 +551,7 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { max_attempts = DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX; } if (_direct_retries[i].retry_attempts_sent >= max_attempts) { + onDirectRetryEvent("failed_all_tries", packet, 0); onDirectRetryEvent("failure", packet, 0); clearDirectRetrySlot(i); continue; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index bae389ba..03d83308 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -11,12 +11,13 @@ // These bytes used to be reserved/unused in persisted prefs, so keep a marker before trusting them. #define DIRECT_RETRY_PREFS_MAGIC_0 0xD4 #define DIRECT_RETRY_PREFS_MAGIC_1 0x52 -#define DIRECT_RETRY_RECENT_DEFAULT 0 -#define DIRECT_RETRY_SNR_MARGIN_DB_DEFAULT 5 -#define DIRECT_RETRY_SNR_MARGIN_DB_MAX 40 +#define DIRECT_RETRY_RECENT_DEFAULT 1 +#define DIRECT_RETRY_SNR_MARGIN_DB_DEFAULT_X4 10 +#define DIRECT_RETRY_SNR_MARGIN_DB_MAX 40 +#define DIRECT_RETRY_SNR_MARGIN_X4_MAX (DIRECT_RETRY_SNR_MARGIN_DB_MAX * 4) #define DIRECT_RETRY_TIMING_MAGIC_0 0xD5 #define DIRECT_RETRY_TIMING_MAGIC_1 0x54 -#define DIRECT_RETRY_COUNT_DEFAULT 3 +#define DIRECT_RETRY_COUNT_DEFAULT 15 #define DIRECT_RETRY_COUNT_MIN 1 #define DIRECT_RETRY_COUNT_MAX 15 #define DIRECT_RETRY_BASE_MS_DEFAULT 200 @@ -33,6 +34,15 @@ static uint32_t _atoi(const char* sp) { return n; } +static uint8_t directRetryMarginDbToX4(float margin_db) { + int32_t scaled_x4 = (int32_t)((margin_db * 4.0f) + 0.5f); + return (uint8_t)constrain(scaled_x4, 0, DIRECT_RETRY_SNR_MARGIN_X4_MAX); +} + +static float directRetryMarginX4ToDb(uint8_t margin_x4) { + return ((float)margin_x4) / 4.0f; +} + static bool isValidName(const char *n) { while (*n) { if (*n == '[' || *n == ']' || *n == '\\' || *n == ':' || *n == ',' || *n == '?' || *n == '*') return false; @@ -123,10 +133,10 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { if (_prefs->direct_retry_prefs_magic[0] != DIRECT_RETRY_PREFS_MAGIC_0 || _prefs->direct_retry_prefs_magic[1] != DIRECT_RETRY_PREFS_MAGIC_1) { _prefs->direct_retry_recent_enabled = DIRECT_RETRY_RECENT_DEFAULT; - _prefs->direct_retry_snr_margin_db = DIRECT_RETRY_SNR_MARGIN_DB_DEFAULT; + _prefs->direct_retry_snr_margin_db = DIRECT_RETRY_SNR_MARGIN_DB_DEFAULT_X4; } else { _prefs->direct_retry_recent_enabled = constrain(_prefs->direct_retry_recent_enabled, 0, 1); - _prefs->direct_retry_snr_margin_db = constrain(_prefs->direct_retry_snr_margin_db, 0, DIRECT_RETRY_SNR_MARGIN_DB_MAX); + _prefs->direct_retry_snr_margin_db = constrain(_prefs->direct_retry_snr_margin_db, 0, DIRECT_RETRY_SNR_MARGIN_X4_MAX); } if (_prefs->direct_retry_timing_magic[0] != DIRECT_RETRY_TIMING_MAGIC_0 || _prefs->direct_retry_timing_magic[1] != DIRECT_RETRY_TIMING_MAGIC_1) { @@ -368,7 +378,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch } else if (memcmp(config, "direct.retry.heard", 18) == 0) { sprintf(reply, "> %s", _prefs->direct_retry_recent_enabled ? "on" : "off"); } else if (memcmp(config, "direct.retry.margin", 19) == 0) { - sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_snr_margin_db); + sprintf(reply, "> %s", StrHelper::ftoa(directRetryMarginX4ToDb(_prefs->direct_retry_snr_margin_db))); } else if (memcmp(config, "direct.retry.count", 18) == 0) { sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_attempts); } else if (memcmp(config, "direct.retry.base", 17) == 0) { @@ -605,9 +615,9 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch strcpy(reply, "Error, must be on or off"); } } else if (memcmp(config, "direct.retry.margin ", 20) == 0) { - int db = atoi(&config[20]); + float db = atof(&config[20]); if (db >= 0 && db <= DIRECT_RETRY_SNR_MARGIN_DB_MAX) { - _prefs->direct_retry_snr_margin_db = (uint8_t)db; + _prefs->direct_retry_snr_margin_db = directRetryMarginDbToX4(db); savePrefs(); strcpy(reply, "OK"); } else { diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index ab8e058a..6d49fec8 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -28,7 +28,7 @@ struct NodePrefs { // persisted to file char guest_password[16]; float direct_tx_delay_factor; uint8_t direct_retry_recent_enabled; - uint8_t direct_retry_snr_margin_db; + uint8_t direct_retry_snr_margin_db; // stored in quarter-dB units (x4) uint8_t direct_retry_prefs_magic[2]; uint8_t sf; uint8_t cr; diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index effea219..ac6d01b5 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -8,7 +8,14 @@ #define MAX_PACKET_HASHES 128 #define MAX_PACKET_ACKS 64 -#define MAX_RECENT_REPEATERS 64 +#ifndef MAX_RECENT_REPEATERS + // Two defaults. Can be overridden with -D MAX_RECENT_REPEATERS=. + #if defined(ESP32) + #define MAX_RECENT_REPEATERS 512 + #else + #define MAX_RECENT_REPEATERS 64 + #endif +#endif #define MAX_ROUTE_HASH_BYTES 3 class SimpleMeshTables : public mesh::MeshTables { @@ -17,9 +24,12 @@ public: struct RecentRepeaterInfo { // Just enough identity to match a next-hop path prefix plus the SNR that heard it. + uint16_t retry_count; + uint16_t fail_count; uint8_t prefix[MAX_ROUTE_HASH_BYTES]; uint8_t prefix_len; int8_t snr_x4; + uint8_t snr_locked; }; private: @@ -68,19 +78,20 @@ private: return n > 0 && memcmp(a, b, n) == 0; } - int8_t avgSnrX4RoundUp(int8_t curr_snr_x4, int8_t new_snr_x4) const { - int16_t sum = (int16_t)curr_snr_x4 + (int16_t)new_snr_x4; - int16_t avg = sum / 2; // truncates toward zero - // "Round up" means ceil(), which only differs from truncation for positive odd sums. - if (sum > 0 && (sum & 1)) { - avg++; + int8_t weightedSnrX4RoundUp(int8_t curr_snr_x4, int8_t new_snr_x4) const { + // Keep existing SNR heavier than a single new sample: 75% existing + 25% new. + int16_t weighted_sum = ((int16_t)curr_snr_x4 * 3) + (int16_t)new_snr_x4; + int16_t blended = weighted_sum / 4; // truncates toward zero + // "Round up" means ceil(), which only differs from truncation for positive remainders. + if (weighted_sum > 0 && (weighted_sum % 4) != 0) { + blended++; } - if (avg > 127) { - avg = 127; - } else if (avg < -128) { - avg = -128; + if (blended > 127) { + blended = 127; + } else if (blended < -128) { + blended = -128; } - return (int8_t)avg; + return (int8_t)blended; } bool extractRecentRepeater(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const { @@ -249,7 +260,8 @@ public: _recent_repeater_allow_fn = fn; _recent_repeater_allow_ctx = ctx; } - bool setRecentRepeater(const uint8_t* prefix, uint8_t prefix_len, int8_t snr_x4) { + bool setRecentRepeater(const uint8_t* prefix, uint8_t prefix_len, int8_t snr_x4, bool snr_locked = false, + bool bypass_allow_filter = false) { if (prefix == NULL || prefix_len == 0) { return false; } @@ -258,7 +270,8 @@ public: prefix_len = MAX_ROUTE_HASH_BYTES; } - if (_recent_repeater_allow_fn != NULL && !_recent_repeater_allow_fn(prefix, prefix_len, _recent_repeater_allow_ctx)) { + if (!bypass_allow_filter && _recent_repeater_allow_fn != NULL + && !_recent_repeater_allow_fn(prefix, prefix_len, _recent_repeater_allow_ctx)) { return false; } @@ -273,19 +286,160 @@ public: memcpy(existing.prefix, prefix, prefix_len); existing.prefix_len = prefix_len; } - existing.snr_x4 = avgSnrX4RoundUp(existing.snr_x4, snr_x4); + if (snr_locked) { + existing.snr_x4 = snr_x4; + existing.snr_locked = 1; + } else if (!existing.snr_locked) { + existing.snr_x4 = weightedSnrX4RoundUp(existing.snr_x4, snr_x4); + } return true; } - // Ring buffer is enough here; retry fallback only needs a recent prefix->SNR observation. - RecentRepeaterInfo& slot = _recent_repeaters[_next_recent_repeater_idx]; + int slot_idx = -1; + // Prefer empty slots first while preserving newest-order iteration. + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + int idx = (_next_recent_repeater_idx + i) % MAX_RECENT_REPEATERS; + if (_recent_repeaters[idx].prefix_len == 0) { + slot_idx = idx; + break; + } + } + if (slot_idx < 0) { + // Table is full: evict the weakest observed SNR entry. + slot_idx = 0; + int8_t min_snr_x4 = _recent_repeaters[0].snr_x4; + for (int i = 1; i < MAX_RECENT_REPEATERS; i++) { + if (_recent_repeaters[i].snr_x4 < min_snr_x4) { + min_snr_x4 = _recent_repeaters[i].snr_x4; + slot_idx = i; + } + } + } + + RecentRepeaterInfo& slot = _recent_repeaters[slot_idx]; memset(slot.prefix, 0, sizeof(slot.prefix)); memcpy(slot.prefix, prefix, prefix_len); slot.prefix_len = prefix_len; slot.snr_x4 = snr_x4; - _next_recent_repeater_idx = (_next_recent_repeater_idx + 1) % MAX_RECENT_REPEATERS; + slot.retry_count = 0; + slot.fail_count = 0; + slot.snr_locked = snr_locked ? 1 : 0; + _next_recent_repeater_idx = (slot_idx + 1) % MAX_RECENT_REPEATERS; return true; } + bool incrementRecentRepeaterRetryCount(const uint8_t* prefix, uint8_t prefix_len, + bool create_if_missing = false, int8_t seed_snr_x4 = 0, + bool bypass_allow_filter = false) { + if (prefix == NULL || prefix_len == 0) { + return false; + } + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + RecentRepeaterInfo& existing = _recent_repeaters[i]; + if (existing.prefix_len == 0 || !prefixesOverlap(existing.prefix, existing.prefix_len, prefix, prefix_len)) { + continue; + } + if (prefix_len > existing.prefix_len) { + memset(existing.prefix, 0, sizeof(existing.prefix)); + memcpy(existing.prefix, prefix, prefix_len); + existing.prefix_len = prefix_len; + } + if (existing.retry_count < 0xFFFF) { + existing.retry_count++; + } + return true; + } + + if (!create_if_missing || !setRecentRepeater(prefix, prefix_len, seed_snr_x4, false, bypass_allow_filter)) { + return false; + } + + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + RecentRepeaterInfo& existing = _recent_repeaters[i]; + if (existing.prefix_len == 0 || !prefixesOverlap(existing.prefix, existing.prefix_len, prefix, prefix_len)) { + continue; + } + if (existing.retry_count < 0xFFFF) { + existing.retry_count++; + } + return true; + } + return false; + } + bool incrementRecentRepeaterFailCount(const uint8_t* prefix, uint8_t prefix_len, + bool create_if_missing = false, int8_t seed_snr_x4 = 0, + bool bypass_allow_filter = false) { + if (prefix == NULL || prefix_len == 0) { + return false; + } + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + RecentRepeaterInfo& existing = _recent_repeaters[i]; + if (existing.prefix_len == 0 || !prefixesOverlap(existing.prefix, existing.prefix_len, prefix, prefix_len)) { + continue; + } + if (prefix_len > existing.prefix_len) { + memset(existing.prefix, 0, sizeof(existing.prefix)); + memcpy(existing.prefix, prefix, prefix_len); + existing.prefix_len = prefix_len; + } + if (existing.fail_count < 0xFFFF) { + existing.fail_count++; + } + return true; + } + + if (!create_if_missing || !setRecentRepeater(prefix, prefix_len, seed_snr_x4, false, bypass_allow_filter)) { + return false; + } + + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + RecentRepeaterInfo& existing = _recent_repeaters[i]; + if (existing.prefix_len == 0 || !prefixesOverlap(existing.prefix, existing.prefix_len, prefix, prefix_len)) { + continue; + } + if (existing.fail_count < 0xFFFF) { + existing.fail_count++; + } + return true; + } + return false; + } + bool decrementRecentRepeaterSnrX4(const uint8_t* prefix, uint8_t prefix_len, uint8_t amount_x4 = 1) { + if (prefix == NULL || prefix_len == 0 || amount_x4 == 0) { + return false; + } + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + RecentRepeaterInfo& existing = _recent_repeaters[i]; + if (existing.prefix_len == 0 || !prefixesOverlap(existing.prefix, existing.prefix_len, prefix, prefix_len)) { + continue; + } + if (prefix_len > existing.prefix_len) { + memset(existing.prefix, 0, sizeof(existing.prefix)); + memcpy(existing.prefix, prefix, prefix_len); + existing.prefix_len = prefix_len; + } + if (!existing.snr_locked) { + int16_t lowered = (int16_t)existing.snr_x4 - (int16_t)amount_x4; + if (lowered < -128) { + lowered = -128; + } + existing.snr_x4 = (int8_t)lowered; + } + return true; + } + return false; + } const RecentRepeaterInfo* getLatestRecentRepeater() const { for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { int idx = (_next_recent_repeater_idx - 1 - i + MAX_RECENT_REPEATERS) % MAX_RECENT_REPEATERS; @@ -360,6 +514,10 @@ public: } return NULL; } + void clearRecentRepeaters() { + memset(_recent_repeaters, 0, sizeof(_recent_repeaters)); + _next_recent_repeater_idx = 0; + } void resetStats() { _direct_dups = _flood_dups = 0; } }; From c20e57385a6b9912663553040153732fe31b5703 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Thu, 7 May 2026 16:51:49 -0700 Subject: [PATCH 094/214] Fix direct retry diagnostics --- docs/cli_commands.md | 10 ++- examples/simple_repeater/MyMesh.cpp | 133 ++++++++++++++++++++-------- src/Mesh.cpp | 80 +++++++++++++---- src/Mesh.h | 1 + 4 files changed, 162 insertions(+), 62 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 78f8626d..12f49de2 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -111,10 +111,12 @@ - `page`: 1-based page number **Notes:** -- `set` is rejected when the prefix already exists in neighbors. -- Rows are shown newest-first. -- Serial CLI prints all rows (no paging). -- Over LoRa remote CLI, page size is fixed at `4` rows; choose page with `get recent.repeater `. +- `set` stores or updates the prefix in the recent repeater table. +- Rows are sorted by prefix width (3-byte, 2-byte, 1-byte), then SNR descending. +- A full direct retry failure lowers the stored SNR by `0.25 dB`. +- If a full failure has no row yet, it first seeds the row at the active retry cutoff + `2.5 dB`, then applies the `0.25 dB` penalty. +- Serial CLI page size is fixed at `128` rows; choose page with `get recent.repeater `. +- Over LoRa remote CLI, page size is fixed at `7` rows; choose page with `get recent.repeater `. --- diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 10be2212..34b9bb57 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1,5 +1,6 @@ #include "MyMesh.h" #include +#include /* ------------------------------ Config -------------------------------- */ @@ -124,6 +125,45 @@ static void formatRecentRepeaterSnrX4(int8_t snr_x4, char* out, size_t out_len) } } +static int buildSortedRecentRepeaterView(SimpleMeshTables* tables, + const SimpleMeshTables::RecentRepeaterInfo** out, + int out_cap) { + if (tables == NULL || out == NULL || out_cap <= 0) { + return 0; + } + + int total = tables->getRecentRepeaterCount(); + if (total > out_cap) { + total = out_cap; + } + + int count = 0; + for (int i = 0; i < total; i++) { + const auto* info = tables->getRecentRepeaterNewestByIdx(i); + if (info != NULL) { + out[count++] = info; + } + } + + std::stable_sort(out, out + count, [](const SimpleMeshTables::RecentRepeaterInfo* a, + const SimpleMeshTables::RecentRepeaterInfo* b) { + uint8_t a_len = a->prefix_len; + uint8_t b_len = b->prefix_len; + if (a_len > MAX_ROUTE_HASH_BYTES) a_len = MAX_ROUTE_HASH_BYTES; + if (b_len > MAX_ROUTE_HASH_BYTES) b_len = MAX_ROUTE_HASH_BYTES; + + if (a_len != b_len) { + return a_len > b_len; + } + if (a->snr_x4 != b->snr_x4) { + return a->snr_x4 > b->snr_x4; + } + return false; + }); + + return count; +} + static uint8_t decodeTraceHashSize(uint8_t flags, uint8_t route_bytes) { uint8_t code = flags & 0x03; uint8_t size_pow2 = (uint8_t)(1U << code); // legacy TRACE interpretation @@ -605,6 +645,9 @@ void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, u if (packet == NULL) { return; } + if (strcmp(event, "failure") == 0) { + return; + } uint8_t prefix[MAX_ROUTE_HASH_BYTES] = {0}; uint8_t prefix_len = 0; @@ -1561,15 +1604,58 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } } } else { - int total = tables->getRecentRepeaterCount(); + const long page_size = sender_timestamp == 0 ? 128 : 7; + long page_num = 1; + const char* arg = sub; + + if (strncmp(arg, "page ", 5) == 0) { + arg += 5; + while (*arg == ' ') arg++; + } + + if (*arg != 0) { + char* end_ptr = NULL; + page_num = strtol(arg, &end_ptr, 10); + while (end_ptr != NULL && *end_ptr == ' ') end_ptr++; + if (end_ptr == NULL || page_num <= 0 || (end_ptr != NULL && *end_ptr != 0)) { + strcpy(reply, "Err - usage: get recent.repeater [page]"); + return; + } + } + + size_t sorted_size = sizeof(SimpleMeshTables::RecentRepeaterInfo*) * MAX_RECENT_REPEATERS; + const SimpleMeshTables::RecentRepeaterInfo** sorted_recent = + (const SimpleMeshTables::RecentRepeaterInfo**)malloc(sorted_size); + if (sorted_recent == NULL) { + strcpy(reply, "Err - unable to allocate recent repeater view"); + return; + } + + int total = buildSortedRecentRepeaterView(tables, sorted_recent, MAX_RECENT_REPEATERS); if (total <= 0) { strcpy(reply, "> none"); } else { + int total_pages = (total + (int)page_size - 1) / (int)page_size; + if (page_num > total_pages) { + sprintf(reply, "> none (page=%ld/%d)", page_num, total_pages); + free(sorted_recent); + return; + } + + int offset = ((int)page_num - 1) * (int)page_size; + int limit = total - offset; + if (limit > (int)page_size) { + limit = (int)page_size; + } + if (sender_timestamp == 0) { - // Serial CLI: print all entries (no paging). - Serial.printf("Recent repeater table (newest first, total=%d):\n", total); - for (int i = 0; i < total; i++) { - const auto* info = tables->getRecentRepeaterNewestByIdx(i); + Serial.printf("Recent repeater table (3-byte,2-byte,1-byte; SNR desc, page=%ld/%d, n=%d/%d):\n", + page_num, + total_pages, + limit, + total); + for (int i = 0; i < limit; i++) { + const auto* info = sorted_recent[offset + i]; if (info == NULL) { continue; } @@ -1592,40 +1678,8 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply (uint32_t)info->fail_count, info->snr_locked ? ",l" : ""); } - sprintf(reply, "> n=%d/%d", total, total); + sprintf(reply, "> page=%ld/%d n=%d/%d", page_num, total_pages, limit, total); } else { - // Remote CLI: page by fixed size to fit packet-limited reply payload. - long page_num = 1; - const long page_size = 4; - const char* arg = sub; - - if (strncmp(arg, "page ", 5) == 0) { - arg += 5; - while (*arg == ' ') arg++; - } - - if (*arg != 0) { - char* end_ptr = NULL; - page_num = strtol(arg, &end_ptr, 10); - while (end_ptr != NULL && *end_ptr == ' ') end_ptr++; - if (end_ptr == NULL || page_num <= 0 || (end_ptr != NULL && *end_ptr != 0)) { - strcpy(reply, "Err - usage: get recent.repeater [page]"); - return; - } - } - - int total_pages = (total + (int)page_size - 1) / (int)page_size; - if (page_num > total_pages) { - sprintf(reply, "> none (page=%ld/%d)", page_num, total_pages); - return; - } - - int offset = ((int)page_num - 1) * (int)page_size; - int limit = total - offset; - if (limit > (int)page_size) { - limit = (int)page_size; - } - int written = snprintf(reply, 160, "> page=%ld/%d n=%d/%d", page_num, total_pages, limit, total); bool truncated = false; if (written < 0) { @@ -1635,7 +1689,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply for (int i = 0; i < limit; i++) { int idx = offset + i; - const auto* info = tables->getRecentRepeaterNewestByIdx(idx); + const auto* info = sorted_recent[idx]; if (info == NULL) { continue; } @@ -1668,6 +1722,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply } } } + free(sorted_recent); } } else{ _cli.handleCommand(sender_timestamp, command, reply); // common CLI commands diff --git a/src/Mesh.cpp b/src/Mesh.cpp index bc34c5ee..a84a1fda 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -16,6 +16,7 @@ void Mesh::begin() { _direct_retries[i].priority = 0; _direct_retries[i].progress_marker = 0; _direct_retries[i].expect_path_growth = false; + _direct_retries[i].waiting_final_echo = false; _direct_retries[i].queued = false; _direct_retries[i].active = false; } @@ -26,7 +27,25 @@ void Mesh::loop() { Dispatcher::loop(); for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { - if (!_direct_retries[i].active || !_direct_retries[i].queued || !millisHasNowPassed(_direct_retries[i].retry_at)) { + if (!_direct_retries[i].active) { + continue; + } + + if (_direct_retries[i].waiting_final_echo) { + if (!millisHasNowPassed(_direct_retries[i].retry_at)) { + continue; + } + + uint32_t elapsed_millis = _direct_retries[i].retry_started_at == 0 + ? 0 + : (uint32_t)(_ms->getMillis() - _direct_retries[i].retry_started_at); + onDirectRetryEvent("failed_all_tries", _direct_retries[i].packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); + onDirectRetryEvent("failure", _direct_retries[i].packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); + clearDirectRetrySlot(i); + continue; + } + + if (!_direct_retries[i].queued || !millisHasNowPassed(_direct_retries[i].retry_at)) { continue; } @@ -454,6 +473,9 @@ void Mesh::routeDirectRecvAcks(Packet* packet, uint32_t delay_millis) { } void Mesh::clearDirectRetrySlot(int idx) { + if (_direct_retries[idx].waiting_final_echo && _direct_retries[idx].packet != NULL) { + releasePacket(_direct_retries[idx].packet); + } _direct_retries[idx].packet = NULL; _direct_retries[idx].trigger_packet = NULL; _direct_retries[idx].retry_at = 0; @@ -462,6 +484,7 @@ void Mesh::clearDirectRetrySlot(int idx) { _direct_retries[idx].priority = 0; _direct_retries[idx].progress_marker = 0; _direct_retries[idx].expect_path_growth = false; + _direct_retries[idx].waiting_final_echo = false; _direct_retries[idx].queued = false; _direct_retries[idx].active = false; } @@ -497,24 +520,30 @@ bool Mesh::cancelDirectRetryOnEcho(const Packet* packet) { continue; } - if (_direct_retries[i].queued) { - if (_direct_retries[i].expect_path_growth - && _direct_retries[i].packet != NULL - && _direct_retries[i].progress_marker < packet->path_len) { - // For retry-good quality, use the received echo packet SNR (return-link quality). - _direct_retries[i].packet->_snr = packet->_snr; + int8_t echo_snr_x4 = packet->_snr; + if (_direct_retries[i].queued || _direct_retries[i].waiting_final_echo) { + if (_direct_retries[i].packet != NULL) { + // Success quality comes from the received downstream echo, not the original upstream RX. + _direct_retries[i].packet->_snr = echo_snr_x4; } - for (int j = 0; j < _mgr->getOutboundTotal(); j++) { - if (_mgr->getOutboundByIdx(j) == _direct_retries[i].packet) { - Packet* pending = _mgr->removeOutboundByIdx(j); - if (pending) { - releasePacket(pending); + uint32_t echo_millis = _direct_retries[i].echo_wait_started_at == 0 + ? 0 + : (uint32_t)(_ms->getMillis() - _direct_retries[i].echo_wait_started_at); + uint8_t retry_attempt = _direct_retries[i].waiting_final_echo + ? _direct_retries[i].retry_attempts_sent + : _direct_retries[i].retry_attempts_sent + 1; + onDirectRetryEvent("good", _direct_retries[i].packet, echo_millis, retry_attempt); + if (_direct_retries[i].queued) { + for (int j = 0; j < _mgr->getOutboundTotal(); j++) { + if (_mgr->getOutboundByIdx(j) == _direct_retries[i].packet) { + Packet* pending = _mgr->removeOutboundByIdx(j); + if (pending) { + releasePacket(pending); + } + break; } - break; } } - onDirectRetryEvent("canceled_echo", _direct_retries[i].packet, 0); - onDirectRetryEvent("good", _direct_retries[i].packet, 0); clearDirectRetrySlot(i); } else { if (_direct_retries[i].expect_path_growth @@ -551,9 +580,19 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { max_attempts = DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX; } if (_direct_retries[i].retry_attempts_sent >= max_attempts) { - onDirectRetryEvent("failed_all_tries", packet, 0); - onDirectRetryEvent("failure", packet, 0); - clearDirectRetrySlot(i); + Packet* final_wait = obtainNewPacket(); + if (final_wait == NULL) { + onDirectRetryEvent("dropped_no_packet", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); + onDirectRetryEvent("failure", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); + clearDirectRetrySlot(i); + continue; + } + + *final_wait = *packet; + _direct_retries[i].packet = final_wait; + _direct_retries[i].retry_at = futureMillis(_direct_retries[i].retry_delay); + _direct_retries[i].waiting_final_echo = true; + _direct_retries[i].queued = false; continue; } @@ -572,7 +611,8 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { _direct_retries[i].packet = retry; _direct_retries[i].retry_delay = retry_delay; _direct_retries[i].retry_at = futureMillis(retry_delay); - onDirectRetryEvent("queued", retry, retry_delay); + _direct_retries[i].waiting_final_echo = false; + onDirectRetryEvent("queued", retry, retry_delay, _direct_retries[i].retry_attempts_sent + 1); } else { onDirectRetryEvent("dropped_queue_full", retry, retry_delay); onDirectRetryEvent("failure", retry, 0); @@ -603,6 +643,7 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { _direct_retries[i].packet = retry; _direct_retries[i].trigger_packet = NULL; _direct_retries[i].queued = true; + _direct_retries[i].waiting_final_echo = false; _direct_retries[i].retry_at = futureMillis(_direct_retries[i].retry_delay); onDirectRetryEvent("queued", retry, _direct_retries[i].retry_delay); } else { @@ -726,6 +767,7 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { _direct_retries[slot_idx].priority = priority; _direct_retries[slot_idx].progress_marker = progress_marker; _direct_retries[slot_idx].expect_path_growth = expect_path_growth; + _direct_retries[slot_idx].waiting_final_echo = false; _direct_retries[slot_idx].queued = false; _direct_retries[slot_idx].active = true; onDirectRetryEvent("armed", packet, retry_delay); diff --git a/src/Mesh.h b/src/Mesh.h index dd58d754..85848875 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -39,6 +39,7 @@ class Mesh : public Dispatcher { uint8_t priority; uint8_t progress_marker; bool expect_path_growth; + bool waiting_final_echo; bool queued; bool active; }; From dea5ed790fe2166f7248da97270f186c1f05e752 Mon Sep 17 00:00:00 2001 From: Nick Le Mouton Date: Fri, 5 Jun 2026 21:25:25 +1200 Subject: [PATCH 095/214] Add SECURITY.md --- SECURITY.md | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..a4b2207d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,57 @@ +# Security Policy + +## Supported Versions + +Security fixes are applied to the latest release only. We do not backport +fixes to older versions. + +| Version | Supported | +|---------|-----------| +| 1.15+ | ✅ | +| <1.15 | ❌ | + +## Reporting a Vulnerability + +**Please do not report security vulnerabilities through public GitHub issues.** + +Use GitHub's private vulnerability reporting instead: +1. Go to the **Security** tab of this repository +2. Click **Report a vulnerability** +3. Fill in the details and submit + +### What to include + +A useful report tells us: +- Which component or file is affected +- What an attacker can do (impact) and under what conditions +- A minimal reproduction case or proof-of-concept if you have one +- Whether you believe it is remotely exploitable + +You do not need a working exploit to report. An incomplete report is better +than no report. + +## What to expect + +This is a volunteer-maintained open-source project. We will do our best to +respond in a reasonable timeframe, but cannot commit to specific deadlines. + +We ask that you give us a fair opportunity to investigate and address the +issue before any public disclosure. If you have not heard back after +**90 days**, feel free to follow up or proceed with disclosure at your +discretion. + +## Scope + +In scope: +- Remote code execution, memory corruption, or denial-of-service via crafted + radio packets +- Authentication or encryption bypasses +- Vulnerabilities in the packet routing or path handling logic + +Out of scope: +- Physical access attacks (e.g., JTAG, UART extraction of keys) +- Regulatory compliance (duty cycle, frequency restrictions) +- Jamming or other physical-layer radio interference +- Issues in third-party libraries (RadioLib, Crypto, etc.) — report those + upstream +- "Best practice" suggestions without a demonstrated attack path From 07bfe9069565729205d79edf4cd2a0071b22fe3f Mon Sep 17 00:00:00 2001 From: liamcottle Date: Tue, 9 Jun 2026 00:31:48 +1200 Subject: [PATCH 096/214] free packet on parse failure --- examples/companion_radio/MyMesh.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 6fbb0f74..07353988 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1981,6 +1981,7 @@ void MyMesh::handleCmdFrame(size_t len) { sendPacket(pkt, priority, 0); writeOKFrame(); } else { + _mgr->free(pkt); writeErrFrame(ERR_CODE_ILLEGAL_ARG); } } else { From ae0bb7ee9544db0a0eb05ce7405432cb8ed6496a Mon Sep 17 00:00:00 2001 From: liamcottle Date: Tue, 9 Jun 2026 00:42:58 +1200 Subject: [PATCH 097/214] use releasePacket instead of _mgr->free --- examples/companion_radio/MyMesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 07353988..c468967f 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1981,7 +1981,7 @@ void MyMesh::handleCmdFrame(size_t len) { sendPacket(pkt, priority, 0); writeOKFrame(); } else { - _mgr->free(pkt); + releasePacket(pkt); writeErrFrame(ERR_CODE_ILLEGAL_ARG); } } else { From e67933ca2a39e0cb590d53c4c310d6380ee0f115 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Mon, 8 Jun 2026 21:11:49 +0700 Subject: [PATCH 098/214] PowerSaving 16 --- boards/nrf52840_s140_v6.ld | 15 +- boards/nrf52840_s140_v6_extrafs.ld | 15 +- boards/nrf52840_s140_v7.ld | 15 +- boards/nrf52840_s140_v7_extrafs.ld | 12 +- build-iotthinks.sh | 135 ++++++++++++++++ build.sh | 7 +- docs/cli_commands.md | 61 +++++++ examples/companion_radio/DataStore.cpp | 2 + examples/companion_radio/MyMesh.cpp | 29 ++++ examples/companion_radio/NodePrefs.h | 1 + examples/companion_radio/main.cpp | 40 +++++ examples/simple_repeater/MyMesh.cpp | 153 ++++++++++++++++-- examples/simple_repeater/MyMesh.h | 2 +- examples/simple_repeater/main.cpp | 7 +- examples/simple_room_server/MyMesh.cpp | 10 ++ examples/simple_room_server/MyMesh.h | 3 + examples/simple_room_server/main.cpp | 13 ++ src/MeshCore.h | 3 + src/helpers/ArduinoHelpers.cpp | 6 + src/helpers/ArduinoHelpers.h | 42 ++++- src/helpers/ArduinoSerialInterface.cpp | 4 + src/helpers/ArduinoSerialInterface.h | 1 + src/helpers/BaseSerialInterface.h | 1 + src/helpers/ClientACL.h | 3 +- src/helpers/CommonCLI.cpp | 92 ++++++++++- src/helpers/CommonCLI.h | 2 + src/helpers/ESP32Board.h | 93 ++++++++++- src/helpers/MeshadventurerBoard.h | 30 ---- src/helpers/esp32/SerialBLEInterface.cpp | 4 + src/helpers/esp32/SerialBLEInterface.h | 1 + src/helpers/esp32/SerialWifiInterface.cpp | 4 + src/helpers/esp32/SerialWifiInterface.h | 1 + src/helpers/esp32/TBeamBoard.h | 24 --- src/helpers/nrf52/SerialBLEInterface.cpp | 4 + src/helpers/nrf52/SerialBLEInterface.h | 1 + .../sensors/EnvironmentSensorManager.cpp | 15 ++ .../sensors/MicroNMEALocationProvider.h | 2 + src/helpers/ui/ST7735Display.cpp | 9 ++ variants/gat562_30s_mesh_kit/platformio.ini | 3 +- variants/gat562_mesh_evb_pro/platformio.ini | 2 +- .../gat562_mesh_tracker_pro/platformio.ini | 3 +- variants/gat562_mesh_watch13/platformio.ini | 2 +- variants/heltec_ct62/platformio.ini | 4 + variants/heltec_e213/HeltecE213Board.cpp | 27 ---- variants/heltec_e213/HeltecE213Board.h | 3 - variants/heltec_e290/HeltecE290Board.cpp | 27 ---- variants/heltec_e290/HeltecE290Board.h | 3 - variants/heltec_t096/LoRaFEMControl.h | 5 +- variants/heltec_t096/T096Board.cpp | 24 ++- variants/heltec_t096/T096Board.h | 3 + variants/heltec_t096/platformio.ini | 8 +- variants/heltec_t114/platformio.ini | 2 +- variants/heltec_t190/HeltecT190Board.cpp | 27 ---- variants/heltec_t190/HeltecT190Board.h | 3 - variants/heltec_tracker/platformio.ini | 4 + .../HeltecTrackerV2Board.cpp | 49 +++--- .../heltec_tracker_v2/HeltecTrackerV2Board.h | 5 +- variants/heltec_tracker_v2/LoRaFEMControl.h | 5 +- variants/heltec_tracker_v2/platformio.ini | 4 + variants/heltec_v2/HeltecV2Board.h | 25 --- variants/heltec_v2/platformio.ini | 4 + variants/heltec_v3/HeltecV3Board.h | 29 ---- variants/heltec_v3/platformio.ini | 8 + variants/heltec_v4/HeltecV4Board.cpp | 53 +++--- variants/heltec_v4/HeltecV4Board.h | 5 +- variants/heltec_v4/LoRaFEMControl.h | 5 +- variants/heltec_v4/platformio.ini | 18 +++ variants/heltec_wireless_paper/platformio.ini | 4 + variants/lilygo_t3s3/platformio.ini | 4 + variants/lilygo_tbeam_1w/platformio.ini | 5 + variants/lilygo_tbeam_SX1262/platformio.ini | 7 + variants/lilygo_tbeam_SX1276/platformio.ini | 4 + .../platformio.ini | 4 + variants/lilygo_tdeck/TDeckBoard.h | 24 --- variants/lilygo_tlora_v2_1/platformio.ini | 6 +- variants/promicro/platformio.ini | 1 + variants/rak3112/RAK3112Board.h | 29 ---- variants/rak3401/platformio.ini | 3 +- variants/rak4631/platformio.ini | 3 +- variants/rak_wismesh_tag/platformio.ini | 1 + variants/sensecap_solar/platformio.ini | 2 +- variants/station_g2/StationG2Board.h | 24 --- variants/thinknode_m2/ThinknodeM2Board.cpp | 58 +++---- variants/thinknode_m2/ThinknodeM2Board.h | 4 - variants/thinknode_m5/ThinknodeM5Board.cpp | 8 - variants/thinknode_m5/ThinknodeM5Board.h | 3 - variants/xiao_c3/XiaoC3Board.h | 32 ---- variants/xiao_c3/platformio.ini | 5 + variants/xiao_c6/platformio.ini | 1 + variants/xiao_nrf52/platformio.ini | 2 +- variants/xiao_s3/platformio.ini | 4 + variants/xiao_s3_wio/platformio.ini | 4 + 92 files changed, 1000 insertions(+), 464 deletions(-) create mode 100644 build-iotthinks.sh create mode 100644 src/helpers/ArduinoHelpers.cpp diff --git a/boards/nrf52840_s140_v6.ld b/boards/nrf52840_s140_v6.ld index 6dad975b..d0c7d1dc 100644 --- a/boards/nrf52840_s140_v6.ld +++ b/boards/nrf52840_s140_v6.ld @@ -7,6 +7,9 @@ MEMORY { FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0xED000 - 0x26000 + /* To keep data in RAM across resets */ + PERSISTENT_RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 8 + /* SRAM required by Softdevice depend on * - Attribute Table Size (Number of Services and Characteristics) * - Vendor UUID count @@ -14,11 +17,19 @@ MEMORY * - Concurrent connection peripheral + central + secure links * - Event Len, HVN queue, Write CMD queue */ - RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 0x20040000 - 0x20006000 + RAM (rwx) : ORIGIN = 0x20006000 + 8, LENGTH = 0x20040000 - 0x20006000 - 8 } SECTIONS { + . = ALIGN(4); + .persistent (NOLOAD) : + { + KEEP(*(.persistent_magic)) + KEEP(*(.persistent_data)) + . = ALIGN(4); + } > PERSISTENT_RAM + . = ALIGN(4); .svc_data : { @@ -33,6 +44,6 @@ SECTIONS KEEP(*(.fs_data)) PROVIDE(__stop_fs_data = .); } > RAM -} INSERT AFTER .data; +} INCLUDE "nrf52_common.ld" diff --git a/boards/nrf52840_s140_v6_extrafs.ld b/boards/nrf52840_s140_v6_extrafs.ld index 35261067..bd454747 100644 --- a/boards/nrf52840_s140_v6_extrafs.ld +++ b/boards/nrf52840_s140_v6_extrafs.ld @@ -7,6 +7,9 @@ MEMORY { FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0xD4000 - 0x26000 + /* To keep data in RAM across resets */ + PERSISTENT_RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 8 + /* SRAM required by Softdevice depend on * - Attribute Table Size (Number of Services and Characteristics) * - Vendor UUID count @@ -14,11 +17,19 @@ MEMORY * - Concurrent connection peripheral + central + secure links * - Event Len, HVN queue, Write CMD queue */ - RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 0x20040000 - 0x20006000 + RAM (rwx) : ORIGIN = 0x20006000 + 8, LENGTH = 0x20040000 - 0x20006000 - 8 } SECTIONS { + . = ALIGN(4); + .persistent (NOLOAD) : + { + KEEP(*(.persistent_magic)) + KEEP(*(.persistent_data)) + . = ALIGN(4); + } > PERSISTENT_RAM + . = ALIGN(4); .svc_data : { @@ -33,6 +44,6 @@ SECTIONS KEEP(*(.fs_data)) PROVIDE(__stop_fs_data = .); } > RAM -} INSERT AFTER .data; +} INCLUDE "nrf52_common.ld" diff --git a/boards/nrf52840_s140_v7.ld b/boards/nrf52840_s140_v7.ld index 6aaeb403..2333238f 100644 --- a/boards/nrf52840_s140_v7.ld +++ b/boards/nrf52840_s140_v7.ld @@ -7,6 +7,9 @@ MEMORY { FLASH (rx) : ORIGIN = 0x27000, LENGTH = 0xED000 - 0x27000 + /* To keep data in RAM across resets */ + PERSISTENT_RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 8 + /* SRAM required by Softdevice depend on * - Attribute Table Size (Number of Services and Characteristics) * - Vendor UUID count @@ -14,11 +17,19 @@ MEMORY * - Concurrent connection peripheral + central + secure links * - Event Len, HVN queue, Write CMD queue */ - RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 0x20040000 - 0x20006000 + RAM (rwx) : ORIGIN = 0x20006000 + 8, LENGTH = 0x20040000 - 0x20006000 - 8 } SECTIONS { + . = ALIGN(4); + .persistent (NOLOAD) : + { + KEEP(*(.persistent_magic)) + KEEP(*(.persistent_data)) + . = ALIGN(4); + } > PERSISTENT_RAM + . = ALIGN(4); .svc_data : { @@ -33,6 +44,6 @@ SECTIONS KEEP(*(.fs_data)) PROVIDE(__stop_fs_data = .); } > RAM -} INSERT AFTER .data; +} INCLUDE "nrf52_common.ld" diff --git a/boards/nrf52840_s140_v7_extrafs.ld b/boards/nrf52840_s140_v7_extrafs.ld index 5956183a..48348188 100644 --- a/boards/nrf52840_s140_v7_extrafs.ld +++ b/boards/nrf52840_s140_v7_extrafs.ld @@ -14,11 +14,19 @@ MEMORY * - Concurrent connection peripheral + central + secure links * - Event Len, HVN queue, Write CMD queue */ - RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 0x20040000 - 0x20006000 + RAM (rwx) : ORIGIN = 0x20006000 + 8, LENGTH = 0x20040000 - 0x20006000 - 8 } SECTIONS { + . = ALIGN(4); + .persistent (NOLOAD) : + { + KEEP(*(.persistent_magic)) + KEEP(*(.persistent_data)) + . = ALIGN(4); + } > PERSISTENT_RAM + . = ALIGN(4); .svc_data : { @@ -33,6 +41,6 @@ SECTIONS KEEP(*(.fs_data)) PROVIDE(__stop_fs_data = .); } > RAM -} INSERT AFTER .data; +} INCLUDE "nrf52_common.ld" diff --git a/build-iotthinks.sh b/build-iotthinks.sh new file mode 100644 index 00000000..613789d0 --- /dev/null +++ b/build-iotthinks.sh @@ -0,0 +1,135 @@ +# sh ./build-repeaters-iotthinks.sh +export FIRMWARE_VERSION="PowerSaving16" + +############# Repeaters ############# +# Commonly-used boards +## ESP32 - 17 boards +sh build.sh build-firmware \ +Heltec_v3_repeater \ +Heltec_WSL3_repeater \ +heltec_v4_repeater \ +Station_G2_repeater \ +T_Beam_S3_Supreme_SX1262_repeater \ +Tbeam_SX1262_repeater \ +LilyGo_T3S3_sx1262_repeater \ +Xiao_S3_WIO_repeater \ +Xiao_C3_repeater \ +Xiao_C6_repeater_ \ +Heltec_E290_repeater \ +Heltec_Wireless_Tracker_repeater \ +LilyGo_TBeam_1W_repeater \ +Xiao_S3_repeater \ +heltec_tracker_v2_repeater \ +Heltec_Wireless_Paper_repeater \ +Heltec_ct62_repeater + +## NRF52 - 17 boards +sh build.sh build-firmware \ +RAK_4631_repeater \ +Heltec_t114_repeater \ +Xiao_nrf52_repeater \ +Heltec_mesh_solar_repeater \ +ProMicro_repeater \ +SenseCap_Solar_repeater \ +t1000e_repeater \ +LilyGo_T-Echo_repeater \ +WioTrackerL1_repeater \ +RAK_3401_repeater \ +RAK_WisMesh_Tag_repeater \ +GAT562_30S_Mesh_Kit_repeater \ +GAT562_Mesh_Tracker_Pro_repeater \ +ikoka_nano_nrf_22dbm_repeater \ +ikoka_nano_nrf_30dbm_repeater \ +ikoka_nano_nrf_33dbm_repeater \ +ThinkNode_M1_repeater \ +Heltec_t096_repeater + +## ESP32, SX1276 - 3 boards +sh build.sh build-firmware \ +Heltec_v2_repeater \ +LilyGo_TLora_V2_1_1_6_repeater \ +Tbeam_SX1276_repeater + +############# Room Server ############# +# ESP32 - 7 boards +sh build.sh build-firmware \ +Heltec_v3_room_server \ +heltec_v4_room_server \ +LilyGo_TBeam_1W_room_server \ +Heltec_WSL3_room_server \ +Xiao_S3_room_server \ +heltec_tracker_v2_room_server \ +Heltec_Wireless_Paper_room_server + +# NRF52 - 6 boards +sh build.sh build-firmware \ +RAK_4631_room_server \ +Heltec_t114_room_server \ +Xiao_nrf52_room_server \ +t1000e_room_server \ +WioTrackerL1_room_server \ +RAK_3401_room_server \ +Heltec_t096_room_server + +############# Companions BLE ############# +# NRF52 - 12 boards +sh build.sh build-firmware \ +RAK_4631_companion_radio_ble \ +Heltec_t114_companion_radio_ble \ +Xiao_nrf52_companion_radio_ble \ +t1000e_companion_radio_ble \ +LilyGo_T-Echo_companion_radio_ble \ +WioTrackerL1_companion_radio_ble \ +RAK_3401_companion_radio_ble \ +RAK_WisMesh_Tag_companion_radio_ble \ +SenseCap_Solar_companion_radio_ble \ +ThinkNode_M1_companion_radio_ble \ +Heltec_t096_companion_radio_ble \ +Heltec_t096_companion_radio_ble_femoff + +############# Companions BLE PS ############# +# ESP32 - 18 boards +sh build.sh build-firmware \ +Heltec_v3_companion_radio_ble_ps \ +heltec_v4_companion_radio_ble_ps \ +heltec_v4_3_companion_radio_ble_ps_femoff \ +Xiao_C3_companion_radio_ble_ps \ +Xiao_S3_companion_radio_ble_ps \ +Xiao_S3_WIO_companion_radio_ble_ps \ +Heltec_v2_companion_radio_ble_ps \ +LilyGo_TBeam_1W_companion_radio_ble_ps \ +Heltec_WSL3_companion_radio_ble_ps \ +Heltec_Wireless_Tracker_companion_radio_ble_ps \ +heltec_tracker_v2_companion_radio_ble_ps \ +Heltec_Wireless_Paper_companion_radio_ble_ps \ +LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps \ +Heltec_ct62_companion_radio_ble_ps \ +T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps \ +Tbeam_SX1262_companion_radio_ble_ps \ +heltec_v4_expansionkit_tft_companion_radio_ble_ps \ +LilyGo_T3S3_sx1262_companion_radio_ble_ps + +# Not working +Tbeam_SX1276_companion_radio_ble_ps \ + +############# Companions USB ############# +sh build.sh build-firmware \ +Heltec_t096_companion_radio_usb + +############# Sample builds ############# +# 14 boards +sh build.sh build-firmware \ +Heltec_v3_repeater \ +heltec_v4_repeater \ +Xiao_C3_repeater \ +Xiao_C6_repeater_ \ +RAK_4631_repeater \ +Heltec_t096_repeater \ +Heltec_v3_companion_radio_ble_ps \ +heltec_v4_companion_radio_ble_ps \ +heltec_v4_3_companion_radio_ble_ps_femoff \ +Xiao_C3_companion_radio_ble_ps \ +Xiao_C6_companion_radio_ble_ \ +RAK_4631_companion_radio_ble \ +Heltec_t096_companion_radio_ble \ +Heltec_t096_companion_radio_ble_femoff diff --git a/build.sh b/build.sh index 313c4c47..006eae96 100755 --- a/build.sh +++ b/build.sh @@ -134,7 +134,8 @@ build_firmware() { # set firmware version string # e.g: v1.0.0-abcdef - FIRMWARE_VERSION_STRING="${FIRMWARE_VERSION}-${COMMIT_HASH}" + # FIRMWARE_VERSION_STRING="${FIRMWARE_VERSION}-${COMMIT_HASH}" + FIRMWARE_VERSION_STRING="${FIRMWARE_VERSION}" # craft filename # e.g: RAK_4631_Repeater-v1.0.0-SHA @@ -152,8 +153,8 @@ build_firmware() { # build merge-bin for esp32 fresh install, copy .bins to out folder (e.g: Heltec_v3_room_server-v1.0.0-SHA.bin) if [ "$ENV_PLATFORM" == "ESP32_PLATFORM" ]; then pio run -t mergebin -e $1 - cp .pio/build/$1/firmware.bin out/${FIRMWARE_FILENAME}.bin 2>/dev/null || true - cp .pio/build/$1/firmware-merged.bin out/${FIRMWARE_FILENAME}-merged.bin 2>/dev/null || true + cp .pio/build/$1/firmware.bin out/${FIRMWARE_FILENAME}-upgrade.bin 2>/dev/null || true + cp .pio/build/$1/firmware-merged.bin out/${FIRMWARE_FILENAME}-freshInstall-merged.bin 2>/dev/null || true fi # build .uf2 for nrf52 boards, copy .uf2 and .zip to out folder (e.g: RAK_4631_Repeater-v1.0.0-SHA.uf2) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 9accb299..4d3270b2 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -219,6 +219,20 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### View or change the boosted receive gain mode +**Usage:** +- `get radio.rxgain` +- `set radio.rxgain ` + +**Parameters:** +- `state`: `on`|`off` + +**Default:** `off` + +**Note:** Only available on SX1262 and SX1268 based boards. + +--- + #### Change the radio parameters for a set duration **Usage:** - `tempradio ,,,,` @@ -263,6 +277,20 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### View or change the LoRa FEM receive-path gain state on supported boards +**Usage:** +- `get radio.fem.rxgain` +- `set radio.fem.rxgain ` + +**Parameters:** +- `state`: `on`|`off` + +**Notes:** +- This controls the external LoRa FEM receive-path LNA where the board supports it. +- This is separate from `radio.rxgain`, which controls the radio chip receive gain mode. + +--- + ### System #### View or change this node's name @@ -417,6 +445,18 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### View or set reboot interval (Repeater and room server) +**Usage:** +- `get reboot.interval` +- `set reboot.interval ` + +**Parameters:** +- `hours`: 0-255. 0 is disabled + +**Default:** `0` (disabled) + +--- + ### Routing #### View or change this node's repeat flag @@ -752,6 +792,27 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### View or set the direct path override for the current remote client +**Usage:** +- `get outpath` +- `set outpath ` +- `set outpath direct` +- `set outpath clear` +- `set outpath flood` + +**Parameters:** +- `hopN_hex`: Hop hash, `2`, `4`, or `6` hex characters. All hops must use the same width. + +**Notes:** +- These commands require remote client context (they target the caller's ACL entry). +- The path hash size is inferred from the hop hash width. +- `outpath` overrides the primary direct route used for replies to the caller. +- `direct` sets a zero-hop direct route for a caller reachable without repeaters. +- `clear` forgets the current direct path and allows normal path discovery to repopulate it. +- `flood` forces replies to use flood packets until the client logs in again. + +--- + #### Create a new region **Usage:** - `region put [parent_name]` diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index bf2f36c3..fdb924ad 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -233,6 +233,7 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no file.read((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89 file.read((uint8_t *)_prefs.default_scope_name, sizeof(_prefs.default_scope_name)); // 90 file.read((uint8_t *)_prefs.default_scope_key, sizeof(_prefs.default_scope_key)); // 121 + file.read((uint8_t *)&_prefs.radio_fem_rxgain, sizeof(_prefs.radio_fem_rxgain)); // 122 file.close(); } @@ -273,6 +274,7 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ file.write((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89 file.write((uint8_t *)_prefs.default_scope_name, sizeof(_prefs.default_scope_name)); // 90 file.write((uint8_t *)_prefs.default_scope_key, sizeof(_prefs.default_scope_key)); // 121 + file.write((uint8_t *)&_prefs.radio_fem_rxgain, sizeof(_prefs.radio_fem_rxgain)); // 122 file.close(); } diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 6fbb0f74..6bf2671a 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -62,6 +62,8 @@ #define CMD_SET_DEFAULT_FLOOD_SCOPE 63 #define CMD_GET_DEFAULT_FLOOD_SCOPE 64 #define CMD_SEND_RAW_PACKET 65 +#define CMD_GET_RADIO_FEM_RXGAIN 66 +#define CMD_SET_RADIO_FEM_RXGAIN 67 // Stats sub-types for CMD_GET_STATS #define STATS_TYPE_CORE 0 @@ -886,6 +888,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe _prefs.rx_boosted_gain = 1; // enabled by default #endif #endif + _prefs.radio_fem_rxgain = 1; } void MyMesh::begin(bool has_display) { @@ -935,6 +938,7 @@ void MyMesh::begin(bool has_display) { _prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, -9, MAX_LORA_TX_POWER); _prefs.gps_enabled = constrain(_prefs.gps_enabled, 0, 1); // Ensure boolean 0 or 1 _prefs.gps_interval = constrain(_prefs.gps_interval, 0, 86400); // Max 24 hours + _prefs.radio_fem_rxgain = constrain(_prefs.radio_fem_rxgain, 0, 1); #ifdef BLE_PIN_CODE // 123456 by default if (_prefs.ble_pin == 0) { @@ -964,6 +968,7 @@ void MyMesh::begin(bool has_display) { radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); radio_driver.setTxPower(_prefs.tx_power_dbm); radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); + board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s", radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled"); } @@ -1821,6 +1826,30 @@ void MyMesh::handleCmdFrame(size_t len) { } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } + } else if (cmd_frame[0] == CMD_GET_RADIO_FEM_RXGAIN) { + if (!board.canControlLoRaFemLna()) { + writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); + } else { + out_frame[0] = RESP_CODE_OK; + uint8_t value = board.isLoRaFemLnaEnabled() ? 1 : 0; + memcpy(&out_frame[1], &value, 1); + _serial->writeFrame(out_frame, 2); + } + } else if (cmd_frame[0] == CMD_SET_RADIO_FEM_RXGAIN && len >= 2) { + uint8_t value = cmd_frame[1]; + if (!board.canControlLoRaFemLna()) { + writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); + } else if (value <= 1) { + _prefs.radio_fem_rxgain = value; + if (board.setLoRaFemLnaEnabled(value != 0)) { + savePrefs(); + writeOKFrame(); + } else { + writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); + } + } else { + writeErrFrame(ERR_CODE_ILLEGAL_ARG); + } } else if (cmd_frame[0] == CMD_GET_ADVERT_PATH && len >= PUB_KEY_SIZE+2) { // FUTURE use: uint8_t reserved = cmd_frame[1]; uint8_t *pub_key = &cmd_frame[2]; diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 48c381ce..6598a69c 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -29,6 +29,7 @@ struct NodePrefs { // persisted to file uint32_t gps_interval; // GPS read interval in seconds uint8_t autoadd_config; // bitmask for auto-add contacts config uint8_t rx_boosted_gain; // SX126x RX boosted gain mode (0=power saving, 1=boosted) + uint8_t radio_fem_rxgain; // LoRa FEM RX gain setting uint8_t client_repeat; uint8_t path_hash_mode; // which path mode to use when sending uint8_t autoadd_max_hops; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index ef9b6bfc..f10cb170 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -2,6 +2,11 @@ #include #include "MyMesh.h" +#ifdef ESP32_PLATFORM +#include "esp_pm.h" +#include "esp_bt.h" +#endif + // Believe it or not, this std C function is busted on some platforms! static uint32_t _atoi(const char* sp) { uint32_t n = 0; @@ -240,6 +245,37 @@ void setup() { #endif board.onBootComplete(); + +#ifdef ESP32_PLATFORM +#if !CONFIG_IDF_TARGET_ESP32C6 + // Enable BLE sleep + esp_err_t errBLESleep = esp_bt_sleep_enable(); + if (errBLESleep == ESP_OK) { + Serial.println("Bluetooth sleep enabled successfully"); + } else { + Serial.printf("Bluetooth sleep enable failed: %s\n", esp_err_to_name(errBLESleep)); + } +#endif + +#if CONFIG_IDF_TARGET_ESP32C3 + esp_pm_config_esp32c3_t pm_config; +#elif CONFIG_IDF_TARGET_ESP32S3 + esp_pm_config_esp32s3_t pm_config; +#elif CONFIG_IDF_TARGET_ESP32 + esp_pm_config_esp32_t pm_config; +#elif CONFIG_IDF_TARGET_ESP32C6 + esp_pm_config_t pm_config; +#endif + + // Configure Power Management + pm_config = { .max_freq_mhz = 80, .min_freq_mhz = 40, .light_sleep_enable = true }; + esp_err_t errPM = esp_pm_configure(&pm_config); + if (errPM == ESP_OK) { + Serial.println("Power Management configured successfully"); + } else { + Serial.printf("Power Management failed to configure: %d\r\n", errPM); + } +#endif } void loop() { @@ -253,6 +289,10 @@ void loop() { if (!the_mesh.hasPendingWork()) { #if defined(NRF52_PLATFORM) board.sleep(0); // nrf ignores seconds param, sleeps whenever possible +#elif defined(ESP32_PLATFORM) + if (!serial_interface.isReadBusy() && !serial_interface.isWriteBusy()) { // BLE is not busy + vTaskDelay(pdMS_TO_TICKS(10)); // attempt to sleep + } #endif } diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 09690749..1b0ca191 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -676,7 +676,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, mesh::Packet *reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len); if (reply) { - if (client->out_path_len != OUT_PATH_UNKNOWN) { // we have an out_path, so send DIRECT + if (mesh::Packet::isValidPathLen(client->out_path_len)) { // we have an out_path, so send DIRECT sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY); } else { sendFloodReply(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize()); @@ -709,10 +709,10 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, mesh::Packet *ack = createAck(ack_hash); if (ack) { - if (client->out_path_len == OUT_PATH_UNKNOWN) { - sendFloodReply(ack, TXT_ACK_DELAY, packet->getPathHashSize()); - } else { + if (mesh::Packet::isValidPathLen(client->out_path_len)) { sendDirect(ack, client->out_path, client->out_path_len, TXT_ACK_DELAY); + } else { + sendFloodReply(ack, TXT_ACK_DELAY, packet->getPathHashSize()); } } } @@ -723,7 +723,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, if (is_retry) { *reply = 0; } else { - handleCommand(sender_timestamp, command, reply); + handleCommand(sender_timestamp, client, command, reply); } int text_len = strlen(reply); if (text_len > 0) { @@ -737,10 +737,10 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len); if (reply) { - if (client->out_path_len == OUT_PATH_UNKNOWN) { - sendFloodReply(reply, CLI_REPLY_DELAY_MILLIS, packet->getPathHashSize()); - } else { + if (mesh::Packet::isValidPathLen(client->out_path_len)) { sendDirect(reply, client->out_path, client->out_path_len, CLI_REPLY_DELAY_MILLIS); + } else { + sendFloodReply(reply, CLI_REPLY_DELAY_MILLIS, packet->getPathHashSize()); } } } @@ -760,7 +760,9 @@ bool MyMesh::onPeerPathRecv(mesh::Packet *packet, int sender_idx, const uint8_t auto client = acl.getClientByIdx(i); // store a copy of path, for sendDirect() - client->out_path_len = mesh::Packet::copyPath(client->out_path, path, path_len); + if (client->out_path_len != OUT_PATH_FORCE_FLOOD) { + client->out_path_len = mesh::Packet::copyPath(client->out_path, path, path_len); + } client->last_activity = getRTCClock()->getCurrentTime(); } else { MESH_DEBUG_PRINTLN("onPeerPathRecv: invalid peer idx: %d", i); @@ -917,6 +919,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.rx_boosted_gain = 1; // enabled by default; #endif #endif + _prefs.radio_fem_rxgain = 1; pending_discover_tag = 0; pending_discover_until = 0; @@ -965,6 +968,7 @@ void MyMesh::begin(FILESYSTEM *fs) { radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s", radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled"); + board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); updateAdvertTimer(); updateFloodAdvertTimer(); @@ -1171,7 +1175,108 @@ void MyMesh::clearStats() { ((SimpleMeshTables *)getTables())->resetStats(); } -void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply) { +static char* trimSpaces(char* s) { + while (*s == ' ') s++; + char* end = s + strlen(s); + while (end > s && end[-1] == ' ') end--; + *end = 0; + return s; +} + +static bool parsePathCommand(char* raw, uint8_t* out_path, uint8_t& out_path_len, const char*& err) { + if (raw == NULL || out_path == NULL) { + err = "Err - bad params"; + return false; + } + + char* spec = trimSpaces(raw); + if (*spec == 0) { + err = "Err - missing path"; + return false; + } + if (strcmp(spec, "clear") == 0 || strcmp(spec, "-") == 0 || strcmp(spec, "none") == 0) { + out_path_len = OUT_PATH_UNKNOWN; + return true; + } + if (strcmp(spec, "flood") == 0) { + out_path_len = OUT_PATH_FORCE_FLOOD; + return true; + } + if (strcmp(spec, "direct") == 0) { + out_path_len = 0; + return true; + } + + uint8_t hash_size = 0; + uint8_t hop_count = 0; + char* token = spec; + while (token && *token) { + char* comma = strchr(token, ','); + if (comma) *comma = 0; + token = trimSpaces(token); + + int hex_len = strlen(token); + if (!(hex_len == 2 || hex_len == 4 || hex_len == 6)) { + err = "Err - bad params"; + return false; + } + + uint8_t hop_hash_size = (uint8_t)(hex_len / 2); + if (hash_size == 0) { + hash_size = hop_hash_size; + } else if (hash_size != hop_hash_size) { + err = "Err - bad params"; + return false; + } + + if (hop_count >= 63 || (hop_count + 1) * hash_size > MAX_PATH_SIZE) { + err = "Err - bad params"; + return false; + } + if (!mesh::Utils::fromHex(&out_path[hop_count * hash_size], hash_size, token)) { + err = "Err - bad hex"; + return false; + } + + hop_count++; + token = comma ? comma + 1 : NULL; + } + + if (hash_size == 0 || hop_count == 0) { + err = "Err - missing path"; + return false; + } + out_path_len = ((hash_size - 1) << 6) | (hop_count & 63); + return true; +} + +static void formatPathReply(const uint8_t* path, uint8_t path_len, char* out, size_t out_len) { + if (path_len == OUT_PATH_FORCE_FLOOD) { + snprintf(out, out_len, "> flood"); + return; + } + if (path_len == OUT_PATH_UNKNOWN) { + snprintf(out, out_len, "> unknown"); + return; + } + if (!mesh::Packet::isValidPathLen(path_len)) { + snprintf(out, out_len, "> invalid"); + return; + } + if ((path_len & 63) == 0) { + snprintf(out, out_len, "> direct"); + return; + } + + uint8_t hash_size = (path_len >> 6) + 1; + uint8_t hop_count = path_len & 63; + uint8_t byte_len = hop_count * hash_size; + char hex[(MAX_PATH_SIZE * 2) + 1]; + mesh::Utils::toHex(hex, path, byte_len); + snprintf(out, out_len, "> hs=%u hops=%u hex=%s", (uint32_t)hash_size, (uint32_t)hop_count, hex); +} + +void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *command, char *reply) { if (region_load_active) { if (StrHelper::isBlank(command)) { // empty/blank line, signal to terminate 'load' operation region_map = temp_map; // copy over the temp instance as new current map @@ -1248,6 +1353,34 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply Serial.printf("\n"); } reply[0] = 0; + } else if (strcmp(command, "get outpath") == 0 + || strcmp(command, "set outpath") == 0 + || strncmp(command, "set outpath ", 12) == 0) { + bool is_get = strncmp(command, "get ", 4) == 0; + if (sender == NULL) { + strcpy(reply, "Err - command needs remote client context"); + } else if (is_get) { + formatPathReply(sender->out_path, sender->out_path_len, reply, 160); + } else { + char* spec = command + 11; // length of "set outpath" + if (*spec == ' ') spec++; + + uint8_t path[MAX_PATH_SIZE]; + uint8_t path_len = OUT_PATH_UNKNOWN; + const char* err = NULL; + if (!parsePathCommand(spec, path, path_len, err)) { + strcpy(reply, err ? err : "Err - invalid path"); + } else { + if (path_len == OUT_PATH_UNKNOWN || path_len == OUT_PATH_FORCE_FLOOD) { + memset(sender->out_path, 0, sizeof(sender->out_path)); + sender->out_path_len = path_len; + } else { + sender->out_path_len = mesh::Packet::copyPath(sender->out_path, path, path_len); + } + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); + formatPathReply(sender->out_path, sender->out_path_len, reply, 160); + } + } } else if (memcmp(command, "discover.neighbors", 18) == 0) { const char* sub = command + 18; while (*sub == ' ') sub++; diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 7597c6c6..fbc756f4 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -223,7 +223,7 @@ public: void saveIdentity(const mesh::LocalIdentity& new_id) override; void clearStats() override; - void handleCommand(uint32_t sender_timestamp, char* command, char* reply); + void handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char* command, char* reply); void loop(); #if defined(WITH_BRIDGE) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 2ce056f5..82e2a212 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -122,7 +122,7 @@ void loop() { Serial.print('\n'); command[len - 1] = 0; // replace newline with C string null terminator char reply[160]; - the_mesh.handleCommand(0, command, reply); // NOTE: there is no sender_timestamp via serial! + the_mesh.handleCommand(0, NULL, command, reply); // NOTE: there is no sender_timestamp via serial! if (reply[0]) { Serial.print(" -> "); Serial.println(reply); } @@ -161,4 +161,9 @@ void loop() { } #endif } + + if (the_mesh.getNodePrefs()->reboot_interval > 0 && + the_mesh.millisHasNowPassed(the_mesh.getNodePrefs()->reboot_interval * 3600000)) { + board.reboot(); + } } diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 98b22fdb..bbea97f5 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -658,6 +658,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.gps_enabled = 0; _prefs.gps_interval = 0; _prefs.advert_loc_policy = ADVERT_LOC_PREFS; + _prefs.radio_fem_rxgain = 1; next_post_idx = 0; next_client_idx = 0; @@ -699,6 +700,7 @@ void MyMesh::begin(FILESYSTEM *fs) { radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); radio_driver.setTxPower(_prefs.tx_power_dbm); + board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); updateAdvertTimer(); updateFloodAdvertTimer(); @@ -1028,3 +1030,11 @@ void MyMesh::loop() { uptime_millis += now - last_millis; last_millis = now; } + +// To check if there is pending work +bool MyMesh::hasPendingWork() const { +#if defined(WITH_BRIDGE) + if (bridge.isRunning()) return true; // bridge needs WiFi radio, can't sleep +#endif + return _mgr->getOutboundTotal() > 0; +} diff --git a/examples/simple_room_server/MyMesh.h b/examples/simple_room_server/MyMesh.h index 5277ddad..24c26418 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -222,4 +222,7 @@ public: void clearStats() override; void handleCommand(uint32_t sender_timestamp, char* command, char* reply); void loop(); + + // To check if there is pending work + bool hasPendingWork() const; }; diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index a3798b21..ad8aa914 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -18,6 +18,9 @@ void halt() { static char command[MAX_POST_TEXT_LEN+1]; +// For power saving +unsigned long POWERSAVING_FIRSTSLEEP_SECS = 120; // The first sleep (if enabled) from boot + void setup() { Serial.begin(115200); delay(1000); @@ -115,4 +118,14 @@ void loop() { ui_task.loop(); #endif rtc_clock.tick(); + + if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) { +#if defined(NRF52_PLATFORM) + board.sleep(0); // nrf ignores seconds param, sleeps whenever possible +#else + if (the_mesh.millisHasNowPassed(POWERSAVING_FIRSTSLEEP_SECS * 1000)) { // To check if it is time to sleep + board.sleep(30); // Sleep. Wake up after a while or when receiving a LoRa packet + } +#endif + } } diff --git a/src/MeshCore.h b/src/MeshCore.h index cfa33cf9..89e60b1f 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -64,6 +64,9 @@ public: virtual uint8_t getStartupReason() const = 0; virtual bool getBootloaderVersion(char* version, size_t max_len) { return false; } virtual bool startOTAUpdate(const char* id, char reply[]) { return false; } // not supported + virtual bool setLoRaFemLnaEnabled(bool enable) { return false; } + virtual bool canControlLoRaFemLna() const { return false; } + virtual bool isLoRaFemLnaEnabled() const { return false; } // Power management interface (boards with power management override these) virtual bool isExternalPowered() { return false; } diff --git a/src/helpers/ArduinoHelpers.cpp b/src/helpers/ArduinoHelpers.cpp new file mode 100644 index 00000000..feb77a79 --- /dev/null +++ b/src/helpers/ArduinoHelpers.cpp @@ -0,0 +1,6 @@ +#include + +extern "C" { + __attribute__((section(".persistent_magic"))) uint32_t persistent_magic; + __attribute__((section(".persistent_data"))) uint32_t persistent_time; +} \ No newline at end of file diff --git a/src/helpers/ArduinoHelpers.h b/src/helpers/ArduinoHelpers.h index 97596daa..9b50b98c 100644 --- a/src/helpers/ArduinoHelpers.h +++ b/src/helpers/ArduinoHelpers.h @@ -3,19 +3,57 @@ #include #include +#ifdef NRF52_PLATFORM +#define CLOCK_MAGIC_NUM 0xAA55CC33 +#define RTC_TIME_MIN 1772323200 // 1 Mar 2026 + +extern uint32_t persistent_magic; +extern uint32_t persistent_time; +#endif + class VolatileRTCClock : public mesh::RTCClock { uint32_t base_time; uint64_t accumulator; unsigned long prev_millis; + public: - VolatileRTCClock() { base_time = 1715770351; accumulator = 0; prev_millis = millis(); } // 15 May 2024, 8:50pm + VolatileRTCClock() { +#ifdef NRF52_PLATFORM + if (persistent_magic == CLOCK_MAGIC_NUM && persistent_time >= RTC_TIME_MIN) { + base_time = persistent_time; + } else { + base_time = RTC_TIME_MIN; + } +#else + base_time = 1715770351; +#endif + + accumulator = 0; + prev_millis = millis(); + } + uint32_t getCurrentTime() override { return base_time + accumulator/1000; } - void setCurrentTime(uint32_t time) override { base_time = time; accumulator = 0; prev_millis = millis(); } + + void setCurrentTime(uint32_t time) override { + base_time = time; + accumulator = 0; + prev_millis = millis(); + +#ifdef NRF52_PLATFORM + persistent_magic = CLOCK_MAGIC_NUM; + persistent_time = time; +#endif + } void tick() override { unsigned long now = millis(); accumulator += (now - prev_millis); prev_millis = now; + +#ifdef NRF52_PLATFORM + persistent_magic = CLOCK_MAGIC_NUM; + persistent_time = getCurrentTime(); +#endif } }; diff --git a/src/helpers/ArduinoSerialInterface.cpp b/src/helpers/ArduinoSerialInterface.cpp index a01fa586..6b443974 100644 --- a/src/helpers/ArduinoSerialInterface.cpp +++ b/src/helpers/ArduinoSerialInterface.cpp @@ -17,6 +17,10 @@ bool ArduinoSerialInterface::isConnected() const { return true; // no way of knowing, so assume yes } +bool ArduinoSerialInterface::isReadBusy() const { + return false; +} + bool ArduinoSerialInterface::isWriteBusy() const { return false; } diff --git a/src/helpers/ArduinoSerialInterface.h b/src/helpers/ArduinoSerialInterface.h index c4086353..3eed7671 100644 --- a/src/helpers/ArduinoSerialInterface.h +++ b/src/helpers/ArduinoSerialInterface.h @@ -28,6 +28,7 @@ public: bool isConnected() const override; + bool isReadBusy() const override; bool isWriteBusy() const override; size_t writeFrame(const uint8_t src[], size_t len) override; size_t checkRecvFrame(uint8_t dest[]) override; diff --git a/src/helpers/BaseSerialInterface.h b/src/helpers/BaseSerialInterface.h index e9a3f2ab..8ff110eb 100644 --- a/src/helpers/BaseSerialInterface.h +++ b/src/helpers/BaseSerialInterface.h @@ -15,6 +15,7 @@ public: virtual bool isConnected() const = 0; + virtual bool isReadBusy() const = 0; virtual bool isWriteBusy() const = 0; virtual size_t writeFrame(const uint8_t src[], size_t len) = 0; virtual size_t checkRecvFrame(uint8_t dest[]) = 0; diff --git a/src/helpers/ClientACL.h b/src/helpers/ClientACL.h index b758f706..e0b8c542 100644 --- a/src/helpers/ClientACL.h +++ b/src/helpers/ClientACL.h @@ -10,7 +10,8 @@ #define PERM_ACL_READ_WRITE 2 #define PERM_ACL_ADMIN 3 -#define OUT_PATH_UNKNOWN 0xFF +#define OUT_PATH_FORCE_FLOOD 0xFE +#define OUT_PATH_UNKNOWN 0xFF struct ClientInfo { mesh::Identity id; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index b78ad6eb..115865ec 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -4,6 +4,8 @@ #include "AdvertDataHelpers.h" #include "TxtDataHelpers.h" #include +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) #ifndef BRIDGE_MAX_BAUD #define BRIDGE_MAX_BAUD 115200 @@ -81,7 +83,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->bridge_channel, sizeof(_prefs->bridge_channel)); // 135 file.read((uint8_t *)&_prefs->bridge_secret, sizeof(_prefs->bridge_secret)); // 136 file.read((uint8_t *)&_prefs->powersaving_enabled, sizeof(_prefs->powersaving_enabled)); // 152 - file.read(pad, 3); // 153 + file.read((uint8_t *)&_prefs->reboot_interval, sizeof(_prefs->reboot_interval)); // 153 + file.read(pad, 2); // 154 file.read((uint8_t *)&_prefs->gps_enabled, sizeof(_prefs->gps_enabled)); // 156 file.read((uint8_t *)&_prefs->gps_interval, sizeof(_prefs->gps_interval)); // 157 file.read((uint8_t *)&_prefs->advert_loc_policy, sizeof (_prefs->advert_loc_policy)); // 161 @@ -91,7 +94,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290 file.read((uint8_t *)&_prefs->flood_max_unscoped, sizeof(_prefs->flood_max_unscoped)); // 291 file.read((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292 - // next: 293 + file.read((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 293 + // next: 294 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -115,12 +119,14 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->bridge_channel = constrain(_prefs->bridge_channel, 0, 14); _prefs->powersaving_enabled = constrain(_prefs->powersaving_enabled, 0, 1); + _prefs->reboot_interval = constrain(_prefs->reboot_interval, 0, 255); _prefs->gps_enabled = constrain(_prefs->gps_enabled, 0, 1); _prefs->advert_loc_policy = constrain(_prefs->advert_loc_policy, 0, 2); // sanitise settings _prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean + _prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean file.close(); } @@ -174,7 +180,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->bridge_channel, sizeof(_prefs->bridge_channel)); // 135 file.write((uint8_t *)&_prefs->bridge_secret, sizeof(_prefs->bridge_secret)); // 136 file.write((uint8_t *)&_prefs->powersaving_enabled, sizeof(_prefs->powersaving_enabled)); // 152 - file.write(pad, 3); // 153 + file.write((uint8_t *)&_prefs->reboot_interval, sizeof(_prefs->reboot_interval)); // 153 + file.write(pad, 2); // 154 file.write((uint8_t *)&_prefs->gps_enabled, sizeof(_prefs->gps_enabled)); // 156 file.write((uint8_t *)&_prefs->gps_interval, sizeof(_prefs->gps_interval)); // 157 file.write((uint8_t *)&_prefs->advert_loc_policy, sizeof(_prefs->advert_loc_policy)); // 161 @@ -185,6 +192,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->flood_max_unscoped, sizeof(_prefs->flood_max_unscoped)); // 291 file.write((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292 // next: 293 + file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 294 + // next: 295 file.close(); } @@ -455,6 +464,36 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else { strcpy(reply, "off"); } + } else if (memcmp(command, "sensor", 6) == 0) { + // I2C +#if defined(ENV_PIN_SDA) && defined(ENV_PIN_SCL) + sprintf(reply, "I2C Wire1: SDA=%s,SCL=%s\r\n", STR(ENV_PIN_SDA), STR(ENV_PIN_SCL)); +#elif defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL) + sprintf(reply, "I2C Wire: SDA=%s, SCL=%s\r\n", STR(PIN_BOARD_SDA), STR(PIN_BOARD_SCL)); +#elif defined(PIN_WIRE_SDA) && defined(PIN_WIRE_SCL) + sprintf(reply, "I2C Wire: SDA=%s, SCL=%s\r\n", STR(PIN_WIRE_SDA), STR(PIN_WIRE_SCL)); +#else + sprintf(reply, "I2C GPIOs not defined\r\n"); +#endif + + // GPS +#if defined(PIN_GPS_RX) && defined(PIN_GPS_TX) + sprintf(reply + strlen(reply), "GPS Serial: RX=%s, TX=%s", STR(PIN_GPS_RX), STR(PIN_GPS_TX)); +#ifdef ENV_INCLUDE_GPS> 0 + sprintf(reply + strlen(reply), ". Configured"); +#else + sprintf(reply + strlen(reply), ". Not configured"); +#endif +#else + sprintf(reply + strlen(reply), "GPS Serial not defined"); +#endif + } else if (memcmp(command, "powerlog", 8) == 0) { + sprintf(reply, "Last reset reason: %s", _board->getResetReasonString(_board->getResetReason())); +#if defined(NRF52_PLATFORM) + sprintf(reply + strlen(reply), "\r\nLast shutdown reason: %s", + _board->getShutdownReasonString(_board->getShutdownReason())); + sprintf(reply + strlen(reply), "\r\nLast boot voltage: %u mV", _board->getBootVoltage()); +#endif } else if (memcmp(command, "log start", 9) == 0) { _callbacks->setLoggingOn(true); strcpy(reply, " logging on"); @@ -568,6 +607,28 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep savePrefs(); _callbacks->setRxBoostedGain(_prefs->rx_boosted_gain); #endif + } else if (memcmp(config, "radio.fem.rxgain ", 17) == 0) { + if (!_board->canControlLoRaFemLna()) { + strcpy(reply, "Error: unsupported"); + } else if (memcmp(&config[17], "on", 2) == 0) { + if (_board->setLoRaFemLnaEnabled(true)) { + _prefs->radio_fem_rxgain = 1; + savePrefs(); + strcpy(reply, "OK - LoRa FEM RX gain on"); + } else { + strcpy(reply, "Error: failed to apply LoRa FEM RX gain"); + } + } else if (memcmp(&config[17], "off", 3) == 0) { + if (_board->setLoRaFemLnaEnabled(false)) { + _prefs->radio_fem_rxgain = 0; + savePrefs(); + strcpy(reply, "OK - LoRa FEM RX gain off"); + } else { + strcpy(reply, "Error: failed to apply LoRa FEM RX gain"); + } + } else { + strcpy(reply, "Error: state must be on or off"); + } } else if (memcmp(config, "radio ", 6) == 0) { strcpy(tmp, &config[6]); const char *parts[4]; @@ -759,6 +820,19 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep _prefs->adc_multiplier = 0.0f; strcpy(reply, "Error: unsupported by this board"); }; + } else if (memcmp(config, "reboot.interval ", 16) == 0) { + int hours = _atoi(&config[16]); + if (hours == 0) { + _prefs->reboot_interval = 0; + savePrefs(); + strcpy(reply, "reboot.interval disabled"); + } else if (hours < 1 || 255 < hours) { + strcpy(reply, "Error: interval range is 1-255 hours"); + } else { + _prefs->reboot_interval = hours; + savePrefs(); + sprintf(reply, "OK - reboot.interval set to %d", _prefs->reboot_interval); + } } else { strcpy(reply, "unknown config: "); StrHelper::strncpy(&reply[16], config, 160-17); @@ -805,6 +879,12 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep } else if (memcmp(config, "radio.rxgain", 12) == 0) { sprintf(reply, "> %s", _prefs->rx_boosted_gain ? "on" : "off"); #endif + } else if (memcmp(config, "radio.fem.rxgain", 16) == 0) { + if (!_board->canControlLoRaFemLna()) { + strcpy(reply, "Error: unsupported"); + } else { + sprintf(reply, "> %s", _board->isLoRaFemLnaEnabled() ? "on" : "off"); + } } else if (memcmp(config, "radio", 5) == 0) { char freq[16], bw[16]; strcpy(freq, StrHelper::ftoa(_prefs->freq)); @@ -926,6 +1006,12 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep #else strcpy(reply, "ERROR: Power management not supported"); #endif + } else if (memcmp(config, "reboot.interval", 15) == 0) { + if (_prefs->reboot_interval == 0) { + strcpy(reply, "disabled"); + } else { + sprintf(reply, "> %d", (uint8_t)_prefs->reboot_interval); + } } else { sprintf(reply, "??: %s", config); } diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index b509c2b3..095b6407 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -53,6 +53,7 @@ struct NodePrefs { // persisted to file char bridge_secret[16]; // for XOR encryption of bridge packets (ESP-NOW only) // Power setting uint8_t powersaving_enabled; // boolean + uint8_t reboot_interval; // hours, 0-255 (default 0=disable) // Gps settings uint8_t gps_enabled; uint32_t gps_interval; // in seconds @@ -61,6 +62,7 @@ struct NodePrefs { // persisted to file float adc_multiplier; char owner_info[120]; uint8_t rx_boosted_gain; // power settings + uint8_t radio_fem_rxgain; // LoRa FEM RX gain setting uint8_t path_hash_mode; // which path mode to use when sending uint8_t loop_detect; }; diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index a4cbf2a9..058c800a 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -14,6 +14,7 @@ #include #include "soc/rtc.h" #include "esp_system.h" +#include class ESP32Board : public mesh::MainBoard { protected: @@ -62,6 +63,31 @@ public: return raw / 4; } + void powerOff() override { + enterDeepSleep(0); // Do not wakeup + } + + void enterDeepSleep(uint32_t secs) { + // Clear stale wakeup sources to avoid ghost wakeup + // This is required when Power Management and automatic lightsleep are enabled + esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); + + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000ULL); + } + + // Keep LoRa inactive during deepsleep + digitalWrite(P_LORA_NSS, HIGH); +#if CONFIG_IDF_TARGET_ESP32C3 + gpio_hold_en((gpio_num_t)P_LORA_NSS); +#else + rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); +#endif + + // Finally set ESP32 into deepsleep + esp_deep_sleep_start(); // CPU halts here and never returns! + } + uint32_t getIRQGpio() override { return P_LORA_DIO_1; // default for SX1262 } @@ -155,21 +181,68 @@ public: void setInhibitSleep(bool inhibit) { inhibit_sleep = inhibit; } + + uint32_t getResetReason() const override { + return esp_reset_reason(); + } + + // https://docs.espressif.com/projects/esp-idf/en/v4.4.7/esp32/api-reference/system/system.html + const char *getResetReasonString(uint32_t reason) { + switch (reason) { + case ESP_RST_UNKNOWN: + return "Unknown or first boot"; + case ESP_RST_POWERON: + return "Power-on reset"; + case ESP_RST_EXT: + return "External reset"; + case ESP_RST_SW: + return "Software reset"; + case ESP_RST_PANIC: + return "Panic / exception reset"; + case ESP_RST_INT_WDT: + return "Interrupt watchdog reset"; + case ESP_RST_TASK_WDT: + return "Task watchdog reset"; + case ESP_RST_WDT: + return "Other watchdog reset"; + case ESP_RST_DEEPSLEEP: + return "Wake from deep sleep"; + case ESP_RST_BROWNOUT: + return "Brownout (low voltage)"; + case ESP_RST_SDIO: + return "SDIO reset"; + default: + static char buf[40]; + snprintf(buf, sizeof(buf), "Unknown reset reason (%d)", reason); + return buf; + } + } }; +static RTC_NOINIT_ATTR uint32_t _rtc_backup_time; +static RTC_NOINIT_ATTR uint32_t _rtc_backup_magic; +#define RTC_BACKUP_MAGIC 0xAA55CC33 +#define RTC_TIME_MIN 1772323200 // 1 Mar 2026 + class ESP32RTCClock : public mesh::RTCClock { public: ESP32RTCClock() { } void begin() { esp_reset_reason_t reason = esp_reset_reason(); - if (reason == ESP_RST_POWERON) { - // start with some date/time in the recent past - struct timeval tv; - tv.tv_sec = 1715770351; // 15 May 2024, 8:50pm + if (reason == ESP_RST_DEEPSLEEP) { + return; // ESP-IDF preserves system time across deep sleep + } + // All other resets (power-on, crash, WDT, brownout) lose system time. + // Restore from RTC backup if valid, otherwise use hardcoded seed. + struct timeval tv; + if (_rtc_backup_magic == RTC_BACKUP_MAGIC && _rtc_backup_time > RTC_TIME_MIN) { + tv.tv_sec = _rtc_backup_time; + } else { + tv.tv_sec = RTC_TIME_MIN; + } tv.tv_usec = 0; settimeofday(&tv, NULL); } - } uint32_t getCurrentTime() override { time_t _now; time(&_now); @@ -180,6 +253,16 @@ public: tv.tv_sec = time; tv.tv_usec = 0; settimeofday(&tv, NULL); + _rtc_backup_time = time; + _rtc_backup_magic = RTC_BACKUP_MAGIC; + } + void tick() override { + time_t now; + time(&now); + if (now > RTC_TIME_MIN && (uint32_t)now != _rtc_backup_time) { + _rtc_backup_time = (uint32_t)now; + _rtc_backup_magic = RTC_BACKUP_MAGIC; + } } }; diff --git a/src/helpers/MeshadventurerBoard.h b/src/helpers/MeshadventurerBoard.h index 65e11102..0325161d 100644 --- a/src/helpers/MeshadventurerBoard.h +++ b/src/helpers/MeshadventurerBoard.h @@ -15,8 +15,6 @@ #include "ESP32Board.h" -#include - class MeshadventurerBoard : public ESP32Board { public: @@ -35,34 +33,6 @@ public: } } - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are held on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - - void powerOff() override { - // TODO: re-enable this when there is a definite wake-up source pin: - // enterDeepSleep(0); - } - uint16_t getBattMilliVolts() override { analogReadResolution(12); diff --git a/src/helpers/esp32/SerialBLEInterface.cpp b/src/helpers/esp32/SerialBLEInterface.cpp index dcfa0e1e..50e1501e 100644 --- a/src/helpers/esp32/SerialBLEInterface.cpp +++ b/src/helpers/esp32/SerialBLEInterface.cpp @@ -182,6 +182,10 @@ size_t SerialBLEInterface::writeFrame(const uint8_t src[], size_t len) { #define BLE_WRITE_MIN_INTERVAL 60 +bool SerialBLEInterface::isReadBusy() const { + return (recv_queue_len > 0); +} + bool SerialBLEInterface::isWriteBusy() const { return millis() < _last_write + BLE_WRITE_MIN_INTERVAL; // still too soon to start another write? } diff --git a/src/helpers/esp32/SerialBLEInterface.h b/src/helpers/esp32/SerialBLEInterface.h index 965e90fd..19e024b0 100644 --- a/src/helpers/esp32/SerialBLEInterface.h +++ b/src/helpers/esp32/SerialBLEInterface.h @@ -76,6 +76,7 @@ public: bool isConnected() const override; + bool isReadBusy() const override; bool isWriteBusy() const override; size_t writeFrame(const uint8_t src[], size_t len) override; size_t checkRecvFrame(uint8_t dest[]) override; diff --git a/src/helpers/esp32/SerialWifiInterface.cpp b/src/helpers/esp32/SerialWifiInterface.cpp index 462e3ecc..bdecb1a9 100644 --- a/src/helpers/esp32/SerialWifiInterface.cpp +++ b/src/helpers/esp32/SerialWifiInterface.cpp @@ -39,6 +39,10 @@ size_t SerialWifiInterface::writeFrame(const uint8_t src[], size_t len) { return 0; } +bool SerialWifiInterface::isReadBusy() const { + return false; +} + bool SerialWifiInterface::isWriteBusy() const { return false; } diff --git a/src/helpers/esp32/SerialWifiInterface.h b/src/helpers/esp32/SerialWifiInterface.h index 19291497..1ff1d83d 100644 --- a/src/helpers/esp32/SerialWifiInterface.h +++ b/src/helpers/esp32/SerialWifiInterface.h @@ -52,6 +52,7 @@ public: bool isEnabled() const override { return _isEnabled; } bool isConnected() const override; + bool isReadBusy() const override; bool isWriteBusy() const override; size_t writeFrame(const uint8_t src[], size_t len) override; diff --git a/src/helpers/esp32/TBeamBoard.h b/src/helpers/esp32/TBeamBoard.h index 98bd16bf..543226d6 100644 --- a/src/helpers/esp32/TBeamBoard.h +++ b/src/helpers/esp32/TBeamBoard.h @@ -86,7 +86,6 @@ #include #include "XPowersLib.h" #include "helpers/ESP32Board.h" -#include class TBeamBoard : public ESP32Board { XPowersLibInterface *PMU = NULL; @@ -131,29 +130,6 @@ public: } #endif - void enterDeepSleep(uint32_t secs, int pin_wake_btn) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! -} - uint16_t getBattMilliVolts(){ return PMU->getBattVoltage(); } diff --git a/src/helpers/nrf52/SerialBLEInterface.cpp b/src/helpers/nrf52/SerialBLEInterface.cpp index 75a4e3b0..a846e744 100644 --- a/src/helpers/nrf52/SerialBLEInterface.cpp +++ b/src/helpers/nrf52/SerialBLEInterface.cpp @@ -401,6 +401,10 @@ bool SerialBLEInterface::isConnected() const { return _isDeviceConnected && Bluefruit.connected() > 0; } +bool SerialBLEInterface::isReadBusy() const { + return (recv_queue_len > 0); +} + bool SerialBLEInterface::isWriteBusy() const { return send_queue_len >= (FRAME_QUEUE_SIZE * 2 / 3); } diff --git a/src/helpers/nrf52/SerialBLEInterface.h b/src/helpers/nrf52/SerialBLEInterface.h index de103054..d3cc5055 100644 --- a/src/helpers/nrf52/SerialBLEInterface.h +++ b/src/helpers/nrf52/SerialBLEInterface.h @@ -66,6 +66,7 @@ public: void disable() override; bool isEnabled() const override { return _isEnabled; } bool isConnected() const override; + bool isReadBusy() const override; bool isWriteBusy() const override; size_t writeFrame(const uint8_t src[], size_t len) override; size_t checkRecvFrame(uint8_t dest[]) override; diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 73842d9e..637d61af 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -40,6 +40,7 @@ static uint32_t bsec_last_save_ms = 0; #ifdef ENV_INCLUDE_BME680 #ifndef TELEM_BME680_ADDRESS #define TELEM_BME680_ADDRESS 0x76 +#define TELEM_BME680_ADDRESS_2 0x77 #endif #define TELEM_BME680_SEALEVELPRESSURE_HPA (1013.25) #include @@ -63,6 +64,7 @@ static Adafruit_AHTX0 AHTX0; #if ENV_INCLUDE_BME280 #ifndef TELEM_BME280_ADDRESS #define TELEM_BME280_ADDRESS 0x76 // BME280 environmental sensor I2C address +#define TELEM_BME280_ADDRESS_2 0x77 #endif #define TELEM_BME280_SEALEVELPRESSURE_HPA (1013.25) // Atmospheric pressure at sea level #include @@ -72,6 +74,7 @@ static Adafruit_BME280 BME280; #if ENV_INCLUDE_BMP280 #ifndef TELEM_BMP280_ADDRESS #define TELEM_BMP280_ADDRESS 0x76 // BMP280 environmental sensor I2C address +#define TELEM_BMP280_ADDRESS_2 0x77 #endif #define TELEM_BMP280_SEALEVELPRESSURE_HPA (1013.25) // Atmospheric pressure at sea level #include @@ -557,15 +560,27 @@ static const SensorDef SENSOR_TABLE[] = { #endif #ifdef ENV_INCLUDE_BME680 { TELEM_BME680_ADDRESS, "BME680", init_bme680, query_bme680 }, + #ifdef TELEM_BME680_ADDRESS_2 + { TELEM_BME680_ADDRESS_2, "BME680", init_bme680, query_bme680 }, + #endif #endif #if ENV_INCLUDE_BME680_BSEC { TELEM_BME680_ADDRESS, "BME680+BSEC", init_bme680_bsec, query_bme680_bsec }, + #ifdef TELEM_BME680_ADDRESS_2 + { TELEM_BME680_ADDRESS_2, "BME680+BSEC", init_bme680_bsec, query_bme680_bsec }, + #endif #endif #if ENV_INCLUDE_BME280 { TELEM_BME280_ADDRESS, "BME280", init_bme280, query_bme280 }, + #ifdef TELEM_BME280_ADDRESS_2 + { TELEM_BME280_ADDRESS_2, "BME280", init_bme280, query_bme280 }, + #endif #endif #if ENV_INCLUDE_BMP280 { TELEM_BMP280_ADDRESS, "BMP280", init_bmp280, query_bmp280 }, + #ifdef TELEM_BMP280_ADDRESS_2 + { TELEM_BMP280_ADDRESS_2, "BMP280", init_bmp280, query_bmp280 }, + #endif #endif #if ENV_INCLUDE_SHTC3 { 0x70, "SHTC3", init_shtc3, query_shtc3 }, diff --git a/src/helpers/sensors/MicroNMEALocationProvider.h b/src/helpers/sensors/MicroNMEALocationProvider.h index eec466d3..8b3c5867 100644 --- a/src/helpers/sensors/MicroNMEALocationProvider.h +++ b/src/helpers/sensors/MicroNMEALocationProvider.h @@ -76,6 +76,7 @@ public : void begin() override { claim(); if (_pin_en != -1) { + pinMode(_pin_en, OUTPUT); digitalWrite(_pin_en, PIN_GPS_EN_ACTIVE); } if (_pin_reset != -1) { @@ -94,6 +95,7 @@ public : void stop() override { if (_pin_en != -1) { digitalWrite(_pin_en, !PIN_GPS_EN_ACTIVE); + pinMode(_pin_en, INPUT); // Reduce 0.3mA leaking } if (_pin_reset != -1) { digitalWrite(_pin_reset, GPS_RESET_FORCE); diff --git a/src/helpers/ui/ST7735Display.cpp b/src/helpers/ui/ST7735Display.cpp index a6087dd8..8983c911 100644 --- a/src/helpers/ui/ST7735Display.cpp +++ b/src/helpers/ui/ST7735Display.cpp @@ -63,6 +63,15 @@ void ST7735Display::turnOff() { #else digitalWrite(PIN_TFT_LEDA_CTL, LOW); #endif + + // Prevent back-powering to save 2.8 mA + pinMode(PIN_TFT_CS, INPUT); + pinMode(PIN_TFT_DC, INPUT); + pinMode(PIN_TFT_SDA, INPUT); + pinMode(PIN_TFT_SCL, INPUT); + pinMode(PIN_TFT_RST, INPUT); + pinMode(PIN_TFT_LEDA_CTL, INPUT); + _isOn = false; if (_peripher_power) _peripher_power->release(); diff --git a/variants/gat562_30s_mesh_kit/platformio.ini b/variants/gat562_30s_mesh_kit/platformio.ini index 2baac256..aa3915a4 100644 --- a/variants/gat562_30s_mesh_kit/platformio.ini +++ b/variants/gat562_30s_mesh_kit/platformio.ini @@ -2,12 +2,13 @@ extends = nrf52_base board = rak4631 board_check = true +board_build.ldscript = boards/nrf52840_s140_v6.ld build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I variants/gat562_30s_mesh_kit -D RAK_4631 -D RAK_BOARD - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D PIN_OLED_RESET=-1 diff --git a/variants/gat562_mesh_evb_pro/platformio.ini b/variants/gat562_mesh_evb_pro/platformio.ini index b3e89417..d7de585a 100644 --- a/variants/gat562_mesh_evb_pro/platformio.ini +++ b/variants/gat562_mesh_evb_pro/platformio.ini @@ -5,7 +5,7 @@ board_check = true build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I variants/gat562_mesh_evb_pro - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D RADIO_CLASS=CustomSX1262 diff --git a/variants/gat562_mesh_tracker_pro/platformio.ini b/variants/gat562_mesh_tracker_pro/platformio.ini index af153b8f..78ec7d01 100644 --- a/variants/gat562_mesh_tracker_pro/platformio.ini +++ b/variants/gat562_mesh_tracker_pro/platformio.ini @@ -2,10 +2,11 @@ extends = nrf52_base board = rak4631 board_check = true +board_build.ldscript = boards/nrf52840_s140_v6.ld build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I variants/gat562_mesh_tracker_pro - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D PIN_OLED_RESET=-1 diff --git a/variants/gat562_mesh_watch13/platformio.ini b/variants/gat562_mesh_watch13/platformio.ini index f3510b74..f457424f 100644 --- a/variants/gat562_mesh_watch13/platformio.ini +++ b/variants/gat562_mesh_watch13/platformio.ini @@ -8,7 +8,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/gat562_mesh_watch13 -D RAK_4631 -D RAK_BOARD - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D PIN_OLED_RESET=-1 diff --git a/variants/heltec_ct62/platformio.ini b/variants/heltec_ct62/platformio.ini index 0179d965..e8becc7e 100644 --- a/variants/heltec_ct62/platformio.ini +++ b/variants/heltec_ct62/platformio.ini @@ -130,6 +130,10 @@ lib_deps = ${esp32_ota.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_ct62_companion_radio_ble_ps] +extends = env:Heltec_ct62_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 + [env:Heltec_ct62_sensor] extends = Heltec_ct62 build_flags = diff --git a/variants/heltec_e213/HeltecE213Board.cpp b/variants/heltec_e213/HeltecE213Board.cpp index af115318..88737c4d 100644 --- a/variants/heltec_e213/HeltecE213Board.cpp +++ b/variants/heltec_e213/HeltecE213Board.cpp @@ -20,33 +20,6 @@ void HeltecE213Board::begin() { } } - void HeltecE213Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - - void HeltecE213Board::powerOff() { - enterDeepSleep(0); - } - uint16_t HeltecE213Board::getBattMilliVolts() { analogReadResolution(10); digitalWrite(PIN_ADC_CTRL, HIGH); diff --git a/variants/heltec_e213/HeltecE213Board.h b/variants/heltec_e213/HeltecE213Board.h index 2192c141..fadc038f 100644 --- a/variants/heltec_e213/HeltecE213Board.h +++ b/variants/heltec_e213/HeltecE213Board.h @@ -3,7 +3,6 @@ #include #include #include -#include class HeltecE213Board : public ESP32Board { @@ -13,8 +12,6 @@ public: HeltecE213Board() : periph_power(PIN_VEXT_EN,PIN_VEXT_EN_ACTIVE) { } void begin(); - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); - void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; }; diff --git a/variants/heltec_e290/HeltecE290Board.cpp b/variants/heltec_e290/HeltecE290Board.cpp index 3994a206..96ec59c9 100644 --- a/variants/heltec_e290/HeltecE290Board.cpp +++ b/variants/heltec_e290/HeltecE290Board.cpp @@ -20,33 +20,6 @@ void HeltecE290Board::begin() { } } - void HeltecE290Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - - void HeltecE290Board::powerOff() { - enterDeepSleep(0); - } - uint16_t HeltecE290Board::getBattMilliVolts() { analogReadResolution(10); digitalWrite(PIN_ADC_CTRL, HIGH); diff --git a/variants/heltec_e290/HeltecE290Board.h b/variants/heltec_e290/HeltecE290Board.h index 645ec348..f287227c 100644 --- a/variants/heltec_e290/HeltecE290Board.h +++ b/variants/heltec_e290/HeltecE290Board.h @@ -3,7 +3,6 @@ #include #include #include -#include class HeltecE290Board : public ESP32Board { @@ -13,8 +12,6 @@ public: HeltecE290Board() : periph_power(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE) { } void begin(); - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); - void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; diff --git a/variants/heltec_t096/LoRaFEMControl.h b/variants/heltec_t096/LoRaFEMControl.h index 2c50b742..a3b5c4ed 100644 --- a/variants/heltec_t096/LoRaFEMControl.h +++ b/variants/heltec_t096/LoRaFEMControl.h @@ -12,10 +12,11 @@ class LoRaFEMControl void setRxModeEnable(void); void setRxModeEnableWhenMCUSleep(void); void setLNAEnable(bool enabled); - bool isLnaCanControl(void) { return lna_can_control; } + bool isLnaCanControl(void) const { return lna_can_control; } void setLnaCanControl(bool can_control) { lna_can_control = can_control; } + bool isLNAEnabled(void) const { return lna_enabled; } private: - bool lna_enabled = false; + bool lna_enabled = true; bool lna_can_control = false; }; diff --git a/variants/heltec_t096/T096Board.cpp b/variants/heltec_t096/T096Board.cpp index 55013157..c7f69722 100644 --- a/variants/heltec_t096/T096Board.cpp +++ b/variants/heltec_t096/T096Board.cpp @@ -123,4 +123,26 @@ void T096Board::powerOff() { const char* T096Board::getManufacturerName() const { return "Heltec T096"; -} \ No newline at end of file +} + +bool T096Board::setLoRaFemLnaEnabled(bool enable) { +#if defined(RADIO_FEM_RXGAIN) && (RADIO_FEM_RXGAIN == 0) + enable = false; +#endif + + if (!loRaFEMControl.isLnaCanControl()) { + return false; + } + + loRaFEMControl.setLNAEnable(enable); + loRaFEMControl.setRxModeEnable(); + return true; +} + +bool T096Board::canControlLoRaFemLna() const { + return loRaFEMControl.isLnaCanControl(); +} + +bool T096Board::isLoRaFemLnaEnabled() const { + return loRaFEMControl.isLNAEnabled(); +} diff --git a/variants/heltec_t096/T096Board.h b/variants/heltec_t096/T096Board.h index d1e3bdfd..15c7e68b 100644 --- a/variants/heltec_t096/T096Board.h +++ b/variants/heltec_t096/T096Board.h @@ -25,4 +25,7 @@ public: uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; void powerOff() override; + bool setLoRaFemLnaEnabled(bool enable) override; + bool canControlLoRaFemLna() const override; + bool isLoRaFemLnaEnabled() const override; }; diff --git a/variants/heltec_t096/platformio.ini b/variants/heltec_t096/platformio.ini index e820bf58..a0e20ef6 100644 --- a/variants/heltec_t096/platformio.ini +++ b/variants/heltec_t096/platformio.ini @@ -9,7 +9,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/heltec_t096 -I src/helpers/ui -D HELTEC_T096 - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D P_LORA_DIO_1=21 -D P_LORA_NSS=5 -D P_LORA_RESET=16 @@ -143,6 +143,12 @@ lib_deps = ${Heltec_t096.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_t096_companion_radio_ble_femoff] +extends = env:Heltec_t096_companion_radio_ble +build_flags = + ${env:Heltec_t096_companion_radio_ble.build_flags} + -D RADIO_FEM_RXGAIN=0 ; undefined (default on), 1=on, 0=off + [env:Heltec_t096_companion_radio_usb] extends = Heltec_t096 board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld diff --git a/variants/heltec_t114/platformio.ini b/variants/heltec_t114/platformio.ini index 135babb1..e808d6c2 100644 --- a/variants/heltec_t114/platformio.ini +++ b/variants/heltec_t114/platformio.ini @@ -12,7 +12,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/heltec_t114 -I src/helpers/ui -D HELTEC_T114 - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D P_LORA_DIO_1=20 -D P_LORA_NSS=24 -D P_LORA_RESET=25 diff --git a/variants/heltec_t190/HeltecT190Board.cpp b/variants/heltec_t190/HeltecT190Board.cpp index 4f35be40..0a16b52b 100644 --- a/variants/heltec_t190/HeltecT190Board.cpp +++ b/variants/heltec_t190/HeltecT190Board.cpp @@ -20,33 +20,6 @@ void HeltecT190Board::begin() { } } - void HeltecT190Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - - void HeltecT190Board::powerOff() { - enterDeepSleep(0); - } - uint16_t HeltecT190Board::getBattMilliVolts() { analogReadResolution(10); digitalWrite(PIN_ADC_CTRL, HIGH); diff --git a/variants/heltec_t190/HeltecT190Board.h b/variants/heltec_t190/HeltecT190Board.h index bc38c1e0..557c070e 100644 --- a/variants/heltec_t190/HeltecT190Board.h +++ b/variants/heltec_t190/HeltecT190Board.h @@ -3,7 +3,6 @@ #include #include #include -#include class HeltecT190Board : public ESP32Board { @@ -13,8 +12,6 @@ public: HeltecT190Board() : periph_power(PIN_VEXT_EN,PIN_VEXT_EN_ACTIVE) { } void begin(); - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); - void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; diff --git a/variants/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini index 69293d70..2cd0cea6 100644 --- a/variants/heltec_tracker/platformio.ini +++ b/variants/heltec_tracker/platformio.ini @@ -99,6 +99,10 @@ lib_deps = ${Heltec_tracker_base.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_Wireless_Tracker_companion_radio_ble_ps] +extends = env:Heltec_Wireless_Tracker_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 + [env:Heltec_Wireless_Tracker_repeater] extends = Heltec_tracker_base build_flags = diff --git a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp index aabfed79..99b1cdfe 100644 --- a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp +++ b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp @@ -35,33 +35,12 @@ void HeltecTrackerV2Board::begin() { loRaFEMControl.setRxModeEnable(); } - void HeltecTrackerV2Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + void HeltecTrackerV2Board::powerOff() { + // Turn off PA + digitalWrite(P_LORA_PA_POWER, LOW); + rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_POWER); - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - loRaFEMControl.setRxModeEnableWhenMCUSleep();//It also needs to be enabled in receive mode - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - - void HeltecTrackerV2Board::powerOff() { - enterDeepSleep(0); + ESP32Board::powerOff(); } uint16_t HeltecTrackerV2Board::getBattMilliVolts() { @@ -82,3 +61,21 @@ void HeltecTrackerV2Board::begin() { const char* HeltecTrackerV2Board::getManufacturerName() const { return "Heltec Tracker V2"; } + + bool HeltecTrackerV2Board::setLoRaFemLnaEnabled(bool enable) { + if (!loRaFEMControl.isLnaCanControl()) { + return false; + } + + loRaFEMControl.setLNAEnable(enable); + loRaFEMControl.setRxModeEnable(); + return true; + } + + bool HeltecTrackerV2Board::canControlLoRaFemLna() const { + return loRaFEMControl.isLnaCanControl(); + } + + bool HeltecTrackerV2Board::isLoRaFemLnaEnabled() const { + return loRaFEMControl.isLNAEnabled(); + } diff --git a/variants/heltec_tracker_v2/HeltecTrackerV2Board.h b/variants/heltec_tracker_v2/HeltecTrackerV2Board.h index 33c897bc..2bd6a025 100644 --- a/variants/heltec_tracker_v2/HeltecTrackerV2Board.h +++ b/variants/heltec_tracker_v2/HeltecTrackerV2Board.h @@ -3,7 +3,6 @@ #include #include #include -#include #include "LoRaFEMControl.h" class HeltecTrackerV2Board : public ESP32Board { @@ -17,9 +16,11 @@ public: void begin(); void onBeforeTransmit(void) override; void onAfterTransmit(void) override; - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; + bool setLoRaFemLnaEnabled(bool enable) override; + bool canControlLoRaFemLna() const override; + bool isLoRaFemLnaEnabled() const override; }; diff --git a/variants/heltec_tracker_v2/LoRaFEMControl.h b/variants/heltec_tracker_v2/LoRaFEMControl.h index 2c50b742..a3b5c4ed 100644 --- a/variants/heltec_tracker_v2/LoRaFEMControl.h +++ b/variants/heltec_tracker_v2/LoRaFEMControl.h @@ -12,10 +12,11 @@ class LoRaFEMControl void setRxModeEnable(void); void setRxModeEnableWhenMCUSleep(void); void setLNAEnable(bool enabled); - bool isLnaCanControl(void) { return lna_can_control; } + bool isLnaCanControl(void) const { return lna_can_control; } void setLnaCanControl(bool can_control) { lna_can_control = can_control; } + bool isLNAEnabled(void) const { return lna_enabled; } private: - bool lna_enabled = false; + bool lna_enabled = true; bool lna_can_control = false; }; diff --git a/variants/heltec_tracker_v2/platformio.ini b/variants/heltec_tracker_v2/platformio.ini index d57c2113..d914ce6a 100644 --- a/variants/heltec_tracker_v2/platformio.ini +++ b/variants/heltec_tracker_v2/platformio.ini @@ -176,6 +176,10 @@ lib_deps = ${Heltec_tracker_v2.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:heltec_tracker_v2_companion_radio_ble_ps] +extends = env:heltec_tracker_v2_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 + [env:heltec_tracker_v2_companion_radio_wifi] extends = Heltec_tracker_v2 build_flags = diff --git a/variants/heltec_v2/HeltecV2Board.h b/variants/heltec_v2/HeltecV2Board.h index fe800890..9b08fe94 100644 --- a/variants/heltec_v2/HeltecV2Board.h +++ b/variants/heltec_v2/HeltecV2Board.h @@ -7,8 +7,6 @@ #define PIN_VBAT_READ 37 #define PIN_LED_BUILTIN 25 -#include - class HeltecV2Board : public ESP32Board { public: void begin() { @@ -26,29 +24,6 @@ public: } } - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_0, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_0); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_0), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_0) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - uint16_t getBattMilliVolts() override { analogReadResolution(10); diff --git a/variants/heltec_v2/platformio.ini b/variants/heltec_v2/platformio.ini index ba4f8694..e9cf56f0 100644 --- a/variants/heltec_v2/platformio.ini +++ b/variants/heltec_v2/platformio.ini @@ -172,6 +172,10 @@ lib_deps = ${Heltec_lora32_v2.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_v2_companion_radio_ble_ps] +extends = env:Heltec_v2_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 + [env:Heltec_v2_companion_radio_wifi] extends = Heltec_lora32_v2 build_flags = diff --git a/variants/heltec_v3/HeltecV3Board.h b/variants/heltec_v3/HeltecV3Board.h index ba22a7f2..7e7abe31 100644 --- a/variants/heltec_v3/HeltecV3Board.h +++ b/variants/heltec_v3/HeltecV3Board.h @@ -17,8 +17,6 @@ #define PIN_ADC_CTRL_ACTIVE LOW #define PIN_ADC_CTRL_INACTIVE HIGH -#include - class HeltecV3Board : public ESP32Board { private: bool adc_active_state; @@ -52,33 +50,6 @@ public: } } - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - - void powerOff() override { - enterDeepSleep(0); - } - uint16_t getBattMilliVolts() override { analogReadResolution(10); digitalWrite(PIN_ADC_CTRL, adc_active_state); diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index a70a93a5..69e4ed05 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -179,6 +179,10 @@ lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_v3_companion_radio_ble_ps] +extends = env:Heltec_v3_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 + [env:Heltec_v3_companion_radio_wifi] extends = Heltec_lora32_v3 build_flags = @@ -320,6 +324,10 @@ lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_WSL3_companion_radio_ble_ps] +extends = env:Heltec_WSL3_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 + [env:Heltec_WSL3_companion_radio_usb] extends = Heltec_lora32_v3 build_flags = diff --git a/variants/heltec_v4/HeltecV4Board.cpp b/variants/heltec_v4/HeltecV4Board.cpp index 8f013797..ba3a7f1c 100644 --- a/variants/heltec_v4/HeltecV4Board.cpp +++ b/variants/heltec_v4/HeltecV4Board.cpp @@ -32,33 +32,12 @@ void HeltecV4Board::begin() { loRaFEMControl.setRxModeEnable(); } - void HeltecV4Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + void HeltecV4Board::powerOff() { + // Turn off PA + digitalWrite(P_LORA_PA_POWER, LOW); + rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_POWER); - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - loRaFEMControl.setRxModeEnableWhenMCUSleep();//It also needs to be enabled in receive mode - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - - void HeltecV4Board::powerOff() { - enterDeepSleep(0); + ESP32Board::powerOff(); } uint16_t HeltecV4Board::getBattMilliVolts() { @@ -83,3 +62,25 @@ void HeltecV4Board::begin() { return loRaFEMControl.getFEMType() == KCT8103L_PA ? "Heltec V4.3 OLED" : "Heltec V4 OLED"; #endif } + + bool HeltecV4Board::setLoRaFemLnaEnabled(bool enable) { +#if defined(RADIO_FEM_RXGAIN) && (RADIO_FEM_RXGAIN == 0) + enable = false; +#endif + + if (!loRaFEMControl.isLnaCanControl()) { + return false; + } + + loRaFEMControl.setLNAEnable(enable); + loRaFEMControl.setRxModeEnable(); + return true; + } + + bool HeltecV4Board::canControlLoRaFemLna() const { + return loRaFEMControl.isLnaCanControl(); + } + + bool HeltecV4Board::isLoRaFemLnaEnabled() const { + return loRaFEMControl.isLNAEnabled(); + } diff --git a/variants/heltec_v4/HeltecV4Board.h b/variants/heltec_v4/HeltecV4Board.h index 95def06c..55166bb3 100644 --- a/variants/heltec_v4/HeltecV4Board.h +++ b/variants/heltec_v4/HeltecV4Board.h @@ -3,7 +3,6 @@ #include #include #include -#include #include "LoRaFEMControl.h" #ifndef ADC_MULTIPLIER @@ -23,8 +22,10 @@ public: void begin(); void onBeforeTransmit(void) override; void onAfterTransmit(void) override; - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); void powerOff() override; + bool setLoRaFemLnaEnabled(bool enable) override; + bool canControlLoRaFemLna() const override; + bool isLoRaFemLnaEnabled() const override; uint16_t getBattMilliVolts() override; bool setAdcMultiplier(float multiplier) override { if (multiplier == 0.0f) { diff --git a/variants/heltec_v4/LoRaFEMControl.h b/variants/heltec_v4/LoRaFEMControl.h index 13225bd5..d84ebe9c 100644 --- a/variants/heltec_v4/LoRaFEMControl.h +++ b/variants/heltec_v4/LoRaFEMControl.h @@ -18,12 +18,13 @@ class LoRaFEMControl void setRxModeEnable(void); void setRxModeEnableWhenMCUSleep(void); void setLNAEnable(bool enabled); - bool isLnaCanControl(void) { return lna_can_control; } + bool isLnaCanControl(void) const { return lna_can_control; } void setLnaCanControl(bool can_control) { lna_can_control = can_control; } + bool isLNAEnabled(void) const { return lna_enabled; } LoRaFEMType getFEMType(void) const { return fem_type; } private: LoRaFEMType fem_type=OTHER_FEM_TYPES; - bool lna_enabled=false; + bool lna_enabled=true; bool lna_can_control=false; }; diff --git a/variants/heltec_v4/platformio.ini b/variants/heltec_v4/platformio.ini index fabf3827..5ad2b1e9 100644 --- a/variants/heltec_v4/platformio.ini +++ b/variants/heltec_v4/platformio.ini @@ -222,6 +222,16 @@ lib_deps = ${heltec_v4_oled.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:heltec_v4_companion_radio_ble_ps] +extends = env:heltec_v4_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 + +[env:heltec_v4_3_companion_radio_ble_ps_femoff] +extends = env:heltec_v4_companion_radio_ble_ps +build_flags = + ${env:heltec_v4_companion_radio_ble_ps.build_flags} + -D RADIO_FEM_RXGAIN=0 ; undefined (default on), 1=on, 0=off + [env:heltec_v4_companion_radio_wifi] extends = heltec_v4_oled build_flags = @@ -386,6 +396,14 @@ lib_deps = ${heltec_v4_tft.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:heltec_v4_expansionkit_tft_companion_radio_ble_ps] +extends = env:heltec_v4_tft_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${env:heltec_v4_tft_companion_radio_ble.build_flags} + -D ENV_PIN_SDA=4 + -D ENV_PIN_SCL=3 + [env:heltec_v4_tft_companion_radio_wifi] extends = heltec_v4_tft build_flags = diff --git a/variants/heltec_wireless_paper/platformio.ini b/variants/heltec_wireless_paper/platformio.ini index 48723d16..e7a107a5 100644 --- a/variants/heltec_wireless_paper/platformio.ini +++ b/variants/heltec_wireless_paper/platformio.ini @@ -64,6 +64,10 @@ lib_deps = densaugeo/base64 @ ~1.4.0 bakercp/CRC32 @ ^2.0.0 +[env:Heltec_Wireless_Paper_companion_radio_ble_ps] +extends = env:Heltec_Wireless_Paper_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 + [env:Heltec_Wireless_Paper_companion_radio_usb] extends = Heltec_Wireless_Paper_base build_flags = diff --git a/variants/lilygo_t3s3/platformio.ini b/variants/lilygo_t3s3/platformio.ini index 54990117..e7f9f11f 100644 --- a/variants/lilygo_t3s3/platformio.ini +++ b/variants/lilygo_t3s3/platformio.ini @@ -174,6 +174,10 @@ lib_deps = ${LilyGo_T3S3_sx1262.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:LilyGo_T3S3_sx1262_companion_radio_ble_ps] +extends = env:LilyGo_T3S3_sx1262_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 + [env:LilyGo_T3S3_sx1262_kiss_modem] extends = LilyGo_T3S3_sx1262 build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} diff --git a/variants/lilygo_tbeam_1w/platformio.ini b/variants/lilygo_tbeam_1w/platformio.ini index c7a59552..4516e4cd 100644 --- a/variants/lilygo_tbeam_1w/platformio.ini +++ b/variants/lilygo_tbeam_1w/platformio.ini @@ -146,6 +146,11 @@ lib_deps = ${LilyGo_TBeam_1W.lib_deps} densaugeo/base64 @ ~1.4.0 +; === LILYGO T-Beam 1W Companion Radio PS (BLE PS) === +[env:LilyGo_TBeam_1W_companion_radio_ble_ps] +extends = env:LilyGo_TBeam_1W_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 + ; === LILYGO T-Beam 1W Companion Radio (WiFi) === [env:LilyGo_TBeam_1W_companion_radio_wifi] extends = LilyGo_TBeam_1W diff --git a/variants/lilygo_tbeam_SX1262/platformio.ini b/variants/lilygo_tbeam_SX1262/platformio.ini index 62ac09f8..5bc0efdd 100644 --- a/variants/lilygo_tbeam_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_SX1262/platformio.ini @@ -65,6 +65,13 @@ lib_deps = ${LilyGo_TBeam_SX1262.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Tbeam_SX1262_companion_radio_ble_ps] +extends = env:Tbeam_SX1262_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +build_flags = + ${env:Tbeam_SX1262_companion_radio_ble.build_flags} + -D MAX_CONTACTS=150 + [env:Tbeam_SX1262_repeater] extends = LilyGo_TBeam_SX1262 build_flags = diff --git a/variants/lilygo_tbeam_SX1276/platformio.ini b/variants/lilygo_tbeam_SX1276/platformio.ini index cb25903c..edf84e21 100644 --- a/variants/lilygo_tbeam_SX1276/platformio.ini +++ b/variants/lilygo_tbeam_SX1276/platformio.ini @@ -61,6 +61,10 @@ lib_deps = ${LilyGo_TBeam_SX1276.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Tbeam_SX1276_companion_radio_ble_ps] +extends = env:Tbeam_SX1276_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 + [env:Tbeam_SX1276_repeater] extends = LilyGo_TBeam_SX1276 build_flags = diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index 02946156..b930a25b 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -142,6 +142,10 @@ lib_deps = ${T_Beam_S3_Supreme_SX1262.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps] +extends = env:T_Beam_S3_Supreme_SX1262_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 + [env:T_Beam_S3_Supreme_SX1262_companion_radio_wifi] extends = T_Beam_S3_Supreme_SX1262 build_flags = diff --git a/variants/lilygo_tdeck/TDeckBoard.h b/variants/lilygo_tdeck/TDeckBoard.h index 7ed007af..e2844360 100644 --- a/variants/lilygo_tdeck/TDeckBoard.h +++ b/variants/lilygo_tdeck/TDeckBoard.h @@ -3,7 +3,6 @@ #include #include #include "helpers/ESP32Board.h" -#include #define PIN_VBAT_READ 4 #define BATTERY_SAMPLES 8 @@ -23,29 +22,6 @@ public: } #endif - void enterDeepSleep(uint32_t secs, int pin_wake_btn) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - uint16_t getBattMilliVolts() { #if defined(PIN_VBAT_READ) && defined(ADC_MULTIPLIER) analogReadResolution(12); diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index 36731668..bb173524 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -92,7 +92,7 @@ extends = LilyGo_TLora_V2_1_1_6 build_flags = ${LilyGo_TLora_V2_1_1_6.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=160 + -D MAX_CONTACTS=100 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 -D OFFLINE_QUEUE_SIZE=128 @@ -108,6 +108,10 @@ lib_deps = ${LilyGo_TLora_V2_1_1_6.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps] +extends = env:LilyGo_TLora_V2_1_1_6_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 + [env:LilyGo_TLora_V2_1_1_6_room_server] extends = LilyGo_TLora_V2_1_1_6 build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} diff --git a/variants/promicro/platformio.ini b/variants/promicro/platformio.ini index 5415e158..1e1a3274 100644 --- a/variants/promicro/platformio.ini +++ b/variants/promicro/platformio.ini @@ -1,6 +1,7 @@ [Promicro] extends = nrf52_base board = promicro_nrf52840 +board_build.ldscript = boards/nrf52840_s140_v6.ld build_flags = ${nrf52_base.build_flags} -I variants/promicro -D PROMICRO diff --git a/variants/rak3112/RAK3112Board.h b/variants/rak3112/RAK3112Board.h index 8ba3197c..704162b8 100644 --- a/variants/rak3112/RAK3112Board.h +++ b/variants/rak3112/RAK3112Board.h @@ -16,8 +16,6 @@ #define ADC_MULTIPLIER (3 * 1.73 * 1.187 * 1000) #define BATTERY_SAMPLES 8 -#include - class RAK3112Board : public ESP32Board { private: bool adc_active_state; @@ -51,33 +49,6 @@ public: } } - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - - void powerOff() override { - enterDeepSleep(0); - } - uint16_t getBattMilliVolts() override { analogReadResolution(12); diff --git a/variants/rak3401/platformio.ini b/variants/rak3401/platformio.ini index 20a8a548..5eeb6a9d 100644 --- a/variants/rak3401/platformio.ini +++ b/variants/rak3401/platformio.ini @@ -2,11 +2,12 @@ extends = nrf52_base board = rak3401 board_check = true +board_build.ldscript = boards/nrf52840_s140_v6.ld build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I variants/rak3401 -D RAK_3401 - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index 2bbba314..8f72999a 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -2,6 +2,7 @@ extends = nrf52_base board = rak4631 board_check = true +board_build.ldscript = boards/nrf52840_s140_v6.ld extra_scripts = ${nrf52_base.extra_scripts} post:variants/rak4631/fix_bsec_lib.py build_flags = ${nrf52_base.build_flags} @@ -9,7 +10,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/rak4631 -D RAK_4631 -D RAK_BOARD - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D PIN_GPS_TX=PIN_SERIAL1_RX diff --git a/variants/rak_wismesh_tag/platformio.ini b/variants/rak_wismesh_tag/platformio.ini index e9cddb74..b596c9a6 100644 --- a/variants/rak_wismesh_tag/platformio.ini +++ b/variants/rak_wismesh_tag/platformio.ini @@ -2,6 +2,7 @@ extends = nrf52_base board = rak4631 board_check = true +board_build.ldscript = boards/nrf52840_s140_v6.ld build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I variants/rak_wismesh_tag diff --git a/variants/sensecap_solar/platformio.ini b/variants/sensecap_solar/platformio.ini index effef38c..c9f7ed39 100644 --- a/variants/sensecap_solar/platformio.ini +++ b/variants/sensecap_solar/platformio.ini @@ -10,7 +10,7 @@ build_flags = ${nrf52_base.build_flags} -I src/helpers/nrf52 -D NRF52_PLATFORM=1 -D USE_SX1262 - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D P_LORA_TX_LED=12 diff --git a/variants/station_g2/StationG2Board.h b/variants/station_g2/StationG2Board.h index a905682c..d1989ee0 100644 --- a/variants/station_g2/StationG2Board.h +++ b/variants/station_g2/StationG2Board.h @@ -2,7 +2,6 @@ #include #include -#include class StationG2Board : public ESP32Board { public: @@ -21,29 +20,6 @@ public: } } - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - uint16_t getBattMilliVolts() override { return 0; } diff --git a/variants/thinknode_m2/ThinknodeM2Board.cpp b/variants/thinknode_m2/ThinknodeM2Board.cpp index 05965103..8d68006d 100644 --- a/variants/thinknode_m2/ThinknodeM2Board.cpp +++ b/variants/thinknode_m2/ThinknodeM2Board.cpp @@ -1,40 +1,30 @@ #include "ThinknodeM2Board.h" - - void ThinknodeM2Board::begin() { - pinMode(PIN_VEXT_EN, OUTPUT); - digitalWrite(PIN_VEXT_EN, !PIN_VEXT_EN_ACTIVE); // force power cycle - delay(20); // allow power rail to discharge - digitalWrite(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE); // turn backlight back on - delay(120); // give display time to bias on cold boot - ESP32Board::begin(); - pinMode(PIN_STATUS_LED, OUTPUT); // init power led - } - - void ThinknodeM2Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { - esp_deep_sleep_start(); - } - - void ThinknodeM2Board::powerOff() { - enterDeepSleep(0); - } - - uint16_t ThinknodeM2Board::getBattMilliVolts() { - analogReadResolution(12); - analogSetPinAttenuation(PIN_VBAT_READ, ADC_11db); - - uint32_t mv = 0; - for (int i = 0; i < 8; ++i) { - mv += analogReadMilliVolts(PIN_VBAT_READ); - delayMicroseconds(200); - } - mv /= 8; - - analogReadResolution(10); - return static_cast(mv * ADC_MULTIPLIER ); + pinMode(PIN_VEXT_EN, OUTPUT); + digitalWrite(PIN_VEXT_EN, !PIN_VEXT_EN_ACTIVE); // force power cycle + delay(20); // allow power rail to discharge + digitalWrite(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE); // turn backlight back on + delay(120); // give display time to bias on cold boot + ESP32Board::begin(); + pinMode(PIN_STATUS_LED, OUTPUT); // init power led } - const char* ThinknodeM2Board::getManufacturerName() const { - return "Elecrow ThinkNode M2"; +uint16_t ThinknodeM2Board::getBattMilliVolts() { + analogReadResolution(12); + analogSetPinAttenuation(PIN_VBAT_READ, ADC_11db); + + uint32_t mv = 0; + for (int i = 0; i < 8; ++i) { + mv += analogReadMilliVolts(PIN_VBAT_READ); + delayMicroseconds(200); } + mv /= 8; + + analogReadResolution(10); + return static_cast(mv * ADC_MULTIPLIER); +} + +const char *ThinknodeM2Board::getManufacturerName() const { + return "Elecrow ThinkNode M2"; +} diff --git a/variants/thinknode_m2/ThinknodeM2Board.h b/variants/thinknode_m2/ThinknodeM2Board.h index 8011fae6..02556777 100644 --- a/variants/thinknode_m2/ThinknodeM2Board.h +++ b/variants/thinknode_m2/ThinknodeM2Board.h @@ -3,15 +3,11 @@ #include #include #include -#include class ThinknodeM2Board : public ESP32Board { public: - void begin(); - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); - void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; diff --git a/variants/thinknode_m5/ThinknodeM5Board.cpp b/variants/thinknode_m5/ThinknodeM5Board.cpp index c4de538c..2cb138e6 100644 --- a/variants/thinknode_m5/ThinknodeM5Board.cpp +++ b/variants/thinknode_m5/ThinknodeM5Board.cpp @@ -19,14 +19,6 @@ void ThinknodeM5Board::begin() { ESP32Board::begin(); } - void ThinknodeM5Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { - esp_deep_sleep_start(); - } - - void ThinknodeM5Board::powerOff() { - enterDeepSleep(0); - } - uint16_t ThinknodeM5Board::getBattMilliVolts() { analogReadResolution(12); analogSetPinAttenuation(PIN_VBAT_READ, ADC_11db); diff --git a/variants/thinknode_m5/ThinknodeM5Board.h b/variants/thinknode_m5/ThinknodeM5Board.h index 3c120027..57ff0d00 100644 --- a/variants/thinknode_m5/ThinknodeM5Board.h +++ b/variants/thinknode_m5/ThinknodeM5Board.h @@ -3,7 +3,6 @@ #include #include #include -#include #include extern PCA9557 expander; @@ -13,8 +12,6 @@ class ThinknodeM5Board : public ESP32Board { public: void begin(); - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); - void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; diff --git a/variants/xiao_c3/XiaoC3Board.h b/variants/xiao_c3/XiaoC3Board.h index 6ea1c15f..c5700c68 100644 --- a/variants/xiao_c3/XiaoC3Board.h +++ b/variants/xiao_c3/XiaoC3Board.h @@ -3,7 +3,6 @@ #include #include -#include #include class XiaoC3Board : public ESP32Board { @@ -40,37 +39,6 @@ public: #endif } - void enterDeepSleep(uint32_t secs, int8_t wake_pin = -1) { - gpio_set_direction(gpio_num_t(P_LORA_DIO_1), GPIO_MODE_INPUT); - if (wake_pin >= 0) { - gpio_set_direction((gpio_num_t)wake_pin, GPIO_MODE_INPUT); - } - - //hold disable, isolate and power domain config functions may be unnecessary - //gpio_deep_sleep_hold_dis(); - //esp_sleep_config_gpio_isolate(); - gpio_deep_sleep_hold_en(); - -#if defined(LORA_TX_BOOST_PIN) - gpio_hold_en((gpio_num_t) LORA_TX_BOOST_PIN); - gpio_deep_sleep_hold_en(); -#endif - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - if (wake_pin >= 0) { - esp_deep_sleep_enable_gpio_wakeup((1 << P_LORA_DIO_1) | (1 << wake_pin), ESP_GPIO_WAKEUP_GPIO_HIGH); - } else { - esp_deep_sleep_enable_gpio_wakeup(1 << P_LORA_DIO_1, ESP_GPIO_WAKEUP_GPIO_HIGH); - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - #if defined(LORA_TX_BOOST_PIN) || defined(P_LORA_TX_LED) void onBeforeTransmit() override { #if defined(P_LORA_TX_LED) diff --git a/variants/xiao_c3/platformio.ini b/variants/xiao_c3/platformio.ini index c0e8458d..fa70d8a0 100644 --- a/variants/xiao_c3/platformio.ini +++ b/variants/xiao_c3/platformio.ini @@ -90,6 +90,11 @@ lib_deps = ${esp32_ota.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Xiao_C3_companion_radio_ble_ps] +extends = env:Xiao_C3_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 +board_build.partitions = min_spiffs.csv ; get around 4mb flash limit + [env:Xiao_C3_companion_radio_usb] extends = Xiao_esp32_C3 build_src_filter = ${Xiao_esp32_C3.build_src_filter} diff --git a/variants/xiao_c6/platformio.ini b/variants/xiao_c6/platformio.ini index 9f504b8e..0d8c2a79 100644 --- a/variants/xiao_c6/platformio.ini +++ b/variants/xiao_c6/platformio.ini @@ -1,6 +1,7 @@ [Xiao_C6] extends = esp32c6_base board = esp32-c6-devkitm-1 +board_build.flash_mode = dio board_build.partitions = min_spiffs.csv ; get around 4mb flash limit build_flags = ${esp32c6_base.build_flags} diff --git a/variants/xiao_nrf52/platformio.ini b/variants/xiao_nrf52/platformio.ini index a0854336..f4d1b93e 100644 --- a/variants/xiao_nrf52/platformio.ini +++ b/variants/xiao_nrf52/platformio.ini @@ -9,7 +9,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/xiao_nrf52 -UENV_INCLUDE_GPS -D NRF52_PLATFORM - -D NRF52_POWER_MANAGEMENT +; -D NRF52_POWER_MANAGEMENT -D XIAO_NRF52 -D USE_SX1262 -D RADIO_CLASS=CustomSX1262 diff --git a/variants/xiao_s3/platformio.ini b/variants/xiao_s3/platformio.ini index 22464e7d..b59e69ae 100644 --- a/variants/xiao_s3/platformio.ini +++ b/variants/xiao_s3/platformio.ini @@ -114,6 +114,10 @@ lib_deps = densaugeo/base64 @ ~1.4.0 adafruit/Adafruit SSD1306 @ ^2.5.13 +[env:Xiao_S3_companion_radio_ble_ps] +extends = env:Xiao_S3_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 + [env:Xiao_S3_companion_radio_usb] extends = Xiao_S3 build_flags = diff --git a/variants/xiao_s3_wio/platformio.ini b/variants/xiao_s3_wio/platformio.ini index db8c5a94..41420acf 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -173,6 +173,10 @@ lib_deps = densaugeo/base64 @ ~1.4.0 adafruit/Adafruit SSD1306 @ ^2.5.13 +[env:Xiao_S3_WIO_companion_radio_ble_ps] +extends = env:Xiao_S3_WIO_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 + [env:Xiao_S3_WIO_companion_radio_serial] extends = Xiao_S3_WIO build_flags = From 4a733c61cec4e7e92079b221322bf662d8da3008 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Tue, 9 Jun 2026 10:02:03 +0700 Subject: [PATCH 099/214] Renamed to add "femon" env to Heltec v4 and T096 --- variants/heltec_t096/platformio.ini | 6 +++--- variants/heltec_v4/platformio.ini | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/variants/heltec_t096/platformio.ini b/variants/heltec_t096/platformio.ini index a0e20ef6..5f17436a 100644 --- a/variants/heltec_t096/platformio.ini +++ b/variants/heltec_t096/platformio.ini @@ -120,7 +120,7 @@ build_src_filter = ${Heltec_t096.build_src_filter} lib_deps = ${Heltec_t096.lib_deps} -[env:Heltec_t096_companion_radio_ble] +[env:Heltec_t096_companion_radio_ble_femon] extends = Heltec_t096 board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld board_upload.maximum_size = 712704 @@ -144,9 +144,9 @@ lib_deps = densaugeo/base64 @ ~1.4.0 [env:Heltec_t096_companion_radio_ble_femoff] -extends = env:Heltec_t096_companion_radio_ble +extends = env:Heltec_t096_companion_radio_ble_femon build_flags = - ${env:Heltec_t096_companion_radio_ble.build_flags} + ${env:Heltec_t096_companion_radio_ble_femon.build_flags} -D RADIO_FEM_RXGAIN=0 ; undefined (default on), 1=on, 0=off [env:Heltec_t096_companion_radio_usb] diff --git a/variants/heltec_v4/platformio.ini b/variants/heltec_v4/platformio.ini index 5ad2b1e9..caa71a33 100644 --- a/variants/heltec_v4/platformio.ini +++ b/variants/heltec_v4/platformio.ini @@ -222,14 +222,14 @@ lib_deps = ${heltec_v4_oled.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:heltec_v4_companion_radio_ble_ps] +[env:heltec_v4_companion_radio_ble_ps_femon] extends = env:heltec_v4_companion_radio_ble platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 [env:heltec_v4_3_companion_radio_ble_ps_femoff] -extends = env:heltec_v4_companion_radio_ble_ps +extends = env:heltec_v4_companion_radio_ble_ps_femon build_flags = - ${env:heltec_v4_companion_radio_ble_ps.build_flags} + ${env:heltec_v4_companion_radio_ble_ps_femon.build_flags} -D RADIO_FEM_RXGAIN=0 ; undefined (default on), 1=on, 0=off [env:heltec_v4_companion_radio_wifi] From e0733f834699e78f9b93cec853a20f53b26b9175 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Tue, 9 Jun 2026 11:34:36 +0700 Subject: [PATCH 100/214] Added 0x77 for ENV_INCLUDE_BME680_BSEC --- src/helpers/sensors/EnvironmentSensorManager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 637d61af..ea57677f 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -15,6 +15,7 @@ #if ENV_INCLUDE_BME680_BSEC #ifndef TELEM_BME680_ADDRESS #define TELEM_BME680_ADDRESS 0x76 +#define TELEM_BME680_ADDRESS_2 0x77 #endif #define TELEM_BME680_SEALEVELPRESSURE_HPA (1013.25) #include From 7c1b1c6e5e64da4d494881afebbe545faaf62f0a Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Tue, 9 Jun 2026 11:59:14 +0700 Subject: [PATCH 101/214] Hotfix: CMD_SEND_RAW_PACKET not freeing packet on parse failure. https://github.com/meshcore-dev/MeshCore/pull/2722 --- examples/companion_radio/MyMesh.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 6bf2671a..1dd5162b 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -2010,6 +2010,7 @@ void MyMesh::handleCmdFrame(size_t len) { sendPacket(pkt, priority, 0); writeOKFrame(); } else { + releasePacket(pkt); writeErrFrame(ERR_CODE_ILLEGAL_ARG); } } else { From c94ed29ca36e73d9026319af18b8977648ec8809 Mon Sep 17 00:00:00 2001 From: liamcottle Date: Fri, 12 Jun 2026 03:18:10 +1200 Subject: [PATCH 102/214] add github workflow to close stale issues --- .github/workflows/stale-bot.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/stale-bot.yml diff --git a/.github/workflows/stale-bot.yml b/.github/workflows/stale-bot.yml new file mode 100644 index 00000000..afe874f8 --- /dev/null +++ b/.github/workflows/stale-bot.yml @@ -0,0 +1,32 @@ +name: 'Run Stale Bot' +on: + schedule: + - cron: '30 1 * * *' # daily at 1:30am + workflow_dispatch: {} + +permissions: + actions: write + issues: write + pull-requests: write + +jobs: + close-issues: + # only run on main repo, not forks + if: github.repository == "meshcore-dev/MeshCore" + runs-on: ubuntu-latest + steps: + - name: Close Stale Issues + uses: actions/stale@v10 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + # auto close issues + days-before-issue-stale: 60 + days-before-issue-close: 7 + exempt-issue-labels: "keep-open" + stale-issue-label: "stale" + stale-issue-message: "This issue is stale because it has been open for 60 days with no activity. Remove the stale label or add a comment if this issue is still relevant, otherwise this issue will automatically close in 7 days." + close-issue-message: "This issue was closed because it has been inactive for 7 days since being marked as stale." + # don't auto close prs + days-before-pr-stale: -1 + days-before-pr-close: -1 + \ No newline at end of file From 3b3992539945b861801ab709b2361cb03edee657 Mon Sep 17 00:00:00 2001 From: liamcottle Date: Fri, 12 Jun 2026 03:20:44 +1200 Subject: [PATCH 103/214] use single quotes for repo name --- .github/workflows/stale-bot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale-bot.yml b/.github/workflows/stale-bot.yml index afe874f8..ec166587 100644 --- a/.github/workflows/stale-bot.yml +++ b/.github/workflows/stale-bot.yml @@ -12,7 +12,7 @@ permissions: jobs: close-issues: # only run on main repo, not forks - if: github.repository == "meshcore-dev/MeshCore" + if: github.repository == 'meshcore-dev/MeshCore' runs-on: ubuntu-latest steps: - name: Close Stale Issues From fe56d01da28b5bf552274cf946a147ffb558a876 Mon Sep 17 00:00:00 2001 From: Quency-D Date: Fri, 12 Jun 2026 17:58:19 +0800 Subject: [PATCH 104/214] add tower v2 PA control --- .../heltec_tower_v2/HeltecTowerV2Board.cpp | 9 +++-- variants/heltec_tower_v2/HeltecTowerV2Board.h | 5 ++- variants/heltec_tower_v2/LoRaFEMControl.cpp | 40 +++++++++++++++++++ variants/heltec_tower_v2/LoRaFEMControl.h | 13 ++++++ variants/heltec_tower_v2/platformio.ini | 2 +- variants/heltec_tower_v2/variant.cpp | 15 +++++++ variants/heltec_tower_v2/variant.h | 13 ++++-- 7 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 variants/heltec_tower_v2/LoRaFEMControl.cpp create mode 100644 variants/heltec_tower_v2/LoRaFEMControl.h diff --git a/variants/heltec_tower_v2/HeltecTowerV2Board.cpp b/variants/heltec_tower_v2/HeltecTowerV2Board.cpp index 2fd7a618..04213bda 100644 --- a/variants/heltec_tower_v2/HeltecTowerV2Board.cpp +++ b/variants/heltec_tower_v2/HeltecTowerV2Board.cpp @@ -19,6 +19,7 @@ void HeltecTowerV2Board::initiateShutdown(uint8_t reason) { digitalWrite(PIN_GPS_STANDBY, LOW); pinMode(PIN_GPS_RESET, OUTPUT); digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); + loRaFEMControl.setSleepModeEnable(); bool enable_lpcomp = (reason == SHUTDOWN_REASON_LOW_VOLTAGE || reason == SHUTDOWN_REASON_BOOT_PROTECT); @@ -37,10 +38,8 @@ void HeltecTowerV2Board::initiateShutdown(uint8_t reason) { void HeltecTowerV2Board::begin() { NRF52Board::begin(); -#ifdef P_LORA_TX_LED pinMode(P_LORA_TX_LED, OUTPUT); digitalWrite(P_LORA_TX_LED, !LED_STATE_ON); -#endif pinMode(PIN_BAT_CTL, OUTPUT); digitalWrite(PIN_BAT_CTL, LOW); @@ -58,17 +57,18 @@ void HeltecTowerV2Board::begin() { digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); pinMode(PIN_GPS_STANDBY, OUTPUT); digitalWrite(PIN_GPS_STANDBY, HIGH); + loRaFEMControl.init(); } -#ifdef P_LORA_TX_LED void HeltecTowerV2Board::onBeforeTransmit() { digitalWrite(P_LORA_TX_LED, LED_STATE_ON); + loRaFEMControl.setTxModeEnable(); } void HeltecTowerV2Board::onAfterTransmit() { digitalWrite(P_LORA_TX_LED, !LED_STATE_ON); + loRaFEMControl.setRxModeEnable(); } -#endif uint16_t HeltecTowerV2Board::getBattMilliVolts() { analogReadResolution(12); @@ -95,6 +95,7 @@ void HeltecTowerV2Board::powerOff() { digitalWrite(PIN_GPS_STANDBY, LOW); pinMode(PIN_GPS_RESET, OUTPUT); digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); + loRaFEMControl.setSleepModeEnable(); pinMode(PIN_BAT_CTL, OUTPUT); digitalWrite(PIN_BAT_CTL, LOW); variant_shutdown(); diff --git a/variants/heltec_tower_v2/HeltecTowerV2Board.h b/variants/heltec_tower_v2/HeltecTowerV2Board.h index 817d3c1a..6912acde 100644 --- a/variants/heltec_tower_v2/HeltecTowerV2Board.h +++ b/variants/heltec_tower_v2/HeltecTowerV2Board.h @@ -3,6 +3,7 @@ #include #include #include +#include "LoRaFEMControl.h" class HeltecTowerV2Board : public NRF52BoardDCDC { protected: @@ -11,12 +12,12 @@ protected: #endif public: + LoRaFEMControl loRaFEMControl; + HeltecTowerV2Board() : NRF52Board("TOWER_V2_OTA") {} void begin(); -#ifdef P_LORA_TX_LED void onBeforeTransmit() override; void onAfterTransmit() override; -#endif uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override; void powerOff() override; diff --git a/variants/heltec_tower_v2/LoRaFEMControl.cpp b/variants/heltec_tower_v2/LoRaFEMControl.cpp new file mode 100644 index 00000000..d87d1849 --- /dev/null +++ b/variants/heltec_tower_v2/LoRaFEMControl.cpp @@ -0,0 +1,40 @@ +#include "LoRaFEMControl.h" + +#include +#include "variant.h" + +static void enableFEMPower() { + bool wasOff = digitalRead(LORA_KCT8103L_EN) != HIGH; + digitalWrite(LORA_KCT8103L_EN, HIGH); + if (wasOff) { + delay(5); + } +} + +void LoRaFEMControl::init() { + pinMode(LORA_KCT8103L_EN, OUTPUT); + digitalWrite(LORA_KCT8103L_EN, HIGH); + delay(1); + pinMode(LORA_KCT8103L_TX_RX, OUTPUT); + digitalWrite(LORA_KCT8103L_TX_RX, LOW); +} + +void LoRaFEMControl::setSleepModeEnable() { + pinMode(LORA_KCT8103L_EN, OUTPUT); + digitalWrite(LORA_KCT8103L_EN, LOW); +} + +void LoRaFEMControl::setTxModeEnable() { + enableFEMPower(); + digitalWrite(LORA_KCT8103L_TX_RX, HIGH); +} + +void LoRaFEMControl::setRxModeEnable() { + enableFEMPower(); + digitalWrite(LORA_KCT8103L_TX_RX, LOW); +} + +void LoRaFEMControl::setRxModeEnableWhenMCUSleep() { + enableFEMPower(); + digitalWrite(LORA_KCT8103L_TX_RX, LOW); +} diff --git a/variants/heltec_tower_v2/LoRaFEMControl.h b/variants/heltec_tower_v2/LoRaFEMControl.h new file mode 100644 index 00000000..e6c0ad2f --- /dev/null +++ b/variants/heltec_tower_v2/LoRaFEMControl.h @@ -0,0 +1,13 @@ +#pragma once + +class LoRaFEMControl { +public: + LoRaFEMControl() {} + virtual ~LoRaFEMControl() {} + + void init(); + void setSleepModeEnable(); + void setTxModeEnable(); + void setRxModeEnable(); + void setRxModeEnableWhenMCUSleep(); +}; diff --git a/variants/heltec_tower_v2/platformio.ini b/variants/heltec_tower_v2/platformio.ini index 2fac9fbc..190a21c9 100644 --- a/variants/heltec_tower_v2/platformio.ini +++ b/variants/heltec_tower_v2/platformio.ini @@ -11,7 +11,7 @@ build_flags = ${nrf52_base.build_flags} -D NRF52_POWER_MANAGEMENT -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper - -D LORA_TX_POWER=22 + -D LORA_TX_POWER=12 -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 build_src_filter = ${nrf52_base.build_src_filter} diff --git a/variants/heltec_tower_v2/variant.cpp b/variants/heltec_tower_v2/variant.cpp index 699c6e4a..66ec2111 100644 --- a/variants/heltec_tower_v2/variant.cpp +++ b/variants/heltec_tower_v2/variant.cpp @@ -19,7 +19,22 @@ void initVariant() void variant_shutdown() { + nrf_gpio_cfg_default(PIN_GPS_EN); nrf_gpio_cfg_default(PIN_GPS_PPS); + nrf_gpio_cfg_default(PIN_GPS_RESET); + nrf_gpio_cfg_default(PIN_GPS_STANDBY); + nrf_gpio_cfg_default(GPS_RX_PIN); + nrf_gpio_cfg_default(GPS_TX_PIN); + nrf_gpio_cfg_default(LORA_KCT8103L_TX_RX); + nrf_gpio_cfg_default(RF_PA_DETECT_PIN); + nrf_gpio_cfg_default(SX126X_CS); + nrf_gpio_cfg_default(SX126X_DIO1); + nrf_gpio_cfg_default(SX126X_BUSY); + nrf_gpio_cfg_default(SX126X_RESET); + nrf_gpio_cfg_default(PIN_SPI_MISO); + nrf_gpio_cfg_default(PIN_SPI_MOSI); + nrf_gpio_cfg_default(PIN_SPI_SCK); + nrf_gpio_cfg_default(PIN_LED); detachInterrupt(PIN_GPS_PPS); detachInterrupt(PIN_BUTTON1); } diff --git a/variants/heltec_tower_v2/variant.h b/variants/heltec_tower_v2/variant.h index d3f2599a..352184b8 100644 --- a/variants/heltec_tower_v2/variant.h +++ b/variants/heltec_tower_v2/variant.h @@ -11,8 +11,8 @@ #define NUM_ANALOG_OUTPUTS (0) #define WIRE_INTERFACES_COUNT (1) -#define PIN_WIRE_SDA (0 + 11) -#define PIN_WIRE_SCL (0 + 12) +#define PIN_WIRE_SDA (0 + 30) +#define PIN_WIRE_SCL (0 + 5) #define PIN_BOARD_SDA PIN_WIRE_SDA #define PIN_BOARD_SCL PIN_WIRE_SCL @@ -52,6 +52,13 @@ #define P_LORA_MOSI PIN_SPI_MOSI #define P_LORA_SCLK PIN_SPI_SCK +#define USE_KCT8103L_PA_ONLY +#define LORA_KCT8103L_EN (0 + 15) +#define LORA_KCT8103L_TX_RX (0 + 16) +#define LORA_PA_POWER LORA_KCT8103L_EN +#define RF_PA_DETECT_PIN (0 + 13) +#define RF_PA_HIGH_POWER_VALUE HIGH + #define GPS_L76K #define GPS_RESET_MODE LOW #define PIN_GPS_RESET (32 + 6) @@ -98,4 +105,4 @@ #define NRF52_POWER_MANAGEMENT #define PWRMGT_VOLTAGE_BOOTLOCK 3100 #define PWRMGT_LPCOMP_AIN 2 -#define PWRMGT_LPCOMP_REFSEL 1 \ No newline at end of file +#define PWRMGT_LPCOMP_REFSEL 1 From d2d3d44c9be7f2f6e90f191a7cb7a3c1d361fc75 Mon Sep 17 00:00:00 2001 From: Quency-D Date: Fri, 12 Jun 2026 18:05:27 +0800 Subject: [PATCH 105/214] Add maximum power limit --- variants/heltec_tower_v2/platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/heltec_tower_v2/platformio.ini b/variants/heltec_tower_v2/platformio.ini index 190a21c9..f029b7d4 100644 --- a/variants/heltec_tower_v2/platformio.ini +++ b/variants/heltec_tower_v2/platformio.ini @@ -12,6 +12,7 @@ build_flags = ${nrf52_base.build_flags} -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=12 + -D MAX_LORA_TX_POWER=22 ; Max SX1262 output -> ~29dBm at antenna -D SX126X_CURRENT_LIMIT=140 -D SX126X_RX_BOOSTED_GAIN=1 build_src_filter = ${nrf52_base.build_src_filter} From 06130dce29c907ebc014dc52156fbb1e52210903 Mon Sep 17 00:00:00 2001 From: formtapez Date: Fri, 12 Jun 2026 12:11:12 +0200 Subject: [PATCH 106/214] added some missing CLI commands --- docs/cli_commands.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 9accb299..f482cfcb 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -28,12 +28,25 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Usage:** - `reboot` +**Note:** No reply is sent. + +--- + +### Power-off the node +**Usage:** +- `poweroff`, or +- `shutdown +` +**Note:** No reply is sent. + --- ### Reset the clock and reboot **Usage:** - `clkreboot` +**Note:** No reply is sent. + --- ### Sync the clock with the remote device @@ -632,10 +645,21 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Parameters:** - `value`: Maximum flood hop count (0-64) for a packet without a scope (no region set) -**Default:** `0xFF` - indicates it hasn't been set, will track flood.max until it is. +**Default:** `64` - indicates it hasn't been set, will track flood.max until it is. **Note:** An alternative to `region denyf *`, setting `flood.max.unscoped` to a lower value such as `3` would allow for local unscoped messages to propagate, while preventing noisy neighbors from flooding a local region. +--- + +#### Limit the number of hops for an advert flood message +**Usage:** +- `get flood.max.advert` +- `set flood.max.advert ` + +**Parameters:** +- `value`: Maximum flood hop count (0-64) for an advert packet + +**Default:** `8` --- From d3444e6b0be513f982f3bcb7d2f05a091f16a2fd Mon Sep 17 00:00:00 2001 From: formtapez Date: Fri, 12 Jun 2026 12:14:46 +0200 Subject: [PATCH 107/214] fix formatting --- docs/cli_commands.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index f482cfcb..66a9b77a 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -35,8 +35,8 @@ This document provides an overview of CLI commands that can be sent to MeshCore ### Power-off the node **Usage:** - `poweroff`, or -- `shutdown -` +- `shutdown` + **Note:** No reply is sent. --- @@ -645,7 +645,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Parameters:** - `value`: Maximum flood hop count (0-64) for a packet without a scope (no region set) -**Default:** `64` - indicates it hasn't been set, will track flood.max until it is. +**Default:** `64` - (`0xFF` indicates it hasn't been set, will track flood.max until it is.) **Note:** An alternative to `region denyf *`, setting `flood.max.unscoped` to a lower value such as `3` would allow for local unscoped messages to propagate, while preventing noisy neighbors from flooding a local region. From 19efda9f2e52e9da10c44bf55952ae72751d09cf Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Fri, 12 Jun 2026 21:19:05 +0700 Subject: [PATCH 108/214] Reverted code change to GPS --- src/helpers/sensors/MicroNMEALocationProvider.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/helpers/sensors/MicroNMEALocationProvider.h b/src/helpers/sensors/MicroNMEALocationProvider.h index 8b3c5867..eec466d3 100644 --- a/src/helpers/sensors/MicroNMEALocationProvider.h +++ b/src/helpers/sensors/MicroNMEALocationProvider.h @@ -76,7 +76,6 @@ public : void begin() override { claim(); if (_pin_en != -1) { - pinMode(_pin_en, OUTPUT); digitalWrite(_pin_en, PIN_GPS_EN_ACTIVE); } if (_pin_reset != -1) { @@ -95,7 +94,6 @@ public : void stop() override { if (_pin_en != -1) { digitalWrite(_pin_en, !PIN_GPS_EN_ACTIVE); - pinMode(_pin_en, INPUT); // Reduce 0.3mA leaking } if (_pin_reset != -1) { digitalWrite(_pin_reset, GPS_RESET_FORCE); From 1c4c995a41777781c390ff3a41f8a1c6eb221ee1 Mon Sep 17 00:00:00 2001 From: Quency-D Date: Sat, 13 Jun 2026 16:00:06 +0800 Subject: [PATCH 109/214] Fix low power consumption issues --- variants/heltec_tower_v2/variant.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/variants/heltec_tower_v2/variant.cpp b/variants/heltec_tower_v2/variant.cpp index 66ec2111..cfb88c4a 100644 --- a/variants/heltec_tower_v2/variant.cpp +++ b/variants/heltec_tower_v2/variant.cpp @@ -25,6 +25,8 @@ void variant_shutdown() nrf_gpio_cfg_default(PIN_GPS_STANDBY); nrf_gpio_cfg_default(GPS_RX_PIN); nrf_gpio_cfg_default(GPS_TX_PIN); + pinMode(LORA_KCT8103L_EN, OUTPUT); + digitalWrite(LORA_KCT8103L_EN, LOW); nrf_gpio_cfg_default(LORA_KCT8103L_TX_RX); nrf_gpio_cfg_default(RF_PA_DETECT_PIN); nrf_gpio_cfg_default(SX126X_CS); From d5f74e93c530a41a2c88dbcc5f34f06860129179 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sat, 13 Jun 2026 18:19:41 +1000 Subject: [PATCH 110/214] * PAYLOAD_TYPE_PATH bad path_len now rejected --- src/Mesh.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 87ad61af..e9b92262 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -155,6 +155,10 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { if (pkt->getPayloadType() == PAYLOAD_TYPE_PATH) { int k = 0; uint8_t path_len = data[k++]; + if (!Packet::isValidPathLen(path_len)) { + MESH_DEBUG_PRINTLN("%s PAYLOAD_TYPE_PATH, bad path_len: %u", getLogDateTime(), (uint32_t)path_len); + break; // reject bad encoding + } uint8_t hash_size = (path_len >> 6) + 1; uint8_t hash_count = path_len & 63; uint8_t* path = &data[k]; k += hash_size*hash_count; From 3ee58fd2e3f16cad205611503b6dd71728aac23c Mon Sep 17 00:00:00 2001 From: Quency-D Date: Sat, 13 Jun 2026 17:31:54 +0800 Subject: [PATCH 111/214] Remove companion FEM RX gain command IDs --- examples/companion_radio/DataStore.cpp | 2 -- examples/companion_radio/MyMesh.cpp | 29 -------------------------- examples/companion_radio/NodePrefs.h | 1 - 3 files changed, 32 deletions(-) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index fdb924ad..bf2f36c3 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -233,7 +233,6 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no file.read((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89 file.read((uint8_t *)_prefs.default_scope_name, sizeof(_prefs.default_scope_name)); // 90 file.read((uint8_t *)_prefs.default_scope_key, sizeof(_prefs.default_scope_key)); // 121 - file.read((uint8_t *)&_prefs.radio_fem_rxgain, sizeof(_prefs.radio_fem_rxgain)); // 122 file.close(); } @@ -274,7 +273,6 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ file.write((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89 file.write((uint8_t *)_prefs.default_scope_name, sizeof(_prefs.default_scope_name)); // 90 file.write((uint8_t *)_prefs.default_scope_key, sizeof(_prefs.default_scope_key)); // 121 - file.write((uint8_t *)&_prefs.radio_fem_rxgain, sizeof(_prefs.radio_fem_rxgain)); // 122 file.close(); } diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 1dd5162b..c468967f 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -62,8 +62,6 @@ #define CMD_SET_DEFAULT_FLOOD_SCOPE 63 #define CMD_GET_DEFAULT_FLOOD_SCOPE 64 #define CMD_SEND_RAW_PACKET 65 -#define CMD_GET_RADIO_FEM_RXGAIN 66 -#define CMD_SET_RADIO_FEM_RXGAIN 67 // Stats sub-types for CMD_GET_STATS #define STATS_TYPE_CORE 0 @@ -888,7 +886,6 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe _prefs.rx_boosted_gain = 1; // enabled by default #endif #endif - _prefs.radio_fem_rxgain = 1; } void MyMesh::begin(bool has_display) { @@ -938,7 +935,6 @@ void MyMesh::begin(bool has_display) { _prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, -9, MAX_LORA_TX_POWER); _prefs.gps_enabled = constrain(_prefs.gps_enabled, 0, 1); // Ensure boolean 0 or 1 _prefs.gps_interval = constrain(_prefs.gps_interval, 0, 86400); // Max 24 hours - _prefs.radio_fem_rxgain = constrain(_prefs.radio_fem_rxgain, 0, 1); #ifdef BLE_PIN_CODE // 123456 by default if (_prefs.ble_pin == 0) { @@ -968,7 +964,6 @@ void MyMesh::begin(bool has_display) { radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); radio_driver.setTxPower(_prefs.tx_power_dbm); radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); - board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s", radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled"); } @@ -1826,30 +1821,6 @@ void MyMesh::handleCmdFrame(size_t len) { } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } - } else if (cmd_frame[0] == CMD_GET_RADIO_FEM_RXGAIN) { - if (!board.canControlLoRaFemLna()) { - writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); - } else { - out_frame[0] = RESP_CODE_OK; - uint8_t value = board.isLoRaFemLnaEnabled() ? 1 : 0; - memcpy(&out_frame[1], &value, 1); - _serial->writeFrame(out_frame, 2); - } - } else if (cmd_frame[0] == CMD_SET_RADIO_FEM_RXGAIN && len >= 2) { - uint8_t value = cmd_frame[1]; - if (!board.canControlLoRaFemLna()) { - writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); - } else if (value <= 1) { - _prefs.radio_fem_rxgain = value; - if (board.setLoRaFemLnaEnabled(value != 0)) { - savePrefs(); - writeOKFrame(); - } else { - writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); - } - } else { - writeErrFrame(ERR_CODE_ILLEGAL_ARG); - } } else if (cmd_frame[0] == CMD_GET_ADVERT_PATH && len >= PUB_KEY_SIZE+2) { // FUTURE use: uint8_t reserved = cmd_frame[1]; uint8_t *pub_key = &cmd_frame[2]; diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index ecb117bd..84d51413 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -29,7 +29,6 @@ struct NodePrefs { // persisted to file uint32_t gps_interval; // GPS read interval in seconds uint8_t autoadd_config; // bitmask for auto-add contacts config uint8_t rx_boosted_gain; // SX126x RX boosted gain mode (0=power saving, 1=boosted) - uint8_t radio_fem_rxgain; // LoRa FEM RX gain setting uint8_t client_repeat; uint8_t path_hash_mode; // which path mode to use when sending uint8_t autoadd_max_hops; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64) From 5300fa18c7b0f441d9a123b86aa49a7a5c9739cf Mon Sep 17 00:00:00 2001 From: Quency-D Date: Sat, 13 Jun 2026 17:40:11 +0800 Subject: [PATCH 112/214] Restore companion NodePrefs file ending --- examples/companion_radio/NodePrefs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 84d51413..48c381ce 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -34,4 +34,4 @@ struct NodePrefs { // persisted to file uint8_t autoadd_max_hops; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64) char default_scope_name[31]; uint8_t default_scope_key[16]; -}; +}; \ No newline at end of file From 07648e3344780bcac297d197ec1579a91a308fbf Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 14 Jun 2026 17:50:41 +1000 Subject: [PATCH 113/214] * fix for anon contacts when full --- examples/companion_radio/MyMesh.cpp | 12 ++------ examples/simple_secure_chat/main.cpp | 2 +- src/helpers/BaseChatMesh.cpp | 45 ++++++++++++++++------------ src/helpers/BaseChatMesh.h | 14 ++++++--- 4 files changed, 39 insertions(+), 34 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index c468967f..d05bc6de 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -854,7 +854,7 @@ void MyMesh::onSendTimeout() {} MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store, AbstractUITask* ui) : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables), - _serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4), _store(&store), _ui(ui) { + _serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4), _store(&store), _ui(ui), _iter(0) { _iter_started = false; _cli_rescue = false; offline_queue_len = 0; @@ -2187,15 +2187,7 @@ void MyMesh::checkSerialInterface() { && !_serial->isWriteBusy() // don't spam the Serial Interface too quickly! ) { ContactInfo contact; - bool found = false; - while (_iter.hasNext(this, contact)) { - if (contact.type != ADV_TYPE_NONE) { - found = true; - break; - } - } - - if (found) { + if (_iter.hasNext(this, contact)) { if (contact.lastmod > _iter_filter_since) { // apply the 'since' filter writeContactRespFrame(RESP_CODE_CONTACT, contact); if (contact.lastmod > _most_recent_lastmod) { diff --git a/examples/simple_secure_chat/main.cpp b/examples/simple_secure_chat/main.cpp index d5066736..d93810ed 100644 --- a/examples/simple_secure_chat/main.cpp +++ b/examples/simple_secure_chat/main.cpp @@ -135,7 +135,7 @@ class MyMesh : public BaseChatMesh, ContactVisitor { File file = _fs->open("/contacts", "w", true); #endif if (file) { - ContactsIterator iter; + ContactsIterator iter = startContactsIterator(); ContactInfo c; uint8_t unused = 0; uint32_t reserved = 0; diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index d3ef034e..71b2681f 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -68,29 +68,36 @@ void BaseChatMesh::bootstrapRTCfromContacts() { } ContactInfo* BaseChatMesh::allocateContactSlot(bool transient_only) { - if (num_contacts < MAX_CONTACTS) { - return &contacts[num_contacts++]; - } else if (transient_only || shouldOverwriteWhenFull()) { - // Find oldest non-favourite contact by oldest lastmod timestamp - int oldest_idx = -1; - uint32_t oldest_lastmod = 0xFFFFFFFF; - for (int i = 0; i < num_contacts; i++) { - if (transient_only) { - if (contacts[i].type == ADV_TYPE_NONE && contacts[i].lastmod < oldest_lastmod) { - oldest_lastmod = contacts[i].lastmod; - oldest_idx = i; - } - } else { + int oldest_idx = -1; + uint32_t oldest_lastmod = 0xFFFFFFFF; + if (transient_only) { + // only allocate from first N + for (int i = 0; i < MAX_ANON_CONTACTS; i++) { + if (contacts[i].type == ADV_TYPE_NONE && contacts[i].lastmod < oldest_lastmod) { + oldest_lastmod = contacts[i].lastmod; + oldest_idx = i; + } + } + if (oldest_idx >= 0) { + // NOTE: do NOT call onContactOverwrite() + return &contacts[oldest_idx]; + } + } else { + if (num_contacts < MAX_ANON_CONTACTS+MAX_CONTACTS) { + return &contacts[num_contacts++]; + } else if (shouldOverwriteWhenFull()) { + // Find oldest non-favourite contact by oldest lastmod timestamp + for (int i = MAX_ANON_CONTACTS; i < num_contacts; i++) { bool is_favourite = (contacts[i].flags & 0x01) != 0; - if (!is_favourite && contacts[i].lastmod < oldest_lastmod && contacts[i].type != ADV_TYPE_NONE) { + if (!is_favourite && contacts[i].lastmod < oldest_lastmod) { oldest_lastmod = contacts[i].lastmod; oldest_idx = i; } } - } - if (oldest_idx >= 0) { - onContactOverwrite(contacts[oldest_idx].id.pub_key); - return &contacts[oldest_idx]; + if (oldest_idx >= 0) { + onContactOverwrite(contacts[oldest_idx].id.pub_key); + return &contacts[oldest_idx]; + } } } return NULL; // no space, no overwrite or all contacts are all favourites @@ -930,7 +937,7 @@ bool BaseChatMesh::getContactByIdx(uint32_t idx, ContactInfo& contact) { } ContactsIterator BaseChatMesh::startContactsIterator() { - return ContactsIterator(); + return ContactsIterator(MAX_ANON_CONTACTS); // start at offset, skip the anon entries } bool ContactsIterator::hasNext(const BaseChatMesh* mesh, ContactInfo& dest) { diff --git a/src/helpers/BaseChatMesh.h b/src/helpers/BaseChatMesh.h index c04bfda3..3a277c1e 100644 --- a/src/helpers/BaseChatMesh.h +++ b/src/helpers/BaseChatMesh.h @@ -28,8 +28,9 @@ public: class BaseChatMesh; class ContactsIterator { - int next_idx = 0; + int next_idx; public: + ContactsIterator(int start) { next_idx = start; } bool hasNext(const BaseChatMesh* mesh, ContactInfo& dest); }; @@ -79,8 +80,9 @@ class BaseChatMesh : public mesh::Mesh { protected: BaseChatMesh(mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::PacketManager& mgr, mesh::MeshTables& tables) : mesh::Mesh(radio, ms, rng, rtc, mgr, tables) - { - num_contacts = 0; + { + resetContacts(); + #ifdef MAX_GROUP_CHANNELS memset(channels, 0, sizeof(channels)); num_channels = 0; @@ -91,7 +93,11 @@ protected: } void bootstrapRTCfromContacts(); - void resetContacts() { num_contacts = 0; } + + void resetContacts() { + memset(contacts, 0, sizeof(contacts[0])*MAX_ANON_CONTACTS); // set all to have type = ADV_TYPE_NONE(0) + num_contacts = MAX_ANON_CONTACTS; // seed the first contacts for anon requests + } void populateContactFromAdvert(ContactInfo& ci, const mesh::Identity& id, const AdvertDataParser& parser, uint32_t timestamp); ContactInfo* allocateContactSlot(bool transient_only=false); // helper to find slot for new contact From c2d223ff5512f2862b004d33b8b39e9b8340cffe Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 14 Jun 2026 18:12:08 +1000 Subject: [PATCH 114/214] * now handle the case where onAdvertRecv() should _replace_ the anon contact slot --- src/helpers/BaseChatMesh.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index 71b2681f..ef586303 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -146,6 +146,11 @@ void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, packet->header = save; } + if (from && from->type == ADV_TYPE_NONE) { // already in contacts, but from a temporary ANON_REQ ? + memset(from, 0, sizeof(*from)); // clear the anon/temp slot + from = NULL; // do normal 'add' flow + } + bool is_new = false; // true = not in contacts[], false = exists in contacts[] if (from == NULL) { if (!shouldAutoAddContactType(parser.getType())) { From 538ac38e18c87181b884b93d7045752999253c21 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 14 Jun 2026 18:39:34 +1000 Subject: [PATCH 115/214] * fix for counter in RESP_CODE_CONTACTS_START --- src/helpers/BaseChatMesh.cpp | 2 +- src/helpers/BaseChatMesh.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index ef586303..972a97e9 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -946,7 +946,7 @@ ContactsIterator BaseChatMesh::startContactsIterator() { } bool ContactsIterator::hasNext(const BaseChatMesh* mesh, ContactInfo& dest) { - if (next_idx >= mesh->getNumContacts()) return false; + if (next_idx >= mesh->getTotalContactSlots()) return false; dest = mesh->contacts[next_idx++]; return true; diff --git a/src/helpers/BaseChatMesh.h b/src/helpers/BaseChatMesh.h index 3a277c1e..d9878547 100644 --- a/src/helpers/BaseChatMesh.h +++ b/src/helpers/BaseChatMesh.h @@ -172,7 +172,8 @@ public: ContactInfo* lookupContactByPubKey(const uint8_t* pub_key, int prefix_len); bool removeContact(ContactInfo& contact); bool addContact(const ContactInfo& contact); - int getNumContacts() const { return num_contacts; } + int getTotalContactSlots() const { return num_contacts; } + int getNumContacts() const { return num_contacts - MAX_ANON_CONTACTS; } // don't include the reserved slots at start bool getContactByIdx(uint32_t idx, ContactInfo& contact); ContactsIterator startContactsIterator(); ChannelDetails* addChannel(const char* name, const char* psk_base64); From 2e73fe948eca9e289f8dd8900a251b027e33a014 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sun, 14 Jun 2026 18:54:51 +1000 Subject: [PATCH 116/214] * fix for anon lastmod --- examples/companion_radio/MyMesh.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index d05bc6de..0b90df8b 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1542,6 +1542,7 @@ void MyMesh::handleCmdFrame(size_t len) { memcpy(anon.id.pub_key, pub_key, PUB_KEY_SIZE); anon.out_path_len = 0; // default to zero-hop direct anon.type = ADV_TYPE_NONE; // unknown + anon.lastmod = getRTCClock()->getCurrentTime(); if (addContact(anon)) recipient = &anon; } From e7db7c5a94680a1e983cf1c7aa2b558f85b72ce0 Mon Sep 17 00:00:00 2001 From: Marco Date: Sun, 14 Jun 2026 11:41:01 +0200 Subject: [PATCH 117/214] Add ESP32 Reset Reason --- src/helpers/CommonCLI.cpp | 4 ---- src/helpers/ESP32Board.h | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 5a5d1eab..25f1a2d1 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -944,13 +944,9 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep strcpy(reply, "ERROR: Power management not supported"); #endif } else if (memcmp(config, "pwrmgt.bootreason", 17) == 0) { -#ifdef NRF52_POWER_MANAGEMENT sprintf(reply, "> Reset: %s; Shutdown: %s", _board->getResetReasonString(_board->getResetReason()), _board->getShutdownReasonString(_board->getShutdownReason())); -#else - strcpy(reply, "ERROR: Power management not supported"); -#endif } else if (memcmp(config, "pwrmgt.bootmv", 13) == 0) { #ifdef NRF52_POWER_MANAGEMENT sprintf(reply, "> %u mV", _board->getBootVoltage()); diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index a4cbf2a9..1efc99f3 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -155,6 +155,42 @@ public: void setInhibitSleep(bool inhibit) { inhibit_sleep = inhibit; } + + uint32_t getResetReason() const override { + return esp_reset_reason(); + } + + // https://docs.espressif.com/projects/esp-idf/en/v4.4.7/esp32/api-reference/system/system.html + const char* getResetReasonString(uint32_t reason) { + switch (reason) { + case ESP_RST_UNKNOWN: + return "Unknown or first boot"; + case ESP_RST_POWERON: + return "Power-on reset"; + case ESP_RST_EXT: + return "External reset"; + case ESP_RST_SW: + return "Software reset"; + case ESP_RST_PANIC: + return "Panic / exception reset"; + case ESP_RST_INT_WDT: + return "Interrupt watchdog reset"; + case ESP_RST_TASK_WDT: + return "Task watchdog reset"; + case ESP_RST_WDT: + return "Other watchdog reset"; + case ESP_RST_DEEPSLEEP: + return "Wake from deep sleep"; + case ESP_RST_BROWNOUT: + return "Brownout (low voltage)"; + case ESP_RST_SDIO: + return "SDIO reset"; + default: + static char buf[40]; + snprintf(buf, sizeof(buf), "Unknown reset reason (%d)", reason); + return buf; + } + } }; class ESP32RTCClock : public mesh::RTCClock { From 4f9a0916714baa2a34ef294289bff78809056676 Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Wed, 18 Feb 2026 01:16:52 +0100 Subject: [PATCH 118/214] Use hardware channel activity detection for checking interference --- examples/companion_radio/MyMesh.cpp | 2 +- examples/simple_repeater/MyMesh.cpp | 2 +- examples/simple_room_server/MyMesh.cpp | 2 +- examples/simple_sensor/SensorMesh.cpp | 2 +- src/helpers/radiolib/RadioLibWrappers.cpp | 13 ++++++++++--- src/helpers/radiolib/RadioLibWrappers.h | 3 ++- 6 files changed, 16 insertions(+), 8 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 0b90df8b..2433f595 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -259,7 +259,7 @@ float MyMesh::getAirtimeBudgetFactor() const { } int MyMesh::getInterferenceThreshold() const { - return 0; // disabled for now, until currentRSSI() problem is resolved + return 1; // non-zero enables hardware CAD (Channel Activity Detection) before TX } int MyMesh::calcRxDelay(float score, uint32_t air_time) const { diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index af4706c1..dd282ec8 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -892,7 +892,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_max = 64; _prefs.flood_max_unscoped = 64; _prefs.flood_max_advert = 8; - _prefs.interference_threshold = 0; // disabled + _prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX // bridge defaults _prefs.bridge_enabled = 1; // enabled diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 349efb44..97ec80ac 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -649,7 +649,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_max = 64; _prefs.flood_max_unscoped = 64; _prefs.flood_max_advert = 8; - _prefs.interference_threshold = 0; // disabled + _prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX #ifdef ROOM_PASSWORD StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password)); #endif diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index c0235a14..2a2c7feb 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -725,7 +725,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise _prefs.flood_advert_interval = 0; // disabled _prefs.disable_fwd = true; _prefs.flood_max = 64; - _prefs.interference_threshold = 0; // disabled + _prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX // GPS defaults _prefs.gps_enabled = 0; diff --git a/src/helpers/radiolib/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp index b6519aef..c67ab768 100644 --- a/src/helpers/radiolib/RadioLibWrappers.cpp +++ b/src/helpers/radiolib/RadioLibWrappers.cpp @@ -178,10 +178,17 @@ void RadioLibWrapper::onSendFinished() { state = STATE_IDLE; } +int16_t RadioLibWrapper::performChannelScan() { + return _radio->scanChannel(); +} + bool RadioLibWrapper::isChannelActive() { - return _threshold == 0 - ? false // interference check is disabled - : getCurrentRSSI() > _noise_floor + _threshold; + if (_threshold == 0) return false; // interference check is disabled + + int16_t result = performChannelScan(); + // scanChannel() leaves radio in standby — restart RX regardless of result + startRecv(); + return (result == RADIOLIB_LORA_DETECTED); } float RadioLibWrapper::getLastRSSI() const { diff --git a/src/helpers/radiolib/RadioLibWrappers.h b/src/helpers/radiolib/RadioLibWrappers.h index efd3e179..c1ece644 100644 --- a/src/helpers/radiolib/RadioLibWrappers.h +++ b/src/helpers/radiolib/RadioLibWrappers.h @@ -32,7 +32,7 @@ public: bool isInRecvMode() const override; bool isChannelActive(); - bool isReceiving() override { + bool isReceiving() override { if (isReceivingPacket()) return true; return isChannelActive(); @@ -46,6 +46,7 @@ public: virtual uint8_t getSpreadingFactor() const { return LORA_SF; } static uint16_t preambleLengthForSF(uint8_t sf) { return sf <= 8 ? 32 : 16; } void updatePreamble(uint8_t sf) { _preamble_sf = sf; _radio->setPreambleLength(preambleLengthForSF(sf)); } + virtual int16_t performChannelScan(); int getNoiseFloor() const override { return _noise_floor; } void triggerNoiseFloorCalibrate(int threshold) override; From 813d108c7ab26e0b8e55087081ff84b9d62e91ba Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Sun, 22 Feb 2026 16:05:18 +0100 Subject: [PATCH 119/214] Also return busy if preamble detected --- src/helpers/radiolib/RadioLibWrappers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/radiolib/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp index c67ab768..9b00b176 100644 --- a/src/helpers/radiolib/RadioLibWrappers.cpp +++ b/src/helpers/radiolib/RadioLibWrappers.cpp @@ -188,7 +188,7 @@ bool RadioLibWrapper::isChannelActive() { int16_t result = performChannelScan(); // scanChannel() leaves radio in standby — restart RX regardless of result startRecv(); - return (result == RADIOLIB_LORA_DETECTED); + return (result == RADIOLIB_LORA_DETECTED || result == RADIOLIB_PREAMBLE_DETECTED); } float RadioLibWrapper::getLastRSSI() const { From 04a6c7009ab26c3af2c2e1550c07201139235dc2 Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Sun, 22 Feb 2026 16:08:04 +0100 Subject: [PATCH 120/214] Just check for not channel free --- src/helpers/radiolib/RadioLibWrappers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/radiolib/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp index 9b00b176..d50c2bac 100644 --- a/src/helpers/radiolib/RadioLibWrappers.cpp +++ b/src/helpers/radiolib/RadioLibWrappers.cpp @@ -188,7 +188,7 @@ bool RadioLibWrapper::isChannelActive() { int16_t result = performChannelScan(); // scanChannel() leaves radio in standby — restart RX regardless of result startRecv(); - return (result == RADIOLIB_LORA_DETECTED || result == RADIOLIB_PREAMBLE_DETECTED); + return result != RADIOLIB_CHANNEL_FREE; } float RadioLibWrapper::getLastRSSI() const { From 099ef6734843261d5a542570d8d1c3f35dced36c Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Fri, 6 Mar 2026 04:09:05 +0100 Subject: [PATCH 121/214] Prevent packet errors from growing --- src/helpers/radiolib/RadioLibWrappers.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/helpers/radiolib/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp index d50c2bac..5d68d7e9 100644 --- a/src/helpers/radiolib/RadioLibWrappers.cpp +++ b/src/helpers/radiolib/RadioLibWrappers.cpp @@ -186,7 +186,10 @@ bool RadioLibWrapper::isChannelActive() { if (_threshold == 0) return false; // interference check is disabled int16_t result = performChannelScan(); - // scanChannel() leaves radio in standby — restart RX regardless of result + // scanChannel() triggers DIO interrupt (CAD done) which sets STATE_INT_READY + // via setFlag() ISR. Clear it before restarting RX so recvRaw() doesn't + // try to read a non-existent packet and count a spurious recv error. + state = STATE_IDLE; startRecv(); return result != RADIOLIB_CHANNEL_FREE; } From 7c8e09245760c71a64f14ca46b4ff6b8dcdd3aa9 Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Wed, 3 Jun 2026 10:13:10 +0200 Subject: [PATCH 122/214] Have CAD be a separate toggle (set cad on/off) --- docs/cli_commands.md | 14 ++++++++++++++ examples/companion_radio/MyMesh.cpp | 5 ++++- examples/companion_radio/MyMesh.h | 1 + examples/simple_repeater/MyMesh.cpp | 3 ++- examples/simple_repeater/MyMesh.h | 3 +++ examples/simple_room_server/MyMesh.cpp | 3 ++- examples/simple_room_server/MyMesh.h | 3 +++ examples/simple_sensor/SensorMesh.cpp | 6 +++++- examples/simple_sensor/SensorMesh.h | 1 + src/Dispatcher.cpp | 1 + src/Dispatcher.h | 3 +++ src/helpers/CommonCLI.cpp | 13 +++++++++++-- src/helpers/CommonCLI.h | 1 + src/helpers/radiolib/RadioLibWrappers.cpp | 23 +++++++++++++++-------- src/helpers/radiolib/RadioLibWrappers.h | 2 ++ 15 files changed, 68 insertions(+), 14 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index ce0e7da1..c06f5e12 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -578,6 +578,20 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### Enable or disable hardware Channel Activity Detection (CAD) +**Usage:** +- `get cad` +- `set cad ` + +**Description:** When enabled, the radio performs a hardware Channel Activity Detection scan before transmitting and defers if the channel is busy. Runs independently of `int.thresh` — either, both, or none may be active. + +**Parameters:** +- `on|off`: Enable or disable hardware CAD + +**Default:** `off` + +--- + #### View or change the AGC Reset Interval **Usage:** - `get agc.reset.interval` diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 2433f595..5fb9bf9d 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -259,7 +259,10 @@ float MyMesh::getAirtimeBudgetFactor() const { } int MyMesh::getInterferenceThreshold() const { - return 1; // non-zero enables hardware CAD (Channel Activity Detection) before TX + return 0; // disabled for now, until currentRSSI() problem is resolved +} +bool MyMesh::getCADEnabled() const { + return true; // hardware CAD before TX (no CLI toggle on companion; enabled by default) } int MyMesh::calcRxDelay(float score, uint32_t air_time) const { diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 43d3950b..f4190f30 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -105,6 +105,7 @@ public: protected: float getAirtimeBudgetFactor() const override; int getInterferenceThreshold() const override; + bool getCADEnabled() const override; int calcRxDelay(float score, uint32_t air_time) const override; uint32_t getRetransmitDelay(const mesh::Packet *packet) override; uint32_t getDirectRetransmitDelay(const mesh::Packet *packet) override; diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index dd282ec8..5cc3a9a1 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -892,7 +892,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_max = 64; _prefs.flood_max_unscoped = 64; _prefs.flood_max_advert = 8; - _prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX + _prefs.interference_threshold = 0; // disabled + _prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on') // bridge defaults _prefs.bridge_enabled = 1; // enabled diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 7597c6c6..24c4b1f2 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -150,6 +150,9 @@ protected: int getInterferenceThreshold() const override { return _prefs.interference_threshold; } + bool getCADEnabled() const override { + return _prefs.cad_enabled; + } int getAGCResetInterval() const override { return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds } diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 97ec80ac..12d0b0c3 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -649,7 +649,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_max = 64; _prefs.flood_max_unscoped = 64; _prefs.flood_max_advert = 8; - _prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX + _prefs.interference_threshold = 0; // disabled + _prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on') #ifdef ROOM_PASSWORD StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password)); #endif diff --git a/examples/simple_room_server/MyMesh.h b/examples/simple_room_server/MyMesh.h index 5277ddad..e9e53ec9 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -144,6 +144,9 @@ protected: int getInterferenceThreshold() const override { return _prefs.interference_threshold; } + bool getCADEnabled() const override { + return _prefs.cad_enabled; + } int getAGCResetInterval() const override { return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds } diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index 2a2c7feb..59c9aa09 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -323,6 +323,9 @@ uint32_t SensorMesh::getDirectRetransmitDelay(const mesh::Packet* packet) { int SensorMesh::getInterferenceThreshold() const { return _prefs.interference_threshold; } +bool SensorMesh::getCADEnabled() const { + return _prefs.cad_enabled; +} int SensorMesh::getAGCResetInterval() const { return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds } @@ -725,7 +728,8 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise _prefs.flood_advert_interval = 0; // disabled _prefs.disable_fwd = true; _prefs.flood_max = 64; - _prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX + _prefs.interference_threshold = 0; // disabled + _prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on') // GPS defaults _prefs.gps_enabled = 0; diff --git a/examples/simple_sensor/SensorMesh.h b/examples/simple_sensor/SensorMesh.h index c9f135f6..1d65b877 100644 --- a/examples/simple_sensor/SensorMesh.h +++ b/examples/simple_sensor/SensorMesh.h @@ -120,6 +120,7 @@ protected: uint32_t getRetransmitDelay(const mesh::Packet* packet) override; uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override; int getInterferenceThreshold() const override; + bool getCADEnabled() const override; int getAGCResetInterval() const override; void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override; int searchPeersByHash(const uint8_t* hash) override; diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index 9d7a1113..c0610b7f 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -66,6 +66,7 @@ uint32_t Dispatcher::getCADFailMaxDuration() const { void Dispatcher::loop() { if (millisHasNowPassed(next_floor_calib_time)) { _radio->triggerNoiseFloorCalibrate(getInterferenceThreshold()); + _radio->setCADEnabled(getCADEnabled()); next_floor_calib_time = futureMillis(NOISE_FLOOR_CALIB_INTERVAL); } _radio->loop(); diff --git a/src/Dispatcher.h b/src/Dispatcher.h index dd032f13..aad6cba3 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -65,6 +65,8 @@ public: virtual void triggerNoiseFloorCalibrate(int threshold) { } + virtual void setCADEnabled(bool enable) { } + virtual void resetAGC() { } virtual bool isInRecvMode() const = 0; @@ -166,6 +168,7 @@ protected: virtual uint32_t getCADFailRetryDelay() const; virtual uint32_t getCADFailMaxDuration() const; virtual int getInterferenceThreshold() const { return 0; } // disabled by default + virtual bool getCADEnabled() const { return false; } // hardware CAD disabled by default virtual int getAGCResetInterval() const { return 0; } // disabled by default virtual unsigned long getDutyCycleWindowMs() const { return 3600000; } diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 5a5d1eab..82e53743 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -92,7 +92,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->flood_max_unscoped, sizeof(_prefs->flood_max_unscoped)); // 291 file.read((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292 file.read((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 293 - // next: 294 + file.read((uint8_t *)&_prefs->cad_enabled, sizeof(_prefs->cad_enabled)); // 294 + // next: 295 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -123,6 +124,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { // sanitise settings _prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean _prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean + _prefs->cad_enabled = constrain(_prefs->cad_enabled, 0, 1); // boolean file.close(); } @@ -187,7 +189,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->flood_max_unscoped, sizeof(_prefs->flood_max_unscoped)); // 291 file.write((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292 file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 293 - // next: 294 + file.write((uint8_t *)&_prefs->cad_enabled, sizeof(_prefs->cad_enabled)); // 294 + // next: 295 file.close(); } @@ -503,6 +506,10 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep _prefs->interference_threshold = atoi(&config[11]); savePrefs(); strcpy(reply, "OK"); + } else if (memcmp(config, "cad ", 4) == 0) { + _prefs->cad_enabled = memcmp(&config[4], "on", 2) == 0; + savePrefs(); + strcpy(reply, "OK"); } else if (memcmp(config, "agc.reset.interval ", 19) == 0) { _prefs->agc_reset_interval = atoi(&config[19]) / 4; savePrefs(); @@ -801,6 +808,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "> %s", StrHelper::ftoa(_prefs->airtime_factor)); } else if (memcmp(config, "int.thresh", 10) == 0) { sprintf(reply, "> %d", (uint32_t) _prefs->interference_threshold); + } else if (memcmp(config, "cad", 3) == 0) { + sprintf(reply, "> %s", _prefs->cad_enabled ? "on" : "off"); } else if (memcmp(config, "agc.reset.interval", 18) == 0) { sprintf(reply, "> %d", ((uint32_t) _prefs->agc_reset_interval) * 4); } else if (memcmp(config, "multi.acks", 10) == 0) { diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index ffeadc5b..10cb00c7 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -64,6 +64,7 @@ struct NodePrefs { // persisted to file uint8_t radio_fem_rxgain; // LoRa FEM RX gain setting uint8_t path_hash_mode; // which path mode to use when sending uint8_t loop_detect; + uint8_t cad_enabled; // hardware Channel Activity Detection before TX (boolean) }; class CommonCLICallbacks { diff --git a/src/helpers/radiolib/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp index 5d68d7e9..5e72336c 100644 --- a/src/helpers/radiolib/RadioLibWrappers.cpp +++ b/src/helpers/radiolib/RadioLibWrappers.cpp @@ -36,6 +36,7 @@ void RadioLibWrapper::begin() { _noise_floor = 0; _threshold = 0; + _cad_enabled = false; // start average out some samples _num_floor_samples = 0; @@ -183,15 +184,21 @@ int16_t RadioLibWrapper::performChannelScan() { } bool RadioLibWrapper::isChannelActive() { - if (_threshold == 0) return false; // interference check is disabled + // int.thresh: RSSI-based interference detection (relative to noise floor) + if (_threshold != 0 && getCurrentRSSI() > _noise_floor + _threshold) return true; - int16_t result = performChannelScan(); - // scanChannel() triggers DIO interrupt (CAD done) which sets STATE_INT_READY - // via setFlag() ISR. Clear it before restarting RX so recvRaw() doesn't - // try to read a non-existent packet and count a spurious recv error. - state = STATE_IDLE; - startRecv(); - return result != RADIOLIB_CHANNEL_FREE; + // cad: hardware channel activity detection + if (_cad_enabled) { + int16_t result = performChannelScan(); + // scanChannel() triggers DIO interrupt (CAD done) which sets STATE_INT_READY + // via setFlag() ISR. Clear it before restarting RX so recvRaw() doesn't + // try to read a non-existent packet and count a spurious recv error. + state = STATE_IDLE; + startRecv(); + if (result != RADIOLIB_CHANNEL_FREE) return true; + } + + return false; } float RadioLibWrapper::getLastRSSI() const { diff --git a/src/helpers/radiolib/RadioLibWrappers.h b/src/helpers/radiolib/RadioLibWrappers.h index c1ece644..9943bcab 100644 --- a/src/helpers/radiolib/RadioLibWrappers.h +++ b/src/helpers/radiolib/RadioLibWrappers.h @@ -9,6 +9,7 @@ protected: mesh::MainBoard* _board; uint32_t n_recv, n_sent, n_recv_errors; int16_t _noise_floor, _threshold; + bool _cad_enabled; uint16_t _num_floor_samples; int32_t _floor_sample_sum; uint8_t _preamble_sf; @@ -50,6 +51,7 @@ public: int getNoiseFloor() const override { return _noise_floor; } void triggerNoiseFloorCalibrate(int threshold) override; + void setCADEnabled(bool enable) override { _cad_enabled = enable; } void resetAGC() override; void loop() override; From dd9b3b32c39876ad6b45090e48ef185e3c83d461 Mon Sep 17 00:00:00 2001 From: taco Date: Mon, 15 Jun 2026 00:57:12 +1000 Subject: [PATCH 123/214] build fix for Heltec Mesh Solar and Mesh Pocket --- variants/heltec_mesh_solar/platformio.ini | 1 - variants/mesh_pocket/platformio.ini | 3 --- 2 files changed, 4 deletions(-) diff --git a/variants/heltec_mesh_solar/platformio.ini b/variants/heltec_mesh_solar/platformio.ini index 92d641a3..bd042322 100644 --- a/variants/heltec_mesh_solar/platformio.ini +++ b/variants/heltec_mesh_solar/platformio.ini @@ -1,7 +1,6 @@ [Heltec_mesh_solar] extends = nrf52_base board = heltec_mesh_solar -platform_packages = framework-arduinoadafruitnrf52 board_build.ldscript = boards/nrf52840_s140_v6.ld build_flags = ${nrf52_base.build_flags} -I src/helpers/nrf52 diff --git a/variants/mesh_pocket/platformio.ini b/variants/mesh_pocket/platformio.ini index 52a0d835..0d2a74ad 100644 --- a/variants/mesh_pocket/platformio.ini +++ b/variants/mesh_pocket/platformio.ini @@ -1,7 +1,6 @@ [Mesh_pocket] extends = nrf52_base board = heltec_mesh_pocket -platform_packages = framework-arduinoadafruitnrf52 board_build.ldscript = boards/nrf52840_s140_v6.ld build_flags = ${nrf52_base.build_flags} -I src/helpers/nrf52 @@ -32,7 +31,6 @@ lib_deps = stevemarple/MicroNMEA @ ^2.0.6 zinggjm/GxEPD2 @ 1.6.2 bakercp/CRC32 @ ^2.0.0 - debug_tool = jlink upload_protocol = nrfutil @@ -40,7 +38,6 @@ upload_protocol = nrfutil extends = Mesh_pocket build_src_filter = ${Mesh_pocket.build_src_filter} +<../examples/simple_repeater> - build_flags = ${Mesh_pocket.build_flags} -D ADVERT_NAME='"Heltec_Mesh_Pocket Repeater"' From 29dc93b184ae2ce7a2926b1e3c1b5e45c985976c Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 15 Jun 2026 15:56:13 -0700 Subject: [PATCH 124/214] Document Halo direct retry settings only --- docs/halo_keymind_settings.md | 361 ---------------------------------- docs/halo_settings.md | 136 +++++++++++++ 2 files changed, 136 insertions(+), 361 deletions(-) delete mode 100644 docs/halo_keymind_settings.md create mode 100644 docs/halo_settings.md diff --git a/docs/halo_keymind_settings.md b/docs/halo_keymind_settings.md deleted file mode 100644 index ca2c0803..00000000 --- a/docs/halo_keymind_settings.md +++ /dev/null @@ -1,361 +0,0 @@ -# Halo and Keymind Branch Settings - -This file covers only CLI settings and helper commands added by the Halo or -Keymind branches. Use `docs/cli_commands.md` for the general MeshCore CLI. - -## Quick Start - -```text -set retry.preset rooftop -set direct.retry.heard on -set flood.retry.advert off -set flood.retry.bridge off -set flood.retry.prefixes none -set flood.retry.ignore none -``` - -Then verify: - -```text -get retry.preset -get direct.retry.heard -get flood.retry.advert -get flood.retry.prefixes -get flood.retry.ignore -``` - -Use prefixes from the analyzer or neighbors list or `get recent.repeater` after the repeater has been online for a few hours. - -## Common Examples - -Disable retrying advert packets: - -```text -set flood.retry.advert off -get flood.retry.advert -``` - -Ignore a repeater as a successful flood retry echo: -Use this if you have a car repeater and a house repeater; have the house ignore the car. - -```text -set flood.retry.ignore 71CE82,C7618C -get flood.retry.ignore -``` - -Only accept specific downstream relays as flood retry success: -You're in a hole and need to hit a mountain top repeater to get out; keep trying till one you see one of these send out your packet. - -```text -set flood.retry.prefixes A58296,860CCA,425E5C -get flood.retry.prefixes -``` - -Bridge two groups of repeaters: - -```text -set flood.retry.bridge on -set flood.retry.bucket 1 71CE82,C7618C -set flood.retry.bucket 2 BEEBB0,425E5C -get flood.retry.bucket.1 -get flood.retry.bucket.2 -``` - -Return to simple non-bridge flood retry: - -```text -set flood.retry.bridge off -set flood.retry.prefixes none -set flood.retry.ignore none -``` - -## Added Settings - -| Setting | What it does | How to use | Example | -| --- | --- | --- | --- | -| `battery.alert` | Sends opt-in low-battery warnings to `#repeaters`. | `get battery.alert`, `set battery.alert on/off` | `set battery.alert on` | -| `battery.alert.low` | Warning threshold percentage. Must be greater than `battery.alert.critical`. | `get battery.alert.low`, `set battery.alert.low <1-100>` | `set battery.alert.low 20` | -| `battery.alert.critical` | Critical threshold percentage. Critical warnings repeat more often. | `get battery.alert.critical`, `set battery.alert.critical <0-99>` | `set battery.alert.critical 10` | -| `recent.repeater` | Shows, seeds, or clears the recent repeater prefix/SNR table used by direct retry and bridge freshness checks. | `get recent.repeater`, `get recent.repeater `, `set recent.repeater `, `clear recent.repeater` | `set recent.repeater A1B2C3 -8.5` | -| `outpath` | Overrides the primary direct route used for replies to the current remote client. | `get outpath`, `set outpath `, `set outpath direct`, `set outpath clear`, `set outpath flood` | `set outpath A1B2C3,D4E5F6` | -| `altpath` | Optional second direct route used for duplicate response attempts to the current remote client. | `get altpath`, `set altpath `, `set altpath clear` | `set altpath A1B2C3,D4E5F6` | - -## Other Keymind Commands - -| Command | What it does | How to use | Example | -| --- | --- | --- | --- | -| `send text.flood` | Sends a `#repeaters` flood text message formatted as `: `. | `send text.flood ` | `send text.flood checking ridge link` | - -## Battery Alerts - -Battery alerts are off by default. When enabled, the repeater checks once per -minute and sends a flood text warning to `#repeaters` when voltage is above -`1 V` and the estimated battery percent is below `battery.alert.low`. - -Warnings repeat every `24` hours, or every `12` hours when the estimate is -below `battery.alert.critical`. - -Defaults: - -| Setting | Default | -| --- | ---: | -| `battery.alert` | `off` | -| `battery.alert.low` | `20` | -| `battery.alert.critical` | `10` | - -Example: - -```text -set battery.alert.low 20 -set battery.alert.critical 10 -set battery.alert on -get battery.alert -``` - -## Recent Repeater Table - -Direct retry uses the recent repeater table when `direct.retry.heard` is `on`. -Bridge buckets also use this table: a configured bucket prefix is active only -when it was heard within the last hour. - -Show learned rows: - -```text -get recent.repeater -get recent.repeater 2 -get recent.repeater page 3 -``` - -Seed or correct a prefix: - -```text -set recent.repeater A1B2C3 8.5 -``` - -Clear learned and manually seeded rows: - -```text -clear recent.repeater -``` - -Rows are sorted by prefix width, then SNR. A full direct retry failure lowers -the matching row by `0.25 dB`. - -Serial CLI pages contain up to `128` rows. Remote LoRa CLI pages contain up to -`7` rows. - -## Direct Path Overrides - -`outpath` and `altpath` apply to the current remote client ACL entry. They need -remote client context, so they are not useful from the local serial CLI. - -Set paths with comma-separated hop hashes. Each hop must be `2`, `4`, or `6` -hex characters, and all hops in one path must use the same width. - -```text -get outpath -set outpath A1B2C3,D4E5F6 -set outpath direct -set outpath clear -set outpath flood - -get altpath -set altpath A1B2C3,D4E5F6 -set altpath clear -``` - -`set outpath direct` sets a zero-hop direct route for a client reachable without -repeaters. `set outpath clear` forgets the override and lets normal path -discovery fill it again. `set outpath flood` forces replies to use flood packets -until the client logs in again. `altpath` sends a duplicate reply over a second -direct route; clearing it returns replies to a single route. - -## Direct Retry Settings - -Direct retry applies to direct-routed packets. A queued resend is canceled when the next-hop echo is heard. - -| Setting | What it does | How to use | Example | -| --- | --- | --- | --- | -| `retry.preset` | Applies shared direct and flood retry defaults. Values: `infra`, `rooftop`, `mobile` or `0`, `1`, `2`. | `get retry.preset`, `set retry.preset ` | `set retry.preset rooftop` | -| `direct.retry.heard` | Uses the recent repeater table as the direct retry eligibility gate. | `get direct.retry.heard`, `set direct.retry.heard on/off` | `set direct.retry.heard on` | -| `direct.retry.margin` | SNR margin in dB above the SF-specific receive floor. | `get direct.retry.margin`, `set direct.retry.margin <0-40>` | `set direct.retry.margin 5` | -| `direct.retry.count` | Maximum direct retry attempts after initial TX. | `get direct.retry.count`, `set direct.retry.count <1-15>` | `set direct.retry.count 15` | -| `direct.retry.base` | Base wait in milliseconds before retry. | `get direct.retry.base`, `set direct.retry.base <10-5000>` | `set direct.retry.base 175` | -| `direct.retry.step` | Milliseconds added per retry attempt. | `get direct.retry.step`, `set direct.retry.step <0-5000>` | `set direct.retry.step 100` | -| `direct.retry.cr` | Adaptive coding-rate thresholds for direct retry packets. Uses `CR4`, `CR5`, `CR7`, or `CR8`; `CR6` is never selected. | `get direct.retry.cr`, `set direct.retry.cr ,,,`, `set direct.retry.cr off` | `set direct.retry.cr 10.0,7.5,2.5,0` | - -The default adaptive coding-rate profile is `10.0,7.5,2.5,2.5`. -SNR `10.0 dB` and up uses `CR4`, `7.5 dB` and up uses `CR5`, -`2.5 dB` and down uses `CR8`, and the middle band uses `CR7`. If no -recent repeater table entry is available, retry packets use `CR5`. Use -`set direct.retry.cr off` to disable adaptive coding-rate overrides. If -adaptive selection chooses `CR4`, retries after the third attempt use -`CR5`. - -Preset details: - -| Preset | Base | Count | Step | SNR gate | -| --- | ---: | ---: | ---: | --- | -| `infra` | `275 ms` | `4` | `150 ms` | SF floor + `15 dB` | -| `rooftop` | `175 ms` | `15` | `100 ms` | SF floor + `5 dB` | -| `mobile` | `175 ms` | `15` | `50 ms` | SF floor | - -Example for a quiet fixed repeater: - -```text -set retry.preset rooftop -set direct.retry.heard on -set direct.retry.margin 5 -``` - -Example for a moving or weak-link node: - -```text -set retry.preset mobile -set direct.retry.margin 0 -``` - -## Flood And Advert Settings - -Flood retry applies to flood-routed packets. A queued retry is canceled when a qualifying downstream echo is heard. - -| Setting | What it does | How to use | Example | -| --- | --- | --- | --- | -| `flood.retry.count` | Maximum flood retry attempts after initial TX. `0` disables flood retry. | `get flood.retry.count`, `set flood.retry.count <0-15>` | `set flood.retry.count 7` | -| `flood.retry.path` | Maximum path hash count eligible for flood retry, or `off` to disable the gate. | `get flood.retry.path`, `set flood.retry.path <0-63/off>` | `set flood.retry.path 1` | -| `flood.retry.advert` | Allows or blocks retry for node advert packets (`type=4`). Default is `off`. | `get flood.retry.advert`, `set flood.retry.advert on/off` | `set flood.retry.advert off` | -| `flood.retry.prefixes` | Target prefixes. If set, only matching downstream echoes cancel a retry. | `get flood.retry.prefixes`, `set flood.retry.prefixes ` | `set flood.retry.prefixes BEEBB0,425E5C` | -| `flood.retry.ignore` | Ignored prefixes. In non-bridge retry, ignored last-hop echoes do not cancel retry. | `get flood.retry.ignore`, `set flood.retry.ignore ` | `set flood.retry.ignore 71CE82,C7618C` | -| `flood.retry.bridge` | Enables bucket-based bridge retry logic. | `get flood.retry.bridge`, `set flood.retry.bridge on/off` | `set flood.retry.bridge on` | -| `flood.retry.bucket.` | Shows one bridge bucket. Buckets are numbered `1`-`6`. | `get flood.retry.bucket.` | `get flood.retry.bucket.1` | -| `flood.retry.bucket` | Sets bridge bucket prefixes. | `set flood.retry.bucket <1-6> ` | `set flood.retry.bucket 1 71CE82,C7618C` | - -The shared retry preset sets these flood defaults: - -| Preset | Retry count | Path gate | -| --- | ---: | ---: | -| `infra` | `1` | `1` | -| `rooftop` | `3` | `2` | -| `mobile` | `3` | `1` | - -Example for path-gated retry: - -```text -set retry.preset rooftop -set flood.retry.path 1 -set flood.retry.advert off -set flood.retry.ignore 71CE82,C7618C -``` - -## North South Buckets - -Buckets describe groups of repeaters on different sides of this relay. Bucket -numbers do not have built-in meanings; this example uses bucket `1` for North -and bucket `2` for South. - -```text - North bucket 1 - +-----------------------+ - | A1B2C3 D4E5F6 | - | North A North B | - +-----------+-----------+ - | - v - +-----------+ - | This node | - +-----------+ - ^ - | - +-----------+-----------+ - | 71CE82 C7618C | - | South A South B | - +-----------------------+ - South bucket 2 -``` - -Configure the buckets: - -```text -set flood.retry.bridge on -set flood.retry.bucket 1 A1B2C3,D4E5F6 -set flood.retry.bucket 2 71CE82,C7618C -set flood.retry.ignore none -``` - -Packet heard from the North: - -```text - heard source - | - v - +--------------+ retry targets - | North bucket | -----> South bucket - | bucket 1 | -----> Other fresh/unbucketed relays - +--------------+ -``` - -Packet heard from the South: - -```text - heard source - | - v - +--------------+ retry targets - | South bucket | -----> North bucket - | bucket 2 | -----> Other fresh/unbucketed relays - +--------------+ -``` - -Packet heard from an unbucketed or pathless source: - -```text - heard source - | - v - +--------------+ retry targets - | Other bucket | -----> North bucket - | implicit | -----> South bucket - +--------------+ -``` - -Bridge retry stays eligible until every target bucket has been heard or -`flood.retry.count` is exhausted. A configured bucket is a target only when at -least one of its prefixes is fresh in `recent.repeater`. Prefixes in -`flood.retry.ignore` never count as bucket hits. - -## Troubleshooting - -If advert packets are still retrying: - -```text -get flood.retry.advert -set flood.retry.advert off -``` - -If ignored prefixes still appear in `flood retry good` logs: - -```text -get flood.retry.ignore -set flood.retry.ignore -``` - -The ignored prefix must match the last hop shown as `heard=`. For example, this log needs `C7618C` in the ignore list: - -```text -flood retry good (... path=7773D0>C7618C, heard=C7618C ...) -``` - -If retries are too aggressive: - -```text -set flood.retry.count 1 -set flood.retry.path 1 -set direct.retry.count 4 -``` - -If retries are too sparse: - -```text -set flood.retry.count 7 -set flood.retry.path 2 -``` diff --git a/docs/halo_settings.md b/docs/halo_settings.md new file mode 100644 index 00000000..9324487d --- /dev/null +++ b/docs/halo_settings.md @@ -0,0 +1,136 @@ +# Halo Direct Message Retry Settings + +This file covers only CLI settings and helper commands added for Halo direct-message retry behavior. Use `docs/cli_commands.md` for the general MeshCore CLI. + +Halo retry applies to direct-routed packets. A queued resend is canceled when the next-hop echo is heard. + +## Quick Start + +```text +set retry.preset rooftop +set direct.retry.heard on +get retry.preset +get direct.retry.heard +get direct.retry.count +get direct.retry.base +get direct.retry.step +``` + +Use prefixes from the analyzer, neighbors list, or `get recent.repeater` after the repeater has been online for a few hours. + +## Added Halo Settings + +| Setting | What it does | How to use | Example | +| --- | --- | --- | --- | +| `recent.repeater` | Shows, seeds, or clears the recent repeater prefix/SNR table used by direct retry. | `get recent.repeater`, `get recent.repeater `, `set recent.repeater `, `clear recent.repeater` | `set recent.repeater A1B2C3 -8.5` | +| `outpath` | Overrides the primary direct route used for replies to the current remote client. | `get outpath`, `set outpath `, `set outpath direct`, `set outpath clear`, `set outpath flood` | `set outpath A1B2C3,D4E5F6` | +| `altpath` | Optional second direct route used for duplicate response attempts to the current remote client. | `get altpath`, `set altpath `, `set altpath clear` | `set altpath A1B2C3,D4E5F6` | +| `retry.preset` | Applies direct retry defaults. Values: `infra`, `rooftop`, `mobile` or `0`, `1`, `2`. | `get retry.preset`, `set retry.preset ` | `set retry.preset rooftop` | +| `direct.retry.heard` | Uses the recent repeater table as the direct retry eligibility gate. | `get direct.retry.heard`, `set direct.retry.heard on/off` | `set direct.retry.heard on` | +| `direct.retry.margin` | SNR margin in dB above the SF-specific receive floor. | `get direct.retry.margin`, `set direct.retry.margin <0-40>` | `set direct.retry.margin 5` | +| `direct.retry.count` | Maximum direct retry attempts after initial TX. | `get direct.retry.count`, `set direct.retry.count <1-15>` | `set direct.retry.count 15` | +| `direct.retry.base` | Base wait in milliseconds before retry. | `get direct.retry.base`, `set direct.retry.base <10-5000>` | `set direct.retry.base 175` | +| `direct.retry.step` | Milliseconds added per retry attempt. | `get direct.retry.step`, `set direct.retry.step <0-5000>` | `set direct.retry.step 100` | +| `direct.retry.cr` | Adaptive coding-rate thresholds for direct retry packets. Uses `CR4`, `CR5`, `CR7`, or `CR8`; `CR6` is never selected. | `get direct.retry.cr`, `set direct.retry.cr ,,,`, `set direct.retry.cr off` | `set direct.retry.cr 10.0,7.5,2.5,0` | + +## Recent Repeater Table + +Direct retry uses the recent repeater table when `direct.retry.heard` is `on`. + +Show learned rows: + +```text +get recent.repeater +get recent.repeater 2 +get recent.repeater page 3 +``` + +Seed or correct a prefix: + +```text +set recent.repeater A1B2C3 8.5 +``` + +Clear learned and manually seeded rows: + +```text +clear recent.repeater +``` + +Rows are sorted by prefix width, then SNR. A full direct retry failure lowers the matching row by `0.25 dB`. + +Serial CLI pages contain up to `128` rows. Remote LoRa CLI pages contain up to `7` rows. + +## Direct Path Overrides + +`outpath` and `altpath` apply to the current remote client ACL entry. They need remote client context, so they are not useful from the local serial CLI. + +Set paths with comma-separated hop hashes. Each hop must be `2`, `4`, or `6` hex characters, and all hops in one path must use the same width. + +```text +get outpath +set outpath A1B2C3,D4E5F6 +set outpath direct +set outpath clear +set outpath flood + +get altpath +set altpath A1B2C3,D4E5F6 +set altpath clear +``` + +`set outpath direct` sets a zero-hop direct route for a client reachable without repeaters. `set outpath clear` forgets the override and lets normal path discovery fill it again. `set outpath flood` forces replies to use flood packets until the client logs in again. `altpath` sends a duplicate reply over a second direct route; clearing it returns replies to a single route. + +## Direct Retry Details + +The default adaptive coding-rate profile is `10.0,7.5,2.5,2.5`. SNR `10.0 dB` and up uses `CR4`, `7.5 dB` and up uses `CR5`, `2.5 dB` and down uses `CR8`, and the middle band uses `CR7`. If no recent repeater table entry is available, retry packets use `CR5`. + +Use `set direct.retry.cr off` to disable adaptive coding-rate overrides. If adaptive selection chooses `CR4`, retries after the third attempt use `CR5`. + +Preset details: + +| Preset | Base | Count | Step | SNR gate | +| --- | ---: | ---: | ---: | --- | +| `infra` | `275 ms` | `4` | `150 ms` | SF floor + `15 dB` | +| `rooftop` | `175 ms` | `15` | `100 ms` | SF floor + `5 dB` | +| `mobile` | `175 ms` | `15` | `50 ms` | SF floor | + +Example for a quiet fixed repeater: + +```text +set retry.preset rooftop +set direct.retry.heard on +set direct.retry.margin 5 +``` + +Example for a moving or weak-link node: + +```text +set retry.preset mobile +set direct.retry.margin 0 +``` + +## Troubleshooting + +If direct retries are too aggressive: + +```text +set direct.retry.count 4 +set direct.retry.margin 10 +``` + +If direct retries are too sparse: + +```text +set direct.retry.count 15 +set direct.retry.margin 0 +``` + +If direct retry is skipping a path you expect it to retry: + +```text +get direct.retry.heard +get recent.repeater +``` + +Either disable the heard gate with `set direct.retry.heard off`, or seed the next-hop prefix with `set recent.repeater `. From 17dc51dbcea17c7d210b5819b24db7fb92af414b Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 15 Jun 2026 15:58:37 -0700 Subject: [PATCH 125/214] Remove build and variant changes from Halo retry branch --- boards/nrf52840_s140_v6.ld | 15 +---- boards/nrf52840_s140_v6_extrafs.ld | 15 +---- boards/nrf52840_s140_v7.ld | 15 +---- boards/nrf52840_s140_v7_extrafs.ld | 12 +--- build.sh | 7 +-- variants/gat562_30s_mesh_kit/platformio.ini | 3 +- variants/gat562_mesh_evb_pro/platformio.ini | 2 +- .../gat562_mesh_tracker_pro/platformio.ini | 3 +- variants/gat562_mesh_watch13/platformio.ini | 2 +- variants/heltec_ct62/platformio.ini | 4 -- variants/heltec_e213/HeltecE213Board.cpp | 27 +++++++++ variants/heltec_e213/HeltecE213Board.h | 3 + variants/heltec_e290/HeltecE290Board.cpp | 27 +++++++++ variants/heltec_e290/HeltecE290Board.h | 3 + variants/heltec_t096/LoRaFEMControl.h | 2 +- variants/heltec_t096/platformio.ini | 10 +--- variants/heltec_t114/platformio.ini | 2 +- variants/heltec_t190/HeltecT190Board.cpp | 27 +++++++++ variants/heltec_t190/HeltecT190Board.h | 3 + variants/heltec_tracker/platformio.ini | 4 -- .../HeltecTrackerV2Board.cpp | 31 ++++++++-- .../heltec_tracker_v2/HeltecTrackerV2Board.h | 2 + variants/heltec_tracker_v2/LoRaFEMControl.h | 2 +- variants/heltec_tracker_v2/platformio.ini | 4 -- variants/heltec_v2/HeltecV2Board.h | 25 +++++++++ variants/heltec_v2/platformio.ini | 4 -- variants/heltec_v3/HeltecV3Board.h | 29 ++++++++++ variants/heltec_v3/platformio.ini | 8 --- variants/heltec_v4/HeltecV4Board.h | 2 + variants/heltec_v4/LoRaFEMControl.h | 2 +- variants/heltec_v4/platformio.ini | 18 ------ variants/heltec_wireless_paper/platformio.ini | 4 -- variants/lilygo_t3s3/platformio.ini | 4 -- variants/lilygo_tbeam_1w/platformio.ini | 5 -- variants/lilygo_tbeam_SX1262/platformio.ini | 7 --- variants/lilygo_tbeam_SX1276/platformio.ini | 4 -- .../platformio.ini | 4 -- variants/lilygo_tdeck/TDeckBoard.h | 24 ++++++++ variants/lilygo_tlora_v2_1/platformio.ini | 6 +- variants/promicro/platformio.ini | 1 - variants/rak3112/RAK3112Board.h | 29 ++++++++++ variants/rak3401/platformio.ini | 3 +- variants/rak4631/platformio.ini | 3 +- variants/rak_wismesh_tag/platformio.ini | 1 - variants/sensecap_solar/platformio.ini | 2 +- variants/station_g2/StationG2Board.h | 24 ++++++++ variants/station_g2/platformio.ini | 5 ++ variants/station_g2/target.cpp | 15 ----- variants/station_g2/target.h | 3 - variants/thinknode_m2/ThinknodeM2Board.cpp | 56 +++++++++++-------- variants/thinknode_m2/ThinknodeM2Board.h | 4 ++ variants/thinknode_m5/ThinknodeM5Board.cpp | 8 +++ variants/thinknode_m5/ThinknodeM5Board.h | 3 + variants/xiao_c3/XiaoC3Board.h | 32 +++++++++++ variants/xiao_c3/platformio.ini | 5 -- variants/xiao_c6/platformio.ini | 1 - variants/xiao_nrf52/platformio.ini | 2 +- variants/xiao_s3/platformio.ini | 4 -- variants/xiao_s3_wio/platformio.ini | 4 -- 59 files changed, 362 insertions(+), 214 deletions(-) diff --git a/boards/nrf52840_s140_v6.ld b/boards/nrf52840_s140_v6.ld index d0c7d1dc..6dad975b 100644 --- a/boards/nrf52840_s140_v6.ld +++ b/boards/nrf52840_s140_v6.ld @@ -7,9 +7,6 @@ MEMORY { FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0xED000 - 0x26000 - /* To keep data in RAM across resets */ - PERSISTENT_RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 8 - /* SRAM required by Softdevice depend on * - Attribute Table Size (Number of Services and Characteristics) * - Vendor UUID count @@ -17,19 +14,11 @@ MEMORY * - Concurrent connection peripheral + central + secure links * - Event Len, HVN queue, Write CMD queue */ - RAM (rwx) : ORIGIN = 0x20006000 + 8, LENGTH = 0x20040000 - 0x20006000 - 8 + RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 0x20040000 - 0x20006000 } SECTIONS { - . = ALIGN(4); - .persistent (NOLOAD) : - { - KEEP(*(.persistent_magic)) - KEEP(*(.persistent_data)) - . = ALIGN(4); - } > PERSISTENT_RAM - . = ALIGN(4); .svc_data : { @@ -44,6 +33,6 @@ SECTIONS KEEP(*(.fs_data)) PROVIDE(__stop_fs_data = .); } > RAM -} +} INSERT AFTER .data; INCLUDE "nrf52_common.ld" diff --git a/boards/nrf52840_s140_v6_extrafs.ld b/boards/nrf52840_s140_v6_extrafs.ld index bd454747..35261067 100644 --- a/boards/nrf52840_s140_v6_extrafs.ld +++ b/boards/nrf52840_s140_v6_extrafs.ld @@ -7,9 +7,6 @@ MEMORY { FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0xD4000 - 0x26000 - /* To keep data in RAM across resets */ - PERSISTENT_RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 8 - /* SRAM required by Softdevice depend on * - Attribute Table Size (Number of Services and Characteristics) * - Vendor UUID count @@ -17,19 +14,11 @@ MEMORY * - Concurrent connection peripheral + central + secure links * - Event Len, HVN queue, Write CMD queue */ - RAM (rwx) : ORIGIN = 0x20006000 + 8, LENGTH = 0x20040000 - 0x20006000 - 8 + RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 0x20040000 - 0x20006000 } SECTIONS { - . = ALIGN(4); - .persistent (NOLOAD) : - { - KEEP(*(.persistent_magic)) - KEEP(*(.persistent_data)) - . = ALIGN(4); - } > PERSISTENT_RAM - . = ALIGN(4); .svc_data : { @@ -44,6 +33,6 @@ SECTIONS KEEP(*(.fs_data)) PROVIDE(__stop_fs_data = .); } > RAM -} +} INSERT AFTER .data; INCLUDE "nrf52_common.ld" diff --git a/boards/nrf52840_s140_v7.ld b/boards/nrf52840_s140_v7.ld index 2333238f..6aaeb403 100644 --- a/boards/nrf52840_s140_v7.ld +++ b/boards/nrf52840_s140_v7.ld @@ -7,9 +7,6 @@ MEMORY { FLASH (rx) : ORIGIN = 0x27000, LENGTH = 0xED000 - 0x27000 - /* To keep data in RAM across resets */ - PERSISTENT_RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 8 - /* SRAM required by Softdevice depend on * - Attribute Table Size (Number of Services and Characteristics) * - Vendor UUID count @@ -17,19 +14,11 @@ MEMORY * - Concurrent connection peripheral + central + secure links * - Event Len, HVN queue, Write CMD queue */ - RAM (rwx) : ORIGIN = 0x20006000 + 8, LENGTH = 0x20040000 - 0x20006000 - 8 + RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 0x20040000 - 0x20006000 } SECTIONS { - . = ALIGN(4); - .persistent (NOLOAD) : - { - KEEP(*(.persistent_magic)) - KEEP(*(.persistent_data)) - . = ALIGN(4); - } > PERSISTENT_RAM - . = ALIGN(4); .svc_data : { @@ -44,6 +33,6 @@ SECTIONS KEEP(*(.fs_data)) PROVIDE(__stop_fs_data = .); } > RAM -} +} INSERT AFTER .data; INCLUDE "nrf52_common.ld" diff --git a/boards/nrf52840_s140_v7_extrafs.ld b/boards/nrf52840_s140_v7_extrafs.ld index 48348188..5956183a 100644 --- a/boards/nrf52840_s140_v7_extrafs.ld +++ b/boards/nrf52840_s140_v7_extrafs.ld @@ -14,19 +14,11 @@ MEMORY * - Concurrent connection peripheral + central + secure links * - Event Len, HVN queue, Write CMD queue */ - RAM (rwx) : ORIGIN = 0x20006000 + 8, LENGTH = 0x20040000 - 0x20006000 - 8 + RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 0x20040000 - 0x20006000 } SECTIONS { - . = ALIGN(4); - .persistent (NOLOAD) : - { - KEEP(*(.persistent_magic)) - KEEP(*(.persistent_data)) - . = ALIGN(4); - } > PERSISTENT_RAM - . = ALIGN(4); .svc_data : { @@ -41,6 +33,6 @@ SECTIONS KEEP(*(.fs_data)) PROVIDE(__stop_fs_data = .); } > RAM -} +} INSERT AFTER .data; INCLUDE "nrf52_common.ld" diff --git a/build.sh b/build.sh index 006eae96..313c4c47 100755 --- a/build.sh +++ b/build.sh @@ -134,8 +134,7 @@ build_firmware() { # set firmware version string # e.g: v1.0.0-abcdef - # FIRMWARE_VERSION_STRING="${FIRMWARE_VERSION}-${COMMIT_HASH}" - FIRMWARE_VERSION_STRING="${FIRMWARE_VERSION}" + FIRMWARE_VERSION_STRING="${FIRMWARE_VERSION}-${COMMIT_HASH}" # craft filename # e.g: RAK_4631_Repeater-v1.0.0-SHA @@ -153,8 +152,8 @@ build_firmware() { # build merge-bin for esp32 fresh install, copy .bins to out folder (e.g: Heltec_v3_room_server-v1.0.0-SHA.bin) if [ "$ENV_PLATFORM" == "ESP32_PLATFORM" ]; then pio run -t mergebin -e $1 - cp .pio/build/$1/firmware.bin out/${FIRMWARE_FILENAME}-upgrade.bin 2>/dev/null || true - cp .pio/build/$1/firmware-merged.bin out/${FIRMWARE_FILENAME}-freshInstall-merged.bin 2>/dev/null || true + cp .pio/build/$1/firmware.bin out/${FIRMWARE_FILENAME}.bin 2>/dev/null || true + cp .pio/build/$1/firmware-merged.bin out/${FIRMWARE_FILENAME}-merged.bin 2>/dev/null || true fi # build .uf2 for nrf52 boards, copy .uf2 and .zip to out folder (e.g: RAK_4631_Repeater-v1.0.0-SHA.uf2) diff --git a/variants/gat562_30s_mesh_kit/platformio.ini b/variants/gat562_30s_mesh_kit/platformio.ini index aa3915a4..2baac256 100644 --- a/variants/gat562_30s_mesh_kit/platformio.ini +++ b/variants/gat562_30s_mesh_kit/platformio.ini @@ -2,13 +2,12 @@ extends = nrf52_base board = rak4631 board_check = true -board_build.ldscript = boards/nrf52840_s140_v6.ld build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I variants/gat562_30s_mesh_kit -D RAK_4631 -D RAK_BOARD -; -D NRF52_POWER_MANAGEMENT + -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D PIN_OLED_RESET=-1 diff --git a/variants/gat562_mesh_evb_pro/platformio.ini b/variants/gat562_mesh_evb_pro/platformio.ini index d7de585a..b3e89417 100644 --- a/variants/gat562_mesh_evb_pro/platformio.ini +++ b/variants/gat562_mesh_evb_pro/platformio.ini @@ -5,7 +5,7 @@ board_check = true build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I variants/gat562_mesh_evb_pro -; -D NRF52_POWER_MANAGEMENT + -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D RADIO_CLASS=CustomSX1262 diff --git a/variants/gat562_mesh_tracker_pro/platformio.ini b/variants/gat562_mesh_tracker_pro/platformio.ini index 78ec7d01..af153b8f 100644 --- a/variants/gat562_mesh_tracker_pro/platformio.ini +++ b/variants/gat562_mesh_tracker_pro/platformio.ini @@ -2,11 +2,10 @@ extends = nrf52_base board = rak4631 board_check = true -board_build.ldscript = boards/nrf52840_s140_v6.ld build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I variants/gat562_mesh_tracker_pro -; -D NRF52_POWER_MANAGEMENT + -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D PIN_OLED_RESET=-1 diff --git a/variants/gat562_mesh_watch13/platformio.ini b/variants/gat562_mesh_watch13/platformio.ini index f457424f..f3510b74 100644 --- a/variants/gat562_mesh_watch13/platformio.ini +++ b/variants/gat562_mesh_watch13/platformio.ini @@ -8,7 +8,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/gat562_mesh_watch13 -D RAK_4631 -D RAK_BOARD -; -D NRF52_POWER_MANAGEMENT + -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D PIN_OLED_RESET=-1 diff --git a/variants/heltec_ct62/platformio.ini b/variants/heltec_ct62/platformio.ini index e8becc7e..0179d965 100644 --- a/variants/heltec_ct62/platformio.ini +++ b/variants/heltec_ct62/platformio.ini @@ -130,10 +130,6 @@ lib_deps = ${esp32_ota.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_ct62_companion_radio_ble_ps] -extends = env:Heltec_ct62_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 - [env:Heltec_ct62_sensor] extends = Heltec_ct62 build_flags = diff --git a/variants/heltec_e213/HeltecE213Board.cpp b/variants/heltec_e213/HeltecE213Board.cpp index 88737c4d..af115318 100644 --- a/variants/heltec_e213/HeltecE213Board.cpp +++ b/variants/heltec_e213/HeltecE213Board.cpp @@ -20,6 +20,33 @@ void HeltecE213Board::begin() { } } + void HeltecE213Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + + // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep + rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); + + rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); + + if (pin_wake_btn < 0) { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet + } else { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn + } + + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000); + } + + // Finally set ESP32 into sleep + esp_deep_sleep_start(); // CPU halts here and never returns! + } + + void HeltecE213Board::powerOff() { + enterDeepSleep(0); + } + uint16_t HeltecE213Board::getBattMilliVolts() { analogReadResolution(10); digitalWrite(PIN_ADC_CTRL, HIGH); diff --git a/variants/heltec_e213/HeltecE213Board.h b/variants/heltec_e213/HeltecE213Board.h index fadc038f..2192c141 100644 --- a/variants/heltec_e213/HeltecE213Board.h +++ b/variants/heltec_e213/HeltecE213Board.h @@ -3,6 +3,7 @@ #include #include #include +#include class HeltecE213Board : public ESP32Board { @@ -12,6 +13,8 @@ public: HeltecE213Board() : periph_power(PIN_VEXT_EN,PIN_VEXT_EN_ACTIVE) { } void begin(); + void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); + void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; }; diff --git a/variants/heltec_e290/HeltecE290Board.cpp b/variants/heltec_e290/HeltecE290Board.cpp index 96ec59c9..3994a206 100644 --- a/variants/heltec_e290/HeltecE290Board.cpp +++ b/variants/heltec_e290/HeltecE290Board.cpp @@ -20,6 +20,33 @@ void HeltecE290Board::begin() { } } + void HeltecE290Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + + // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep + rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); + + rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); + + if (pin_wake_btn < 0) { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet + } else { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn + } + + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000); + } + + // Finally set ESP32 into sleep + esp_deep_sleep_start(); // CPU halts here and never returns! + } + + void HeltecE290Board::powerOff() { + enterDeepSleep(0); + } + uint16_t HeltecE290Board::getBattMilliVolts() { analogReadResolution(10); digitalWrite(PIN_ADC_CTRL, HIGH); diff --git a/variants/heltec_e290/HeltecE290Board.h b/variants/heltec_e290/HeltecE290Board.h index f287227c..645ec348 100644 --- a/variants/heltec_e290/HeltecE290Board.h +++ b/variants/heltec_e290/HeltecE290Board.h @@ -3,6 +3,7 @@ #include #include #include +#include class HeltecE290Board : public ESP32Board { @@ -12,6 +13,8 @@ public: HeltecE290Board() : periph_power(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE) { } void begin(); + void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); + void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; diff --git a/variants/heltec_t096/LoRaFEMControl.h b/variants/heltec_t096/LoRaFEMControl.h index a3b5c4ed..0ce60fff 100644 --- a/variants/heltec_t096/LoRaFEMControl.h +++ b/variants/heltec_t096/LoRaFEMControl.h @@ -17,6 +17,6 @@ class LoRaFEMControl bool isLNAEnabled(void) const { return lna_enabled; } private: - bool lna_enabled = true; + bool lna_enabled = false; bool lna_can_control = false; }; diff --git a/variants/heltec_t096/platformio.ini b/variants/heltec_t096/platformio.ini index 5f17436a..e820bf58 100644 --- a/variants/heltec_t096/platformio.ini +++ b/variants/heltec_t096/platformio.ini @@ -9,7 +9,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/heltec_t096 -I src/helpers/ui -D HELTEC_T096 -; -D NRF52_POWER_MANAGEMENT + -D NRF52_POWER_MANAGEMENT -D P_LORA_DIO_1=21 -D P_LORA_NSS=5 -D P_LORA_RESET=16 @@ -120,7 +120,7 @@ build_src_filter = ${Heltec_t096.build_src_filter} lib_deps = ${Heltec_t096.lib_deps} -[env:Heltec_t096_companion_radio_ble_femon] +[env:Heltec_t096_companion_radio_ble] extends = Heltec_t096 board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld board_upload.maximum_size = 712704 @@ -143,12 +143,6 @@ lib_deps = ${Heltec_t096.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_t096_companion_radio_ble_femoff] -extends = env:Heltec_t096_companion_radio_ble_femon -build_flags = - ${env:Heltec_t096_companion_radio_ble_femon.build_flags} - -D RADIO_FEM_RXGAIN=0 ; undefined (default on), 1=on, 0=off - [env:Heltec_t096_companion_radio_usb] extends = Heltec_t096 board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld diff --git a/variants/heltec_t114/platformio.ini b/variants/heltec_t114/platformio.ini index e808d6c2..135babb1 100644 --- a/variants/heltec_t114/platformio.ini +++ b/variants/heltec_t114/platformio.ini @@ -12,7 +12,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/heltec_t114 -I src/helpers/ui -D HELTEC_T114 -; -D NRF52_POWER_MANAGEMENT + -D NRF52_POWER_MANAGEMENT -D P_LORA_DIO_1=20 -D P_LORA_NSS=24 -D P_LORA_RESET=25 diff --git a/variants/heltec_t190/HeltecT190Board.cpp b/variants/heltec_t190/HeltecT190Board.cpp index 0a16b52b..4f35be40 100644 --- a/variants/heltec_t190/HeltecT190Board.cpp +++ b/variants/heltec_t190/HeltecT190Board.cpp @@ -20,6 +20,33 @@ void HeltecT190Board::begin() { } } + void HeltecT190Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + + // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep + rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); + + rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); + + if (pin_wake_btn < 0) { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet + } else { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn + } + + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000); + } + + // Finally set ESP32 into sleep + esp_deep_sleep_start(); // CPU halts here and never returns! + } + + void HeltecT190Board::powerOff() { + enterDeepSleep(0); + } + uint16_t HeltecT190Board::getBattMilliVolts() { analogReadResolution(10); digitalWrite(PIN_ADC_CTRL, HIGH); diff --git a/variants/heltec_t190/HeltecT190Board.h b/variants/heltec_t190/HeltecT190Board.h index 557c070e..bc38c1e0 100644 --- a/variants/heltec_t190/HeltecT190Board.h +++ b/variants/heltec_t190/HeltecT190Board.h @@ -3,6 +3,7 @@ #include #include #include +#include class HeltecT190Board : public ESP32Board { @@ -12,6 +13,8 @@ public: HeltecT190Board() : periph_power(PIN_VEXT_EN,PIN_VEXT_EN_ACTIVE) { } void begin(); + void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); + void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; diff --git a/variants/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini index 2cd0cea6..69293d70 100644 --- a/variants/heltec_tracker/platformio.ini +++ b/variants/heltec_tracker/platformio.ini @@ -99,10 +99,6 @@ lib_deps = ${Heltec_tracker_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_Wireless_Tracker_companion_radio_ble_ps] -extends = env:Heltec_Wireless_Tracker_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 - [env:Heltec_Wireless_Tracker_repeater] extends = Heltec_tracker_base build_flags = diff --git a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp index 99b1cdfe..f182c905 100644 --- a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp +++ b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp @@ -35,12 +35,33 @@ void HeltecTrackerV2Board::begin() { loRaFEMControl.setRxModeEnable(); } - void HeltecTrackerV2Board::powerOff() { - // Turn off PA - digitalWrite(P_LORA_PA_POWER, LOW); - rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_POWER); + void HeltecTrackerV2Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - ESP32Board::powerOff(); + // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep + rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); + + rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); + + loRaFEMControl.setRxModeEnableWhenMCUSleep();//It also needs to be enabled in receive mode + + if (pin_wake_btn < 0) { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet + } else { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn + } + + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000); + } + + // Finally set ESP32 into sleep + esp_deep_sleep_start(); // CPU halts here and never returns! + } + + void HeltecTrackerV2Board::powerOff() { + enterDeepSleep(0); } uint16_t HeltecTrackerV2Board::getBattMilliVolts() { diff --git a/variants/heltec_tracker_v2/HeltecTrackerV2Board.h b/variants/heltec_tracker_v2/HeltecTrackerV2Board.h index 2bd6a025..ccbecc7a 100644 --- a/variants/heltec_tracker_v2/HeltecTrackerV2Board.h +++ b/variants/heltec_tracker_v2/HeltecTrackerV2Board.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "LoRaFEMControl.h" class HeltecTrackerV2Board : public ESP32Board { @@ -16,6 +17,7 @@ public: void begin(); void onBeforeTransmit(void) override; void onAfterTransmit(void) override; + void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; diff --git a/variants/heltec_tracker_v2/LoRaFEMControl.h b/variants/heltec_tracker_v2/LoRaFEMControl.h index a3b5c4ed..0ce60fff 100644 --- a/variants/heltec_tracker_v2/LoRaFEMControl.h +++ b/variants/heltec_tracker_v2/LoRaFEMControl.h @@ -17,6 +17,6 @@ class LoRaFEMControl bool isLNAEnabled(void) const { return lna_enabled; } private: - bool lna_enabled = true; + bool lna_enabled = false; bool lna_can_control = false; }; diff --git a/variants/heltec_tracker_v2/platformio.ini b/variants/heltec_tracker_v2/platformio.ini index d914ce6a..d57c2113 100644 --- a/variants/heltec_tracker_v2/platformio.ini +++ b/variants/heltec_tracker_v2/platformio.ini @@ -176,10 +176,6 @@ lib_deps = ${Heltec_tracker_v2.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:heltec_tracker_v2_companion_radio_ble_ps] -extends = env:heltec_tracker_v2_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 - [env:heltec_tracker_v2_companion_radio_wifi] extends = Heltec_tracker_v2 build_flags = diff --git a/variants/heltec_v2/HeltecV2Board.h b/variants/heltec_v2/HeltecV2Board.h index 9b08fe94..fe800890 100644 --- a/variants/heltec_v2/HeltecV2Board.h +++ b/variants/heltec_v2/HeltecV2Board.h @@ -7,6 +7,8 @@ #define PIN_VBAT_READ 37 #define PIN_LED_BUILTIN 25 +#include + class HeltecV2Board : public ESP32Board { public: void begin() { @@ -24,6 +26,29 @@ public: } } + void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + + // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep + rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_0, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_0); + + rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); + + if (pin_wake_btn < 0) { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_0), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet + } else { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_0) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn + } + + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000); + } + + // Finally set ESP32 into sleep + esp_deep_sleep_start(); // CPU halts here and never returns! + } + uint16_t getBattMilliVolts() override { analogReadResolution(10); diff --git a/variants/heltec_v2/platformio.ini b/variants/heltec_v2/platformio.ini index e9cf56f0..ba4f8694 100644 --- a/variants/heltec_v2/platformio.ini +++ b/variants/heltec_v2/platformio.ini @@ -172,10 +172,6 @@ lib_deps = ${Heltec_lora32_v2.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_v2_companion_radio_ble_ps] -extends = env:Heltec_v2_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 - [env:Heltec_v2_companion_radio_wifi] extends = Heltec_lora32_v2 build_flags = diff --git a/variants/heltec_v3/HeltecV3Board.h b/variants/heltec_v3/HeltecV3Board.h index 7e7abe31..ba22a7f2 100644 --- a/variants/heltec_v3/HeltecV3Board.h +++ b/variants/heltec_v3/HeltecV3Board.h @@ -17,6 +17,8 @@ #define PIN_ADC_CTRL_ACTIVE LOW #define PIN_ADC_CTRL_INACTIVE HIGH +#include + class HeltecV3Board : public ESP32Board { private: bool adc_active_state; @@ -50,6 +52,33 @@ public: } } + void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + + // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep + rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); + + rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); + + if (pin_wake_btn < 0) { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet + } else { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn + } + + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000); + } + + // Finally set ESP32 into sleep + esp_deep_sleep_start(); // CPU halts here and never returns! + } + + void powerOff() override { + enterDeepSleep(0); + } + uint16_t getBattMilliVolts() override { analogReadResolution(10); digitalWrite(PIN_ADC_CTRL, adc_active_state); diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 69e4ed05..a70a93a5 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -179,10 +179,6 @@ lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_v3_companion_radio_ble_ps] -extends = env:Heltec_v3_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 - [env:Heltec_v3_companion_radio_wifi] extends = Heltec_lora32_v3 build_flags = @@ -324,10 +320,6 @@ lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Heltec_WSL3_companion_radio_ble_ps] -extends = env:Heltec_WSL3_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 - [env:Heltec_WSL3_companion_radio_usb] extends = Heltec_lora32_v3 build_flags = diff --git a/variants/heltec_v4/HeltecV4Board.h b/variants/heltec_v4/HeltecV4Board.h index 55166bb3..fc37b9f6 100644 --- a/variants/heltec_v4/HeltecV4Board.h +++ b/variants/heltec_v4/HeltecV4Board.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "LoRaFEMControl.h" #ifndef ADC_MULTIPLIER @@ -22,6 +23,7 @@ public: void begin(); void onBeforeTransmit(void) override; void onAfterTransmit(void) override; + void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); void powerOff() override; bool setLoRaFemLnaEnabled(bool enable) override; bool canControlLoRaFemLna() const override; diff --git a/variants/heltec_v4/LoRaFEMControl.h b/variants/heltec_v4/LoRaFEMControl.h index d84ebe9c..d312762d 100644 --- a/variants/heltec_v4/LoRaFEMControl.h +++ b/variants/heltec_v4/LoRaFEMControl.h @@ -24,7 +24,7 @@ class LoRaFEMControl LoRaFEMType getFEMType(void) const { return fem_type; } private: LoRaFEMType fem_type=OTHER_FEM_TYPES; - bool lna_enabled=true; + bool lna_enabled=false; bool lna_can_control=false; }; diff --git a/variants/heltec_v4/platformio.ini b/variants/heltec_v4/platformio.ini index caa71a33..fabf3827 100644 --- a/variants/heltec_v4/platformio.ini +++ b/variants/heltec_v4/platformio.ini @@ -222,16 +222,6 @@ lib_deps = ${heltec_v4_oled.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:heltec_v4_companion_radio_ble_ps_femon] -extends = env:heltec_v4_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 - -[env:heltec_v4_3_companion_radio_ble_ps_femoff] -extends = env:heltec_v4_companion_radio_ble_ps_femon -build_flags = - ${env:heltec_v4_companion_radio_ble_ps_femon.build_flags} - -D RADIO_FEM_RXGAIN=0 ; undefined (default on), 1=on, 0=off - [env:heltec_v4_companion_radio_wifi] extends = heltec_v4_oled build_flags = @@ -396,14 +386,6 @@ lib_deps = ${heltec_v4_tft.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:heltec_v4_expansionkit_tft_companion_radio_ble_ps] -extends = env:heltec_v4_tft_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 -build_flags = - ${env:heltec_v4_tft_companion_radio_ble.build_flags} - -D ENV_PIN_SDA=4 - -D ENV_PIN_SCL=3 - [env:heltec_v4_tft_companion_radio_wifi] extends = heltec_v4_tft build_flags = diff --git a/variants/heltec_wireless_paper/platformio.ini b/variants/heltec_wireless_paper/platformio.ini index e7a107a5..48723d16 100644 --- a/variants/heltec_wireless_paper/platformio.ini +++ b/variants/heltec_wireless_paper/platformio.ini @@ -64,10 +64,6 @@ lib_deps = densaugeo/base64 @ ~1.4.0 bakercp/CRC32 @ ^2.0.0 -[env:Heltec_Wireless_Paper_companion_radio_ble_ps] -extends = env:Heltec_Wireless_Paper_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 - [env:Heltec_Wireless_Paper_companion_radio_usb] extends = Heltec_Wireless_Paper_base build_flags = diff --git a/variants/lilygo_t3s3/platformio.ini b/variants/lilygo_t3s3/platformio.ini index e7f9f11f..54990117 100644 --- a/variants/lilygo_t3s3/platformio.ini +++ b/variants/lilygo_t3s3/platformio.ini @@ -174,10 +174,6 @@ lib_deps = ${LilyGo_T3S3_sx1262.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:LilyGo_T3S3_sx1262_companion_radio_ble_ps] -extends = env:LilyGo_T3S3_sx1262_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 - [env:LilyGo_T3S3_sx1262_kiss_modem] extends = LilyGo_T3S3_sx1262 build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} diff --git a/variants/lilygo_tbeam_1w/platformio.ini b/variants/lilygo_tbeam_1w/platformio.ini index 4516e4cd..c7a59552 100644 --- a/variants/lilygo_tbeam_1w/platformio.ini +++ b/variants/lilygo_tbeam_1w/platformio.ini @@ -146,11 +146,6 @@ lib_deps = ${LilyGo_TBeam_1W.lib_deps} densaugeo/base64 @ ~1.4.0 -; === LILYGO T-Beam 1W Companion Radio PS (BLE PS) === -[env:LilyGo_TBeam_1W_companion_radio_ble_ps] -extends = env:LilyGo_TBeam_1W_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 - ; === LILYGO T-Beam 1W Companion Radio (WiFi) === [env:LilyGo_TBeam_1W_companion_radio_wifi] extends = LilyGo_TBeam_1W diff --git a/variants/lilygo_tbeam_SX1262/platformio.ini b/variants/lilygo_tbeam_SX1262/platformio.ini index 5bc0efdd..62ac09f8 100644 --- a/variants/lilygo_tbeam_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_SX1262/platformio.ini @@ -65,13 +65,6 @@ lib_deps = ${LilyGo_TBeam_SX1262.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Tbeam_SX1262_companion_radio_ble_ps] -extends = env:Tbeam_SX1262_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 -build_flags = - ${env:Tbeam_SX1262_companion_radio_ble.build_flags} - -D MAX_CONTACTS=150 - [env:Tbeam_SX1262_repeater] extends = LilyGo_TBeam_SX1262 build_flags = diff --git a/variants/lilygo_tbeam_SX1276/platformio.ini b/variants/lilygo_tbeam_SX1276/platformio.ini index edf84e21..cb25903c 100644 --- a/variants/lilygo_tbeam_SX1276/platformio.ini +++ b/variants/lilygo_tbeam_SX1276/platformio.ini @@ -61,10 +61,6 @@ lib_deps = ${LilyGo_TBeam_SX1276.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Tbeam_SX1276_companion_radio_ble_ps] -extends = env:Tbeam_SX1276_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 - [env:Tbeam_SX1276_repeater] extends = LilyGo_TBeam_SX1276 build_flags = diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index b930a25b..02946156 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -142,10 +142,6 @@ lib_deps = ${T_Beam_S3_Supreme_SX1262.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps] -extends = env:T_Beam_S3_Supreme_SX1262_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 - [env:T_Beam_S3_Supreme_SX1262_companion_radio_wifi] extends = T_Beam_S3_Supreme_SX1262 build_flags = diff --git a/variants/lilygo_tdeck/TDeckBoard.h b/variants/lilygo_tdeck/TDeckBoard.h index e2844360..7ed007af 100644 --- a/variants/lilygo_tdeck/TDeckBoard.h +++ b/variants/lilygo_tdeck/TDeckBoard.h @@ -3,6 +3,7 @@ #include #include #include "helpers/ESP32Board.h" +#include #define PIN_VBAT_READ 4 #define BATTERY_SAMPLES 8 @@ -22,6 +23,29 @@ public: } #endif + void enterDeepSleep(uint32_t secs, int pin_wake_btn) { + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + + // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep + rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); + + rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); + + if (pin_wake_btn < 0) { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet + } else { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn + } + + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000); + } + + // Finally set ESP32 into sleep + esp_deep_sleep_start(); // CPU halts here and never returns! + } + uint16_t getBattMilliVolts() { #if defined(PIN_VBAT_READ) && defined(ADC_MULTIPLIER) analogReadResolution(12); diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index bb173524..36731668 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -92,7 +92,7 @@ extends = LilyGo_TLora_V2_1_1_6 build_flags = ${LilyGo_TLora_V2_1_1_6.build_flags} -I examples/companion_radio/ui-new - -D MAX_CONTACTS=100 + -D MAX_CONTACTS=160 -D MAX_GROUP_CHANNELS=8 -D BLE_PIN_CODE=123456 -D OFFLINE_QUEUE_SIZE=128 @@ -108,10 +108,6 @@ lib_deps = ${LilyGo_TLora_V2_1_1_6.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps] -extends = env:LilyGo_TLora_V2_1_1_6_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 - [env:LilyGo_TLora_V2_1_1_6_room_server] extends = LilyGo_TLora_V2_1_1_6 build_src_filter = ${LilyGo_TLora_V2_1_1_6.build_src_filter} diff --git a/variants/promicro/platformio.ini b/variants/promicro/platformio.ini index 1e1a3274..5415e158 100644 --- a/variants/promicro/platformio.ini +++ b/variants/promicro/platformio.ini @@ -1,7 +1,6 @@ [Promicro] extends = nrf52_base board = promicro_nrf52840 -board_build.ldscript = boards/nrf52840_s140_v6.ld build_flags = ${nrf52_base.build_flags} -I variants/promicro -D PROMICRO diff --git a/variants/rak3112/RAK3112Board.h b/variants/rak3112/RAK3112Board.h index 704162b8..8ba3197c 100644 --- a/variants/rak3112/RAK3112Board.h +++ b/variants/rak3112/RAK3112Board.h @@ -16,6 +16,8 @@ #define ADC_MULTIPLIER (3 * 1.73 * 1.187 * 1000) #define BATTERY_SAMPLES 8 +#include + class RAK3112Board : public ESP32Board { private: bool adc_active_state; @@ -49,6 +51,33 @@ public: } } + void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + + // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep + rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); + + rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); + + if (pin_wake_btn < 0) { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet + } else { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn + } + + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000); + } + + // Finally set ESP32 into sleep + esp_deep_sleep_start(); // CPU halts here and never returns! + } + + void powerOff() override { + enterDeepSleep(0); + } + uint16_t getBattMilliVolts() override { analogReadResolution(12); diff --git a/variants/rak3401/platformio.ini b/variants/rak3401/platformio.ini index 5eeb6a9d..20a8a548 100644 --- a/variants/rak3401/platformio.ini +++ b/variants/rak3401/platformio.ini @@ -2,12 +2,11 @@ extends = nrf52_base board = rak3401 board_check = true -board_build.ldscript = boards/nrf52840_s140_v6.ld build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I variants/rak3401 -D RAK_3401 -; -D NRF52_POWER_MANAGEMENT + -D NRF52_POWER_MANAGEMENT -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D LORA_TX_POWER=22 diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index 8f72999a..2bbba314 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -2,7 +2,6 @@ extends = nrf52_base board = rak4631 board_check = true -board_build.ldscript = boards/nrf52840_s140_v6.ld extra_scripts = ${nrf52_base.extra_scripts} post:variants/rak4631/fix_bsec_lib.py build_flags = ${nrf52_base.build_flags} @@ -10,7 +9,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/rak4631 -D RAK_4631 -D RAK_BOARD -; -D NRF52_POWER_MANAGEMENT + -D NRF52_POWER_MANAGEMENT -D PIN_BOARD_SCL=14 -D PIN_BOARD_SDA=13 -D PIN_GPS_TX=PIN_SERIAL1_RX diff --git a/variants/rak_wismesh_tag/platformio.ini b/variants/rak_wismesh_tag/platformio.ini index b596c9a6..e9cddb74 100644 --- a/variants/rak_wismesh_tag/platformio.ini +++ b/variants/rak_wismesh_tag/platformio.ini @@ -2,7 +2,6 @@ extends = nrf52_base board = rak4631 board_check = true -board_build.ldscript = boards/nrf52840_s140_v6.ld build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I variants/rak_wismesh_tag diff --git a/variants/sensecap_solar/platformio.ini b/variants/sensecap_solar/platformio.ini index c9f7ed39..effef38c 100644 --- a/variants/sensecap_solar/platformio.ini +++ b/variants/sensecap_solar/platformio.ini @@ -10,7 +10,7 @@ build_flags = ${nrf52_base.build_flags} -I src/helpers/nrf52 -D NRF52_PLATFORM=1 -D USE_SX1262 -; -D NRF52_POWER_MANAGEMENT + -D NRF52_POWER_MANAGEMENT -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D P_LORA_TX_LED=12 diff --git a/variants/station_g2/StationG2Board.h b/variants/station_g2/StationG2Board.h index d1989ee0..a905682c 100644 --- a/variants/station_g2/StationG2Board.h +++ b/variants/station_g2/StationG2Board.h @@ -2,6 +2,7 @@ #include #include +#include class StationG2Board : public ESP32Board { public: @@ -20,6 +21,29 @@ public: } } + void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + + // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep + rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); + + rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); + + if (pin_wake_btn < 0) { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet + } else { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn + } + + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000); + } + + // Finally set ESP32 into sleep + esp_deep_sleep_start(); // CPU halts here and never returns! + } + uint16_t getBattMilliVolts() override { return 0; } diff --git a/variants/station_g2/platformio.ini b/variants/station_g2/platformio.ini index 87e77152..6432b523 100644 --- a/variants/station_g2/platformio.ini +++ b/variants/station_g2/platformio.ini @@ -238,3 +238,8 @@ build_src_filter = ${Station_G2.build_src_filter} lib_deps = ${Station_G2.lib_deps} densaugeo/base64 @ ~1.4.0 + +[env:Station_G2_kiss_modem] +extends = Station_G2 +build_src_filter = ${Station_G2.build_src_filter} + +<../examples/kiss_modem/> diff --git a/variants/station_g2/target.cpp b/variants/station_g2/target.cpp index 40fa8005..8018c20e 100644 --- a/variants/station_g2/target.cpp +++ b/variants/station_g2/target.cpp @@ -40,21 +40,6 @@ bool radio_init() { #endif } -uint32_t radio_get_rng_seed() { - return radio.random(0x7FFFFFFF); -} - -void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { - radio.setFrequency(freq); - radio.setSpreadingFactor(sf); - radio.setBandwidth(bw); - radio.setCodingRate(cr); -} - -void radio_set_tx_power(int8_t dbm) { - radio.setOutputPower(dbm); -} - mesh::LocalIdentity radio_new_identity() { RadioNoiseListener rng(radio); return mesh::LocalIdentity(&rng); // create new random identity diff --git a/variants/station_g2/target.h b/variants/station_g2/target.h index 9a361025..b4eadbba 100644 --- a/variants/station_g2/target.h +++ b/variants/station_g2/target.h @@ -24,8 +24,5 @@ extern EnvironmentSensorManager sensors; #endif bool radio_init(); -uint32_t radio_get_rng_seed(); -void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); -void radio_set_tx_power(int8_t dbm); mesh::LocalIdentity radio_new_identity(); diff --git a/variants/thinknode_m2/ThinknodeM2Board.cpp b/variants/thinknode_m2/ThinknodeM2Board.cpp index 8d68006d..05965103 100644 --- a/variants/thinknode_m2/ThinknodeM2Board.cpp +++ b/variants/thinknode_m2/ThinknodeM2Board.cpp @@ -1,30 +1,40 @@ #include "ThinknodeM2Board.h" + + void ThinknodeM2Board::begin() { - pinMode(PIN_VEXT_EN, OUTPUT); - digitalWrite(PIN_VEXT_EN, !PIN_VEXT_EN_ACTIVE); // force power cycle - delay(20); // allow power rail to discharge - digitalWrite(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE); // turn backlight back on - delay(120); // give display time to bias on cold boot - ESP32Board::begin(); - pinMode(PIN_STATUS_LED, OUTPUT); // init power led -} - -uint16_t ThinknodeM2Board::getBattMilliVolts() { - analogReadResolution(12); - analogSetPinAttenuation(PIN_VBAT_READ, ADC_11db); - - uint32_t mv = 0; - for (int i = 0; i < 8; ++i) { - mv += analogReadMilliVolts(PIN_VBAT_READ); - delayMicroseconds(200); + pinMode(PIN_VEXT_EN, OUTPUT); + digitalWrite(PIN_VEXT_EN, !PIN_VEXT_EN_ACTIVE); // force power cycle + delay(20); // allow power rail to discharge + digitalWrite(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE); // turn backlight back on + delay(120); // give display time to bias on cold boot + ESP32Board::begin(); + pinMode(PIN_STATUS_LED, OUTPUT); // init power led } - mv /= 8; - analogReadResolution(10); - return static_cast(mv * ADC_MULTIPLIER); + void ThinknodeM2Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { + esp_deep_sleep_start(); + } + + void ThinknodeM2Board::powerOff() { + enterDeepSleep(0); + } + + uint16_t ThinknodeM2Board::getBattMilliVolts() { + analogReadResolution(12); + analogSetPinAttenuation(PIN_VBAT_READ, ADC_11db); + + uint32_t mv = 0; + for (int i = 0; i < 8; ++i) { + mv += analogReadMilliVolts(PIN_VBAT_READ); + delayMicroseconds(200); + } + mv /= 8; + + analogReadResolution(10); + return static_cast(mv * ADC_MULTIPLIER ); } -const char *ThinknodeM2Board::getManufacturerName() const { - return "Elecrow ThinkNode M2"; -} + const char* ThinknodeM2Board::getManufacturerName() const { + return "Elecrow ThinkNode M2"; + } diff --git a/variants/thinknode_m2/ThinknodeM2Board.h b/variants/thinknode_m2/ThinknodeM2Board.h index 02556777..8011fae6 100644 --- a/variants/thinknode_m2/ThinknodeM2Board.h +++ b/variants/thinknode_m2/ThinknodeM2Board.h @@ -3,11 +3,15 @@ #include #include #include +#include class ThinknodeM2Board : public ESP32Board { public: + void begin(); + void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); + void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; diff --git a/variants/thinknode_m5/ThinknodeM5Board.cpp b/variants/thinknode_m5/ThinknodeM5Board.cpp index 2cb138e6..c4de538c 100644 --- a/variants/thinknode_m5/ThinknodeM5Board.cpp +++ b/variants/thinknode_m5/ThinknodeM5Board.cpp @@ -19,6 +19,14 @@ void ThinknodeM5Board::begin() { ESP32Board::begin(); } + void ThinknodeM5Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { + esp_deep_sleep_start(); + } + + void ThinknodeM5Board::powerOff() { + enterDeepSleep(0); + } + uint16_t ThinknodeM5Board::getBattMilliVolts() { analogReadResolution(12); analogSetPinAttenuation(PIN_VBAT_READ, ADC_11db); diff --git a/variants/thinknode_m5/ThinknodeM5Board.h b/variants/thinknode_m5/ThinknodeM5Board.h index 57ff0d00..3c120027 100644 --- a/variants/thinknode_m5/ThinknodeM5Board.h +++ b/variants/thinknode_m5/ThinknodeM5Board.h @@ -3,6 +3,7 @@ #include #include #include +#include #include extern PCA9557 expander; @@ -12,6 +13,8 @@ class ThinknodeM5Board : public ESP32Board { public: void begin(); + void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); + void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; diff --git a/variants/xiao_c3/XiaoC3Board.h b/variants/xiao_c3/XiaoC3Board.h index c5700c68..6ea1c15f 100644 --- a/variants/xiao_c3/XiaoC3Board.h +++ b/variants/xiao_c3/XiaoC3Board.h @@ -3,6 +3,7 @@ #include #include +#include #include class XiaoC3Board : public ESP32Board { @@ -39,6 +40,37 @@ public: #endif } + void enterDeepSleep(uint32_t secs, int8_t wake_pin = -1) { + gpio_set_direction(gpio_num_t(P_LORA_DIO_1), GPIO_MODE_INPUT); + if (wake_pin >= 0) { + gpio_set_direction((gpio_num_t)wake_pin, GPIO_MODE_INPUT); + } + + //hold disable, isolate and power domain config functions may be unnecessary + //gpio_deep_sleep_hold_dis(); + //esp_sleep_config_gpio_isolate(); + gpio_deep_sleep_hold_en(); + +#if defined(LORA_TX_BOOST_PIN) + gpio_hold_en((gpio_num_t) LORA_TX_BOOST_PIN); + gpio_deep_sleep_hold_en(); +#endif + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + + if (wake_pin >= 0) { + esp_deep_sleep_enable_gpio_wakeup((1 << P_LORA_DIO_1) | (1 << wake_pin), ESP_GPIO_WAKEUP_GPIO_HIGH); + } else { + esp_deep_sleep_enable_gpio_wakeup(1 << P_LORA_DIO_1, ESP_GPIO_WAKEUP_GPIO_HIGH); + } + + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000); + } + + // Finally set ESP32 into sleep + esp_deep_sleep_start(); // CPU halts here and never returns! + } + #if defined(LORA_TX_BOOST_PIN) || defined(P_LORA_TX_LED) void onBeforeTransmit() override { #if defined(P_LORA_TX_LED) diff --git a/variants/xiao_c3/platformio.ini b/variants/xiao_c3/platformio.ini index fa70d8a0..c0e8458d 100644 --- a/variants/xiao_c3/platformio.ini +++ b/variants/xiao_c3/platformio.ini @@ -90,11 +90,6 @@ lib_deps = ${esp32_ota.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:Xiao_C3_companion_radio_ble_ps] -extends = env:Xiao_C3_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 -board_build.partitions = min_spiffs.csv ; get around 4mb flash limit - [env:Xiao_C3_companion_radio_usb] extends = Xiao_esp32_C3 build_src_filter = ${Xiao_esp32_C3.build_src_filter} diff --git a/variants/xiao_c6/platformio.ini b/variants/xiao_c6/platformio.ini index 0d8c2a79..9f504b8e 100644 --- a/variants/xiao_c6/platformio.ini +++ b/variants/xiao_c6/platformio.ini @@ -1,7 +1,6 @@ [Xiao_C6] extends = esp32c6_base board = esp32-c6-devkitm-1 -board_build.flash_mode = dio board_build.partitions = min_spiffs.csv ; get around 4mb flash limit build_flags = ${esp32c6_base.build_flags} diff --git a/variants/xiao_nrf52/platformio.ini b/variants/xiao_nrf52/platformio.ini index f4d1b93e..a0854336 100644 --- a/variants/xiao_nrf52/platformio.ini +++ b/variants/xiao_nrf52/platformio.ini @@ -9,7 +9,7 @@ build_flags = ${nrf52_base.build_flags} -I variants/xiao_nrf52 -UENV_INCLUDE_GPS -D NRF52_PLATFORM -; -D NRF52_POWER_MANAGEMENT + -D NRF52_POWER_MANAGEMENT -D XIAO_NRF52 -D USE_SX1262 -D RADIO_CLASS=CustomSX1262 diff --git a/variants/xiao_s3/platformio.ini b/variants/xiao_s3/platformio.ini index b59e69ae..22464e7d 100644 --- a/variants/xiao_s3/platformio.ini +++ b/variants/xiao_s3/platformio.ini @@ -114,10 +114,6 @@ lib_deps = densaugeo/base64 @ ~1.4.0 adafruit/Adafruit SSD1306 @ ^2.5.13 -[env:Xiao_S3_companion_radio_ble_ps] -extends = env:Xiao_S3_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 - [env:Xiao_S3_companion_radio_usb] extends = Xiao_S3 build_flags = diff --git a/variants/xiao_s3_wio/platformio.ini b/variants/xiao_s3_wio/platformio.ini index 41420acf..db8c5a94 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -173,10 +173,6 @@ lib_deps = densaugeo/base64 @ ~1.4.0 adafruit/Adafruit SSD1306 @ ^2.5.13 -[env:Xiao_S3_WIO_companion_radio_ble_ps] -extends = env:Xiao_S3_WIO_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 - [env:Xiao_S3_WIO_companion_radio_serial] extends = Xiao_S3_WIO build_flags = From 2acd61ec84988bbbba006d8fc7ae747f852c368f Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 15 Jun 2026 16:02:22 -0700 Subject: [PATCH 126/214] Remove PowerSaving changes from Halo retry branch --- .github/workflows/stale-bot.yml | 32 ----- SECURITY.md | 57 -------- build-iotthinks.sh | 135 ------------------ examples/companion_radio/DataStore.cpp | 2 - examples/companion_radio/MyMesh.cpp | 29 ---- examples/companion_radio/MyMesh.h | 2 +- examples/companion_radio/NodePrefs.h | 3 +- examples/companion_radio/main.cpp | 40 ------ examples/simple_room_server/MyMesh.cpp | 8 -- examples/simple_room_server/MyMesh.h | 3 - examples/simple_room_server/main.cpp | 13 -- src/MeshCore.h | 4 + src/helpers/ArduinoHelpers.cpp | 6 - src/helpers/ArduinoHelpers.h | 42 +----- src/helpers/ArduinoSerialInterface.cpp | 4 - src/helpers/ArduinoSerialInterface.h | 3 +- src/helpers/BaseChatMesh.h | 10 +- src/helpers/BaseSerialInterface.h | 1 - src/helpers/ESP32Board.h | 106 ++------------ src/helpers/MeshadventurerBoard.h | 30 ++++ src/helpers/NRF52Board.cpp | 28 ++-- src/helpers/NRF52Board.h | 4 +- src/helpers/SensorManager.h | 2 - src/helpers/esp32/SerialBLEInterface.cpp | 4 - src/helpers/esp32/SerialBLEInterface.h | 1 - src/helpers/esp32/SerialWifiInterface.cpp | 4 - src/helpers/esp32/SerialWifiInterface.h | 1 - src/helpers/esp32/TBeamBoard.h | 24 ++++ src/helpers/nrf52/SerialBLEInterface.cpp | 4 - src/helpers/nrf52/SerialBLEInterface.h | 1 - .../sensors/EnvironmentSensorManager.cpp | 16 --- .../sensors/EnvironmentSensorManager.h | 1 - src/helpers/ui/ST7735Display.cpp | 9 -- 33 files changed, 93 insertions(+), 536 deletions(-) delete mode 100644 .github/workflows/stale-bot.yml delete mode 100644 SECURITY.md delete mode 100644 build-iotthinks.sh delete mode 100644 src/helpers/ArduinoHelpers.cpp diff --git a/.github/workflows/stale-bot.yml b/.github/workflows/stale-bot.yml deleted file mode 100644 index ec166587..00000000 --- a/.github/workflows/stale-bot.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: 'Run Stale Bot' -on: - schedule: - - cron: '30 1 * * *' # daily at 1:30am - workflow_dispatch: {} - -permissions: - actions: write - issues: write - pull-requests: write - -jobs: - close-issues: - # only run on main repo, not forks - if: github.repository == 'meshcore-dev/MeshCore' - runs-on: ubuntu-latest - steps: - - name: Close Stale Issues - uses: actions/stale@v10 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - # auto close issues - days-before-issue-stale: 60 - days-before-issue-close: 7 - exempt-issue-labels: "keep-open" - stale-issue-label: "stale" - stale-issue-message: "This issue is stale because it has been open for 60 days with no activity. Remove the stale label or add a comment if this issue is still relevant, otherwise this issue will automatically close in 7 days." - close-issue-message: "This issue was closed because it has been inactive for 7 days since being marked as stale." - # don't auto close prs - days-before-pr-stale: -1 - days-before-pr-close: -1 - \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index a4b2207d..00000000 --- a/SECURITY.md +++ /dev/null @@ -1,57 +0,0 @@ -# Security Policy - -## Supported Versions - -Security fixes are applied to the latest release only. We do not backport -fixes to older versions. - -| Version | Supported | -|---------|-----------| -| 1.15+ | ✅ | -| <1.15 | ❌ | - -## Reporting a Vulnerability - -**Please do not report security vulnerabilities through public GitHub issues.** - -Use GitHub's private vulnerability reporting instead: -1. Go to the **Security** tab of this repository -2. Click **Report a vulnerability** -3. Fill in the details and submit - -### What to include - -A useful report tells us: -- Which component or file is affected -- What an attacker can do (impact) and under what conditions -- A minimal reproduction case or proof-of-concept if you have one -- Whether you believe it is remotely exploitable - -You do not need a working exploit to report. An incomplete report is better -than no report. - -## What to expect - -This is a volunteer-maintained open-source project. We will do our best to -respond in a reasonable timeframe, but cannot commit to specific deadlines. - -We ask that you give us a fair opportunity to investigate and address the -issue before any public disclosure. If you have not heard back after -**90 days**, feel free to follow up or proceed with disclosure at your -discretion. - -## Scope - -In scope: -- Remote code execution, memory corruption, or denial-of-service via crafted - radio packets -- Authentication or encryption bypasses -- Vulnerabilities in the packet routing or path handling logic - -Out of scope: -- Physical access attacks (e.g., JTAG, UART extraction of keys) -- Regulatory compliance (duty cycle, frequency restrictions) -- Jamming or other physical-layer radio interference -- Issues in third-party libraries (RadioLib, Crypto, etc.) — report those - upstream -- "Best practice" suggestions without a demonstrated attack path diff --git a/build-iotthinks.sh b/build-iotthinks.sh deleted file mode 100644 index 613789d0..00000000 --- a/build-iotthinks.sh +++ /dev/null @@ -1,135 +0,0 @@ -# sh ./build-repeaters-iotthinks.sh -export FIRMWARE_VERSION="PowerSaving16" - -############# Repeaters ############# -# Commonly-used boards -## ESP32 - 17 boards -sh build.sh build-firmware \ -Heltec_v3_repeater \ -Heltec_WSL3_repeater \ -heltec_v4_repeater \ -Station_G2_repeater \ -T_Beam_S3_Supreme_SX1262_repeater \ -Tbeam_SX1262_repeater \ -LilyGo_T3S3_sx1262_repeater \ -Xiao_S3_WIO_repeater \ -Xiao_C3_repeater \ -Xiao_C6_repeater_ \ -Heltec_E290_repeater \ -Heltec_Wireless_Tracker_repeater \ -LilyGo_TBeam_1W_repeater \ -Xiao_S3_repeater \ -heltec_tracker_v2_repeater \ -Heltec_Wireless_Paper_repeater \ -Heltec_ct62_repeater - -## NRF52 - 17 boards -sh build.sh build-firmware \ -RAK_4631_repeater \ -Heltec_t114_repeater \ -Xiao_nrf52_repeater \ -Heltec_mesh_solar_repeater \ -ProMicro_repeater \ -SenseCap_Solar_repeater \ -t1000e_repeater \ -LilyGo_T-Echo_repeater \ -WioTrackerL1_repeater \ -RAK_3401_repeater \ -RAK_WisMesh_Tag_repeater \ -GAT562_30S_Mesh_Kit_repeater \ -GAT562_Mesh_Tracker_Pro_repeater \ -ikoka_nano_nrf_22dbm_repeater \ -ikoka_nano_nrf_30dbm_repeater \ -ikoka_nano_nrf_33dbm_repeater \ -ThinkNode_M1_repeater \ -Heltec_t096_repeater - -## ESP32, SX1276 - 3 boards -sh build.sh build-firmware \ -Heltec_v2_repeater \ -LilyGo_TLora_V2_1_1_6_repeater \ -Tbeam_SX1276_repeater - -############# Room Server ############# -# ESP32 - 7 boards -sh build.sh build-firmware \ -Heltec_v3_room_server \ -heltec_v4_room_server \ -LilyGo_TBeam_1W_room_server \ -Heltec_WSL3_room_server \ -Xiao_S3_room_server \ -heltec_tracker_v2_room_server \ -Heltec_Wireless_Paper_room_server - -# NRF52 - 6 boards -sh build.sh build-firmware \ -RAK_4631_room_server \ -Heltec_t114_room_server \ -Xiao_nrf52_room_server \ -t1000e_room_server \ -WioTrackerL1_room_server \ -RAK_3401_room_server \ -Heltec_t096_room_server - -############# Companions BLE ############# -# NRF52 - 12 boards -sh build.sh build-firmware \ -RAK_4631_companion_radio_ble \ -Heltec_t114_companion_radio_ble \ -Xiao_nrf52_companion_radio_ble \ -t1000e_companion_radio_ble \ -LilyGo_T-Echo_companion_radio_ble \ -WioTrackerL1_companion_radio_ble \ -RAK_3401_companion_radio_ble \ -RAK_WisMesh_Tag_companion_radio_ble \ -SenseCap_Solar_companion_radio_ble \ -ThinkNode_M1_companion_radio_ble \ -Heltec_t096_companion_radio_ble \ -Heltec_t096_companion_radio_ble_femoff - -############# Companions BLE PS ############# -# ESP32 - 18 boards -sh build.sh build-firmware \ -Heltec_v3_companion_radio_ble_ps \ -heltec_v4_companion_radio_ble_ps \ -heltec_v4_3_companion_radio_ble_ps_femoff \ -Xiao_C3_companion_radio_ble_ps \ -Xiao_S3_companion_radio_ble_ps \ -Xiao_S3_WIO_companion_radio_ble_ps \ -Heltec_v2_companion_radio_ble_ps \ -LilyGo_TBeam_1W_companion_radio_ble_ps \ -Heltec_WSL3_companion_radio_ble_ps \ -Heltec_Wireless_Tracker_companion_radio_ble_ps \ -heltec_tracker_v2_companion_radio_ble_ps \ -Heltec_Wireless_Paper_companion_radio_ble_ps \ -LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps \ -Heltec_ct62_companion_radio_ble_ps \ -T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps \ -Tbeam_SX1262_companion_radio_ble_ps \ -heltec_v4_expansionkit_tft_companion_radio_ble_ps \ -LilyGo_T3S3_sx1262_companion_radio_ble_ps - -# Not working -Tbeam_SX1276_companion_radio_ble_ps \ - -############# Companions USB ############# -sh build.sh build-firmware \ -Heltec_t096_companion_radio_usb - -############# Sample builds ############# -# 14 boards -sh build.sh build-firmware \ -Heltec_v3_repeater \ -heltec_v4_repeater \ -Xiao_C3_repeater \ -Xiao_C6_repeater_ \ -RAK_4631_repeater \ -Heltec_t096_repeater \ -Heltec_v3_companion_radio_ble_ps \ -heltec_v4_companion_radio_ble_ps \ -heltec_v4_3_companion_radio_ble_ps_femoff \ -Xiao_C3_companion_radio_ble_ps \ -Xiao_C6_companion_radio_ble_ \ -RAK_4631_companion_radio_ble \ -Heltec_t096_companion_radio_ble \ -Heltec_t096_companion_radio_ble_femoff diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index fdb924ad..bf2f36c3 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -233,7 +233,6 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no file.read((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89 file.read((uint8_t *)_prefs.default_scope_name, sizeof(_prefs.default_scope_name)); // 90 file.read((uint8_t *)_prefs.default_scope_key, sizeof(_prefs.default_scope_key)); // 121 - file.read((uint8_t *)&_prefs.radio_fem_rxgain, sizeof(_prefs.radio_fem_rxgain)); // 122 file.close(); } @@ -274,7 +273,6 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ file.write((uint8_t *)&_prefs.rx_boosted_gain, sizeof(_prefs.rx_boosted_gain)); // 89 file.write((uint8_t *)_prefs.default_scope_name, sizeof(_prefs.default_scope_name)); // 90 file.write((uint8_t *)_prefs.default_scope_key, sizeof(_prefs.default_scope_key)); // 121 - file.write((uint8_t *)&_prefs.radio_fem_rxgain, sizeof(_prefs.radio_fem_rxgain)); // 122 file.close(); } diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 5d1adb46..5fb9bf9d 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -62,8 +62,6 @@ #define CMD_SET_DEFAULT_FLOOD_SCOPE 63 #define CMD_GET_DEFAULT_FLOOD_SCOPE 64 #define CMD_SEND_RAW_PACKET 65 -#define CMD_GET_RADIO_FEM_RXGAIN 66 -#define CMD_SET_RADIO_FEM_RXGAIN 67 // Stats sub-types for CMD_GET_STATS #define STATS_TYPE_CORE 0 @@ -891,7 +889,6 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe _prefs.rx_boosted_gain = 1; // enabled by default #endif #endif - _prefs.radio_fem_rxgain = 1; } void MyMesh::begin(bool has_display) { @@ -941,7 +938,6 @@ void MyMesh::begin(bool has_display) { _prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, -9, MAX_LORA_TX_POWER); _prefs.gps_enabled = constrain(_prefs.gps_enabled, 0, 1); // Ensure boolean 0 or 1 _prefs.gps_interval = constrain(_prefs.gps_interval, 0, 86400); // Max 24 hours - _prefs.radio_fem_rxgain = constrain(_prefs.radio_fem_rxgain, 0, 1); #ifdef BLE_PIN_CODE // 123456 by default if (_prefs.ble_pin == 0) { @@ -971,7 +967,6 @@ void MyMesh::begin(bool has_display) { radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); radio_driver.setTxPower(_prefs.tx_power_dbm); radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); - board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s", radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled"); } @@ -1830,30 +1825,6 @@ void MyMesh::handleCmdFrame(size_t len) { } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } - } else if (cmd_frame[0] == CMD_GET_RADIO_FEM_RXGAIN) { - if (!board.canControlLoRaFemLna()) { - writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); - } else { - out_frame[0] = RESP_CODE_OK; - uint8_t value = board.isLoRaFemLnaEnabled() ? 1 : 0; - memcpy(&out_frame[1], &value, 1); - _serial->writeFrame(out_frame, 2); - } - } else if (cmd_frame[0] == CMD_SET_RADIO_FEM_RXGAIN && len >= 2) { - uint8_t value = cmd_frame[1]; - if (!board.canControlLoRaFemLna()) { - writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); - } else if (value <= 1) { - _prefs.radio_fem_rxgain = value; - if (board.setLoRaFemLnaEnabled(value != 0)) { - savePrefs(); - writeOKFrame(); - } else { - writeErrFrame(ERR_CODE_UNSUPPORTED_CMD); - } - } else { - writeErrFrame(ERR_CODE_ILLEGAL_ARG); - } } else if (cmd_frame[0] == CMD_GET_ADVERT_PATH && len >= PUB_KEY_SIZE+2) { // FUTURE use: uint8_t reserved = cmd_frame[1]; uint8_t *pub_key = &cmd_frame[2]; diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 0d687cfd..f4190f30 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -166,7 +166,7 @@ protected: public: void savePrefs() { _store->savePrefs(_prefs, sensors.node_lat, sensors.node_lon); } - + #if ENV_INCLUDE_GPS == 1 void applyGpsPrefs() { sensors.setSettingValue("gps", _prefs.gps_enabled ? "1" : "0"); diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index ecb117bd..48c381ce 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -29,10 +29,9 @@ struct NodePrefs { // persisted to file uint32_t gps_interval; // GPS read interval in seconds uint8_t autoadd_config; // bitmask for auto-add contacts config uint8_t rx_boosted_gain; // SX126x RX boosted gain mode (0=power saving, 1=boosted) - uint8_t radio_fem_rxgain; // LoRa FEM RX gain setting uint8_t client_repeat; uint8_t path_hash_mode; // which path mode to use when sending uint8_t autoadd_max_hops; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64) char default_scope_name[31]; uint8_t default_scope_key[16]; -}; +}; \ No newline at end of file diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index f10cb170..ef9b6bfc 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -2,11 +2,6 @@ #include #include "MyMesh.h" -#ifdef ESP32_PLATFORM -#include "esp_pm.h" -#include "esp_bt.h" -#endif - // Believe it or not, this std C function is busted on some platforms! static uint32_t _atoi(const char* sp) { uint32_t n = 0; @@ -245,37 +240,6 @@ void setup() { #endif board.onBootComplete(); - -#ifdef ESP32_PLATFORM -#if !CONFIG_IDF_TARGET_ESP32C6 - // Enable BLE sleep - esp_err_t errBLESleep = esp_bt_sleep_enable(); - if (errBLESleep == ESP_OK) { - Serial.println("Bluetooth sleep enabled successfully"); - } else { - Serial.printf("Bluetooth sleep enable failed: %s\n", esp_err_to_name(errBLESleep)); - } -#endif - -#if CONFIG_IDF_TARGET_ESP32C3 - esp_pm_config_esp32c3_t pm_config; -#elif CONFIG_IDF_TARGET_ESP32S3 - esp_pm_config_esp32s3_t pm_config; -#elif CONFIG_IDF_TARGET_ESP32 - esp_pm_config_esp32_t pm_config; -#elif CONFIG_IDF_TARGET_ESP32C6 - esp_pm_config_t pm_config; -#endif - - // Configure Power Management - pm_config = { .max_freq_mhz = 80, .min_freq_mhz = 40, .light_sleep_enable = true }; - esp_err_t errPM = esp_pm_configure(&pm_config); - if (errPM == ESP_OK) { - Serial.println("Power Management configured successfully"); - } else { - Serial.printf("Power Management failed to configure: %d\r\n", errPM); - } -#endif } void loop() { @@ -289,10 +253,6 @@ void loop() { if (!the_mesh.hasPendingWork()) { #if defined(NRF52_PLATFORM) board.sleep(0); // nrf ignores seconds param, sleeps whenever possible -#elif defined(ESP32_PLATFORM) - if (!serial_interface.isReadBusy() && !serial_interface.isWriteBusy()) { // BLE is not busy - vTaskDelay(pdMS_TO_TICKS(10)); // attempt to sleep - } #endif } diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index c311c941..12d0b0c3 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -1031,11 +1031,3 @@ void MyMesh::loop() { uptime_millis += now - last_millis; last_millis = now; } - -// To check if there is pending work -bool MyMesh::hasPendingWork() const { -#if defined(WITH_BRIDGE) - if (bridge.isRunning()) return true; // bridge needs WiFi radio, can't sleep -#endif - return _mgr->getOutboundTotal() > 0; -} diff --git a/examples/simple_room_server/MyMesh.h b/examples/simple_room_server/MyMesh.h index 380e54da..e9e53ec9 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -225,7 +225,4 @@ public: void clearStats() override; void handleCommand(uint32_t sender_timestamp, char* command, char* reply); void loop(); - - // To check if there is pending work - bool hasPendingWork() const; }; diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index ad8aa914..a3798b21 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -18,9 +18,6 @@ void halt() { static char command[MAX_POST_TEXT_LEN+1]; -// For power saving -unsigned long POWERSAVING_FIRSTSLEEP_SECS = 120; // The first sleep (if enabled) from boot - void setup() { Serial.begin(115200); delay(1000); @@ -118,14 +115,4 @@ void loop() { ui_task.loop(); #endif rtc_clock.tick(); - - if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) { -#if defined(NRF52_PLATFORM) - board.sleep(0); // nrf ignores seconds param, sleeps whenever possible -#else - if (the_mesh.millisHasNowPassed(POWERSAVING_FIRSTSLEEP_SECS * 1000)) { // To check if it is time to sleep - board.sleep(30); // Sleep. Wake up after a while or when receiving a LoRa packet - } -#endif - } } diff --git a/src/MeshCore.h b/src/MeshCore.h index 846b8f05..89e60b1f 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -53,6 +53,10 @@ public: virtual void onAfterTransmit() { } virtual void reboot() = 0; virtual void powerOff() { /* no op */ } + // Called by example setup() functions to signal that boot is complete. + // Boards may override to stop a boot-indicator LED sequence or similar. + // Default no-op: boards that don't care need not implement anything. + virtual void onBootComplete() { /* no op */ } virtual uint32_t getIRQGpio() { return -1; } // not supported. Returns DIO1 (SX1262) and DIO0 (SX127x) virtual void sleep(uint32_t secs) { /* no op */ } virtual uint32_t getGpio() { return 0; } diff --git a/src/helpers/ArduinoHelpers.cpp b/src/helpers/ArduinoHelpers.cpp deleted file mode 100644 index feb77a79..00000000 --- a/src/helpers/ArduinoHelpers.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include - -extern "C" { - __attribute__((section(".persistent_magic"))) uint32_t persistent_magic; - __attribute__((section(".persistent_data"))) uint32_t persistent_time; -} \ No newline at end of file diff --git a/src/helpers/ArduinoHelpers.h b/src/helpers/ArduinoHelpers.h index 9b50b98c..97596daa 100644 --- a/src/helpers/ArduinoHelpers.h +++ b/src/helpers/ArduinoHelpers.h @@ -3,57 +3,19 @@ #include #include -#ifdef NRF52_PLATFORM -#define CLOCK_MAGIC_NUM 0xAA55CC33 -#define RTC_TIME_MIN 1772323200 // 1 Mar 2026 - -extern uint32_t persistent_magic; -extern uint32_t persistent_time; -#endif - class VolatileRTCClock : public mesh::RTCClock { uint32_t base_time; uint64_t accumulator; unsigned long prev_millis; - public: - VolatileRTCClock() { -#ifdef NRF52_PLATFORM - if (persistent_magic == CLOCK_MAGIC_NUM && persistent_time >= RTC_TIME_MIN) { - base_time = persistent_time; - } else { - base_time = RTC_TIME_MIN; - } -#else - base_time = 1715770351; -#endif - - accumulator = 0; - prev_millis = millis(); - } - + VolatileRTCClock() { base_time = 1715770351; accumulator = 0; prev_millis = millis(); } // 15 May 2024, 8:50pm uint32_t getCurrentTime() override { return base_time + accumulator/1000; } - - void setCurrentTime(uint32_t time) override { - base_time = time; - accumulator = 0; - prev_millis = millis(); - -#ifdef NRF52_PLATFORM - persistent_magic = CLOCK_MAGIC_NUM; - persistent_time = time; -#endif - } + void setCurrentTime(uint32_t time) override { base_time = time; accumulator = 0; prev_millis = millis(); } void tick() override { unsigned long now = millis(); accumulator += (now - prev_millis); prev_millis = now; - -#ifdef NRF52_PLATFORM - persistent_magic = CLOCK_MAGIC_NUM; - persistent_time = getCurrentTime(); -#endif } }; diff --git a/src/helpers/ArduinoSerialInterface.cpp b/src/helpers/ArduinoSerialInterface.cpp index 6b443974..a01fa586 100644 --- a/src/helpers/ArduinoSerialInterface.cpp +++ b/src/helpers/ArduinoSerialInterface.cpp @@ -17,10 +17,6 @@ bool ArduinoSerialInterface::isConnected() const { return true; // no way of knowing, so assume yes } -bool ArduinoSerialInterface::isReadBusy() const { - return false; -} - bool ArduinoSerialInterface::isWriteBusy() const { return false; } diff --git a/src/helpers/ArduinoSerialInterface.h b/src/helpers/ArduinoSerialInterface.h index 4fa2b75d..c4086353 100644 --- a/src/helpers/ArduinoSerialInterface.h +++ b/src/helpers/ArduinoSerialInterface.h @@ -28,8 +28,7 @@ public: bool isConnected() const override; - bool isReadBusy() const override; bool isWriteBusy() const override; size_t writeFrame(const uint8_t src[], size_t len) override; size_t checkRecvFrame(uint8_t dest[]) override; -}; +}; \ No newline at end of file diff --git a/src/helpers/BaseChatMesh.h b/src/helpers/BaseChatMesh.h index c6027468..d9878547 100644 --- a/src/helpers/BaseChatMesh.h +++ b/src/helpers/BaseChatMesh.h @@ -38,6 +38,8 @@ public: #define MAX_CONTACTS 32 #endif +#define MAX_ANON_CONTACTS 8 + #ifndef MAX_CONNECTIONS #define MAX_CONNECTIONS 16 #endif @@ -59,9 +61,9 @@ class BaseChatMesh : public mesh::Mesh { friend class ContactsIterator; - ContactInfo contacts[MAX_CONTACTS]; + ContactInfo contacts[MAX_CONTACTS+MAX_ANON_CONTACTS]; int num_contacts; - int sort_array[MAX_CONTACTS]; + int sort_array[MAX_CONTACTS+MAX_ANON_CONTACTS]; int matching_peer_indexes[MAX_SEARCH_RESULTS]; unsigned long txt_send_timeout; #ifdef MAX_GROUP_CHANNELS @@ -73,7 +75,7 @@ class BaseChatMesh : public mesh::Mesh { ConnectionInfo connections[MAX_CONNECTIONS]; mesh::Packet* composeMsgPacket(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char *text, uint32_t& expected_ack); - void sendAckTo(const ContactInfo& dest, uint32_t ack_hash); + void sendAckTo(const ContactInfo& dest, const uint8_t* ack_hash, uint8_t ack_len=4); protected: BaseChatMesh(mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::PacketManager& mgr, mesh::MeshTables& tables) @@ -97,7 +99,7 @@ protected: num_contacts = MAX_ANON_CONTACTS; // seed the first contacts for anon requests } void populateContactFromAdvert(ContactInfo& ci, const mesh::Identity& id, const AdvertDataParser& parser, uint32_t timestamp); - ContactInfo* allocateContactSlot(); // helper to find slot for new contact + ContactInfo* allocateContactSlot(bool transient_only=false); // helper to find slot for new contact // 'UI' concepts, for sub-classes to implement virtual bool isAutoAddEnabled() const { return true; } diff --git a/src/helpers/BaseSerialInterface.h b/src/helpers/BaseSerialInterface.h index 8ff110eb..e9a3f2ab 100644 --- a/src/helpers/BaseSerialInterface.h +++ b/src/helpers/BaseSerialInterface.h @@ -15,7 +15,6 @@ public: virtual bool isConnected() const = 0; - virtual bool isReadBusy() const = 0; virtual bool isWriteBusy() const = 0; virtual size_t writeFrame(const uint8_t src[], size_t len) = 0; virtual size_t checkRecvFrame(uint8_t dest[]) = 0; diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index 48730fdd..a4cbf2a9 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -14,7 +14,6 @@ #include #include "soc/rtc.h" #include "esp_system.h" -#include class ESP32Board : public mesh::MainBoard { protected: @@ -25,7 +24,7 @@ protected: public: void begin() { // for future use, sub-classes SHOULD call this from their begin() - startup_reason = BD_STARTUP_NORMAL; + startup_reason = BD_STARTUP_NORMAL; #ifdef ESP32_CPU_FREQ setCpuFrequencyMhz(ESP32_CPU_FREQ); @@ -48,7 +47,7 @@ public: #endif #else Wire.begin(); - #endif + #endif } // Temperature from ESP32 MCU @@ -63,31 +62,6 @@ public: return raw / 4; } - void powerOff() override { - enterDeepSleep(0); // Do not wakeup - } - - void enterDeepSleep(uint32_t secs) { - // Clear stale wakeup sources to avoid ghost wakeup - // This is required when Power Management and automatic lightsleep are enabled - esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000ULL); - } - - // Keep LoRa inactive during deepsleep - digitalWrite(P_LORA_NSS, HIGH); -#if CONFIG_IDF_TARGET_ESP32C3 - gpio_hold_en((gpio_num_t)P_LORA_NSS); -#else - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); -#endif - - // Finally set ESP32 into deepsleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - uint32_t getIRQGpio() override { return P_LORA_DIO_1; // default for SX1262 } @@ -99,15 +73,8 @@ public: return; } - #if defined(ARDUINO_USB_CDC_ON_BOOT) && ARDUINO_USB_CDC_ON_BOOT - if (Serial) { - delay(1); - return; - } - #endif - // Set GPIO wakeup - gpio_num_t wakeupPin = (gpio_num_t)getIRQGpio(); + gpio_num_t wakeupPin = (gpio_num_t)getIRQGpio(); // Configure timer wakeup if (secs > 0) { @@ -188,68 +155,21 @@ public: void setInhibitSleep(bool inhibit) { inhibit_sleep = inhibit; } - - uint32_t getResetReason() const override { - return esp_reset_reason(); - } - - // https://docs.espressif.com/projects/esp-idf/en/v4.4.7/esp32/api-reference/system/system.html - const char *getResetReasonString(uint32_t reason) { - switch (reason) { - case ESP_RST_UNKNOWN: - return "Unknown or first boot"; - case ESP_RST_POWERON: - return "Power-on reset"; - case ESP_RST_EXT: - return "External reset"; - case ESP_RST_SW: - return "Software reset"; - case ESP_RST_PANIC: - return "Panic / exception reset"; - case ESP_RST_INT_WDT: - return "Interrupt watchdog reset"; - case ESP_RST_TASK_WDT: - return "Task watchdog reset"; - case ESP_RST_WDT: - return "Other watchdog reset"; - case ESP_RST_DEEPSLEEP: - return "Wake from deep sleep"; - case ESP_RST_BROWNOUT: - return "Brownout (low voltage)"; - case ESP_RST_SDIO: - return "SDIO reset"; - default: - static char buf[40]; - snprintf(buf, sizeof(buf), "Unknown reset reason (%d)", reason); - return buf; - } - } }; -static RTC_NOINIT_ATTR uint32_t _rtc_backup_time; -static RTC_NOINIT_ATTR uint32_t _rtc_backup_magic; -#define RTC_BACKUP_MAGIC 0xAA55CC33 -#define RTC_TIME_MIN 1772323200 // 1 Mar 2026 - class ESP32RTCClock : public mesh::RTCClock { public: ESP32RTCClock() { } void begin() { esp_reset_reason_t reason = esp_reset_reason(); - if (reason == ESP_RST_DEEPSLEEP) { - return; // ESP-IDF preserves system time across deep sleep - } - // All other resets (power-on, crash, WDT, brownout) lose system time. - // Restore from RTC backup if valid, otherwise use hardcoded seed. - struct timeval tv; - if (_rtc_backup_magic == RTC_BACKUP_MAGIC && _rtc_backup_time > RTC_TIME_MIN) { - tv.tv_sec = _rtc_backup_time; - } else { - tv.tv_sec = 1772323200; // 1 Mar 2026 - } + if (reason == ESP_RST_POWERON) { + // start with some date/time in the recent past + struct timeval tv; + tv.tv_sec = 1715770351; // 15 May 2024, 8:50pm tv.tv_usec = 0; settimeofday(&tv, NULL); } + } uint32_t getCurrentTime() override { time_t _now; time(&_now); @@ -260,16 +180,6 @@ public: tv.tv_sec = time; tv.tv_usec = 0; settimeofday(&tv, NULL); - _rtc_backup_time = time; - _rtc_backup_magic = RTC_BACKUP_MAGIC; - } - void tick() override { - time_t now; - time(&now); - if (now > RTC_TIME_MIN && (uint32_t)now != _rtc_backup_time) { - _rtc_backup_time = (uint32_t)now; - _rtc_backup_magic = RTC_BACKUP_MAGIC; - } } }; diff --git a/src/helpers/MeshadventurerBoard.h b/src/helpers/MeshadventurerBoard.h index 0325161d..65e11102 100644 --- a/src/helpers/MeshadventurerBoard.h +++ b/src/helpers/MeshadventurerBoard.h @@ -15,6 +15,8 @@ #include "ESP32Board.h" +#include + class MeshadventurerBoard : public ESP32Board { public: @@ -33,6 +35,34 @@ public: } } + void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + + // Make sure the DIO1 and NSS GPIOs are held on required levels during deep sleep + rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); + + rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); + + if (pin_wake_btn < 0) { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet + } else { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn + } + + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000); + } + + // Finally set ESP32 into sleep + esp_deep_sleep_start(); // CPU halts here and never returns! + } + + void powerOff() override { + // TODO: re-enable this when there is a definite wake-up source pin: + // enterDeepSleep(0); + } + uint16_t getBattMilliVolts() override { analogReadResolution(12); diff --git a/src/helpers/NRF52Board.cpp b/src/helpers/NRF52Board.cpp index 2c8753d4..17265f04 100644 --- a/src/helpers/NRF52Board.cpp +++ b/src/helpers/NRF52Board.cpp @@ -66,20 +66,6 @@ void NRF52Board::initPowerMgr() { } } -bool NRF52Board::isExternalPowered() { - // Check if SoftDevice is enabled before using its API - uint8_t sd_enabled = 0; - sd_softdevice_is_enabled(&sd_enabled); - - if (sd_enabled) { - uint32_t usb_status; - sd_power_usbregstatus_get(&usb_status); - return (usb_status & POWER_USBREGSTATUS_VBUSDETECT_Msk) != 0; - } else { - return (NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk) != 0; - } -} - const char* NRF52Board::getResetReasonString(uint32_t reason) { if (reason & POWER_RESETREAS_RESETPIN_Msk) return "Reset Pin"; if (reason & POWER_RESETREAS_DOG_Msk) return "Watchdog"; @@ -251,6 +237,20 @@ void NRF52BoardDCDC::begin() { } } +bool NRF52Board::isExternalPowered() { + // Check if SoftDevice is enabled before using its API + uint8_t sd_enabled = 0; + sd_softdevice_is_enabled(&sd_enabled); + + if (sd_enabled) { + uint32_t usb_status; + sd_power_usbregstatus_get(&usb_status); + return (usb_status & POWER_USBREGSTATUS_VBUSDETECT_Msk) != 0; + } else { + return (NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk) != 0; + } +} + void NRF52Board::sleep(uint32_t secs) { // Clear FPU interrupt flags to avoid insomnia // see errata 87 for details https://docs.nordicsemi.com/bundle/errata_nRF52840_Rev3/page/ERR/nRF52840/Rev3/latest/anomaly_840_87.html diff --git a/src/helpers/NRF52Board.h b/src/helpers/NRF52Board.h index 96f67dc9..17065cf4 100644 --- a/src/helpers/NRF52Board.h +++ b/src/helpers/NRF52Board.h @@ -53,9 +53,9 @@ public: virtual bool getBootloaderVersion(char* version, size_t max_len) override; virtual bool startOTAUpdate(const char *id, char reply[]) override; virtual void sleep(uint32_t secs) override; + bool isExternalPowered() override; #ifdef NRF52_POWER_MANAGEMENT - bool isExternalPowered() override; uint16_t getBootVoltage() override { return boot_voltage_mv; } virtual uint32_t getResetReason() const override { return reset_reason; } uint8_t getShutdownReason() const override { return shutdown_reason; } @@ -67,7 +67,7 @@ public: /* * The NRF52 has an internal DC/DC regulator that allows increased efficiency * compared to the LDO regulator. For being able to use it, the module/board - * needs to have the required inductors and and capacitors populated. If the + * needs to have the required inductors and capacitors populated. If the * hardware requirements are met, this subclass can be used to enable the DC/DC * regulator. */ diff --git a/src/helpers/SensorManager.h b/src/helpers/SensorManager.h index d4aa63b7..89a174c2 100644 --- a/src/helpers/SensorManager.h +++ b/src/helpers/SensorManager.h @@ -2,7 +2,6 @@ #include #include "sensors/LocationProvider.h" -#include #define TELEM_PERM_BASE 0x01 // 'base' permission includes battery #define TELEM_PERM_LOCATION 0x02 @@ -16,7 +15,6 @@ public: double node_altitude; // altitude in meters SensorManager() { node_lat = 0; node_lon = 0; node_altitude = 0; } - virtual bool i2c_probe(TwoWire& wire, uint8_t addr) { return false; } virtual bool begin() { return false; } virtual bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { return false; } virtual void loop() { } diff --git a/src/helpers/esp32/SerialBLEInterface.cpp b/src/helpers/esp32/SerialBLEInterface.cpp index 50e1501e..dcfa0e1e 100644 --- a/src/helpers/esp32/SerialBLEInterface.cpp +++ b/src/helpers/esp32/SerialBLEInterface.cpp @@ -182,10 +182,6 @@ size_t SerialBLEInterface::writeFrame(const uint8_t src[], size_t len) { #define BLE_WRITE_MIN_INTERVAL 60 -bool SerialBLEInterface::isReadBusy() const { - return (recv_queue_len > 0); -} - bool SerialBLEInterface::isWriteBusy() const { return millis() < _last_write + BLE_WRITE_MIN_INTERVAL; // still too soon to start another write? } diff --git a/src/helpers/esp32/SerialBLEInterface.h b/src/helpers/esp32/SerialBLEInterface.h index 19e024b0..965e90fd 100644 --- a/src/helpers/esp32/SerialBLEInterface.h +++ b/src/helpers/esp32/SerialBLEInterface.h @@ -76,7 +76,6 @@ public: bool isConnected() const override; - bool isReadBusy() const override; bool isWriteBusy() const override; size_t writeFrame(const uint8_t src[], size_t len) override; size_t checkRecvFrame(uint8_t dest[]) override; diff --git a/src/helpers/esp32/SerialWifiInterface.cpp b/src/helpers/esp32/SerialWifiInterface.cpp index bdecb1a9..462e3ecc 100644 --- a/src/helpers/esp32/SerialWifiInterface.cpp +++ b/src/helpers/esp32/SerialWifiInterface.cpp @@ -39,10 +39,6 @@ size_t SerialWifiInterface::writeFrame(const uint8_t src[], size_t len) { return 0; } -bool SerialWifiInterface::isReadBusy() const { - return false; -} - bool SerialWifiInterface::isWriteBusy() const { return false; } diff --git a/src/helpers/esp32/SerialWifiInterface.h b/src/helpers/esp32/SerialWifiInterface.h index 1ff1d83d..19291497 100644 --- a/src/helpers/esp32/SerialWifiInterface.h +++ b/src/helpers/esp32/SerialWifiInterface.h @@ -52,7 +52,6 @@ public: bool isEnabled() const override { return _isEnabled; } bool isConnected() const override; - bool isReadBusy() const override; bool isWriteBusy() const override; size_t writeFrame(const uint8_t src[], size_t len) override; diff --git a/src/helpers/esp32/TBeamBoard.h b/src/helpers/esp32/TBeamBoard.h index 543226d6..98bd16bf 100644 --- a/src/helpers/esp32/TBeamBoard.h +++ b/src/helpers/esp32/TBeamBoard.h @@ -86,6 +86,7 @@ #include #include "XPowersLib.h" #include "helpers/ESP32Board.h" +#include class TBeamBoard : public ESP32Board { XPowersLibInterface *PMU = NULL; @@ -130,6 +131,29 @@ public: } #endif + void enterDeepSleep(uint32_t secs, int pin_wake_btn) { + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + + // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep + rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); + + rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); + + if (pin_wake_btn < 0) { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet + } else { + esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn + } + + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000); + } + + // Finally set ESP32 into sleep + esp_deep_sleep_start(); // CPU halts here and never returns! +} + uint16_t getBattMilliVolts(){ return PMU->getBattVoltage(); } diff --git a/src/helpers/nrf52/SerialBLEInterface.cpp b/src/helpers/nrf52/SerialBLEInterface.cpp index a846e744..75a4e3b0 100644 --- a/src/helpers/nrf52/SerialBLEInterface.cpp +++ b/src/helpers/nrf52/SerialBLEInterface.cpp @@ -401,10 +401,6 @@ bool SerialBLEInterface::isConnected() const { return _isDeviceConnected && Bluefruit.connected() > 0; } -bool SerialBLEInterface::isReadBusy() const { - return (recv_queue_len > 0); -} - bool SerialBLEInterface::isWriteBusy() const { return send_queue_len >= (FRAME_QUEUE_SIZE * 2 / 3); } diff --git a/src/helpers/nrf52/SerialBLEInterface.h b/src/helpers/nrf52/SerialBLEInterface.h index d3cc5055..de103054 100644 --- a/src/helpers/nrf52/SerialBLEInterface.h +++ b/src/helpers/nrf52/SerialBLEInterface.h @@ -66,7 +66,6 @@ public: void disable() override; bool isEnabled() const override { return _isEnabled; } bool isConnected() const override; - bool isReadBusy() const override; bool isWriteBusy() const override; size_t writeFrame(const uint8_t src[], size_t len) override; size_t checkRecvFrame(uint8_t dest[]) override; diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index ea57677f..73842d9e 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -15,7 +15,6 @@ #if ENV_INCLUDE_BME680_BSEC #ifndef TELEM_BME680_ADDRESS #define TELEM_BME680_ADDRESS 0x76 -#define TELEM_BME680_ADDRESS_2 0x77 #endif #define TELEM_BME680_SEALEVELPRESSURE_HPA (1013.25) #include @@ -41,7 +40,6 @@ static uint32_t bsec_last_save_ms = 0; #ifdef ENV_INCLUDE_BME680 #ifndef TELEM_BME680_ADDRESS #define TELEM_BME680_ADDRESS 0x76 -#define TELEM_BME680_ADDRESS_2 0x77 #endif #define TELEM_BME680_SEALEVELPRESSURE_HPA (1013.25) #include @@ -65,7 +63,6 @@ static Adafruit_AHTX0 AHTX0; #if ENV_INCLUDE_BME280 #ifndef TELEM_BME280_ADDRESS #define TELEM_BME280_ADDRESS 0x76 // BME280 environmental sensor I2C address -#define TELEM_BME280_ADDRESS_2 0x77 #endif #define TELEM_BME280_SEALEVELPRESSURE_HPA (1013.25) // Atmospheric pressure at sea level #include @@ -75,7 +72,6 @@ static Adafruit_BME280 BME280; #if ENV_INCLUDE_BMP280 #ifndef TELEM_BMP280_ADDRESS #define TELEM_BMP280_ADDRESS 0x76 // BMP280 environmental sensor I2C address -#define TELEM_BMP280_ADDRESS_2 0x77 #endif #define TELEM_BMP280_SEALEVELPRESSURE_HPA (1013.25) // Atmospheric pressure at sea level #include @@ -561,27 +557,15 @@ static const SensorDef SENSOR_TABLE[] = { #endif #ifdef ENV_INCLUDE_BME680 { TELEM_BME680_ADDRESS, "BME680", init_bme680, query_bme680 }, - #ifdef TELEM_BME680_ADDRESS_2 - { TELEM_BME680_ADDRESS_2, "BME680", init_bme680, query_bme680 }, - #endif #endif #if ENV_INCLUDE_BME680_BSEC { TELEM_BME680_ADDRESS, "BME680+BSEC", init_bme680_bsec, query_bme680_bsec }, - #ifdef TELEM_BME680_ADDRESS_2 - { TELEM_BME680_ADDRESS_2, "BME680+BSEC", init_bme680_bsec, query_bme680_bsec }, - #endif #endif #if ENV_INCLUDE_BME280 { TELEM_BME280_ADDRESS, "BME280", init_bme280, query_bme280 }, - #ifdef TELEM_BME280_ADDRESS_2 - { TELEM_BME280_ADDRESS_2, "BME280", init_bme280, query_bme280 }, - #endif #endif #if ENV_INCLUDE_BMP280 { TELEM_BMP280_ADDRESS, "BMP280", init_bmp280, query_bmp280 }, - #ifdef TELEM_BMP280_ADDRESS_2 - { TELEM_BMP280_ADDRESS_2, "BMP280", init_bmp280, query_bmp280 }, - #endif #endif #if ENV_INCLUDE_SHTC3 { 0x70, "SHTC3", init_shtc3, query_shtc3 }, diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index b91d2dc3..29147c89 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -41,7 +41,6 @@ public: #else EnvironmentSensorManager(){}; #endif - bool i2c_probe(TwoWire& wire, uint8_t addr) override; bool begin() override; bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; #if ENV_INCLUDE_GPS || defined(ENV_INCLUDE_BME680_BSEC) diff --git a/src/helpers/ui/ST7735Display.cpp b/src/helpers/ui/ST7735Display.cpp index 8983c911..a6087dd8 100644 --- a/src/helpers/ui/ST7735Display.cpp +++ b/src/helpers/ui/ST7735Display.cpp @@ -63,15 +63,6 @@ void ST7735Display::turnOff() { #else digitalWrite(PIN_TFT_LEDA_CTL, LOW); #endif - - // Prevent back-powering to save 2.8 mA - pinMode(PIN_TFT_CS, INPUT); - pinMode(PIN_TFT_DC, INPUT); - pinMode(PIN_TFT_SDA, INPUT); - pinMode(PIN_TFT_SCL, INPUT); - pinMode(PIN_TFT_RST, INPUT); - pinMode(PIN_TFT_LEDA_CTL, INPUT); - _isOn = false; if (_peripher_power) _peripher_power->release(); From 7d8bb1d319488bf64139d93206b01abfed5ea401 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 15 Jun 2026 16:05:23 -0700 Subject: [PATCH 127/214] Keep Halo branch focused on direct retry --- docs/cli_commands.md | 490 ++++++++---------------------- examples/simple_repeater/MyMesh.h | 39 --- examples/simple_repeater/main.cpp | 5 - src/Mesh.cpp | 308 ------------------- src/Mesh.h | 68 ----- 5 files changed, 127 insertions(+), 783 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index d9a225e8..5c5ee670 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -28,25 +28,12 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Usage:** - `reboot` -**Note:** No reply is sent. - ---- - -### Power-off the node -**Usage:** -- `poweroff`, or -- `shutdown` - -**Note:** No reply is sent. - --- ### Reset the clock and reboot **Usage:** - `clkreboot` -**Note:** No reply is sent. - --- ### Sync the clock with the remote device @@ -128,66 +115,6 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- -### Send flood text to `#repeaters` channel - -**Usage:** -- `send text.flood ` - -**Notes:** -- Sends a `PAYLOAD_TYPE_GRP_TXT` flood message using the built-in `#repeaters` channel key. -- Message format is `: `. - ---- - -### View or change automatic low-battery alerts to `#repeaters` - -**Usage:** -- `get battery.alert` -- `set battery.alert ` -- `get battery.alert.low` -- `set battery.alert.low ` -- `get battery.alert.critical` -- `set battery.alert.critical ` - -**Parameters:** -- `state`: `on` (enable) or `off` (disable) -- `percent`: Battery percentage threshold - -**Default:** `off` - -**Default thresholds:** `20` for `battery.alert.low`, `10` for `battery.alert.critical` - -**Notes:** -- When enabled, sends a `#repeaters` flood text warning if voltage is above `1 V` and the battery estimate is below `battery.alert.low`. -- Warnings repeat every `24` hours, or every `12` hours below `battery.alert.critical`. -- `battery.alert.critical` must be lower than `battery.alert.low`. - ---- - -### Get or set recent repeater prefix/SNR -**Usage:** -- `get recent.repeater` -- `get recent.repeater ` -- `get recent.repeater page ` -- `set recent.repeater ` - -**Parameters:** -- `prefix_hex_6`: Exactly 3 bytes of next-hop prefix in hex (6 chars) -- `snr_db`: SNR in dB (supports decimals; stored at x4 precision) -- `page`: 1-based page number - -**Notes:** -- `set` stores or updates the prefix in the recent repeater table. -- Output rows are `prefix,snr` with optional `,l` for locked manual entries. -- Rows are sorted by prefix width (3-byte, 2-byte, 1-byte), then SNR descending. -- A full direct retry failure lowers the stored SNR by `0.25 dB`. -- If a full failure has no row yet, it first seeds the row at the active retry cutoff + `2.5 dB`, then applies the `0.25 dB` penalty. -- Serial CLI page size is fixed at `128` rows; choose page with `get recent.repeater `. -- Over LoRa remote CLI, page size is fixed at `7` rows; choose page with `get recent.repeater `. -- Repeaters can use adjacent entries in this table to short-circuit non-TRACE direct packets when this node appears later in the direct path. - ---- - ## Statistics ### Clear Stats @@ -292,20 +219,6 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- -#### View or change the boosted receive gain mode -**Usage:** -- `get radio.rxgain` -- `set radio.rxgain ` - -**Parameters:** -- `state`: `on`|`off` - -**Default:** `off` - -**Note:** Only available on SX1262 and SX1268 based boards. - ---- - #### Change the radio parameters for a set duration **Usage:** - `tempradio ,,,,` @@ -336,7 +249,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- -#### View or change this node's rx boosted gain mode (SX12xx only, v1.14.1+) +#### View or change this node's rx boosted gain mode (SX12xx and LR1110, v1.14.1+) **Usage:** - `get radio.rxgain` - `set radio.rxgain ` @@ -469,7 +382,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Note:** `|` characters are translated to newlines -**Note:** Requires firmware 1.12.+ +**Note:** Requires firmware 1.12+ --- @@ -518,18 +431,6 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- -#### View or set reboot interval (Repeater and room server) -**Usage:** -- `get reboot.interval` -- `set reboot.interval ` - -**Parameters:** -- `hours`: 0-255. 0 is disabled - -**Default:** `0` (disabled) - ---- - ### Routing #### View or change this node's repeat flag @@ -560,7 +461,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Note:** the 'path.hash.mode' sets the low-level ID/hash encoding size used when the repeater adverts. This setting has no impact on what packet ID/hash size this repeater forwards, all sizes should be forwarded on firmware >= 1.14. This feature was added in firmware 1.14 -**Temporary Note:** adverts with ID/hash sizes of 2 or 3 bytes may have limited flood propogation in your network while this feature is new as v1.13.0 firmware and older will drop packets with multibyte path ID/hashes as only 1-byte hashes are suppored. Consider your install base of firmware >=1.14 has reached a criticality for effective network flooding before implementing higher ID/hash sizes. +**Temporary Note:** adverts with ID/hash sizes of 2 or 3 bytes may have limited flood propagation in your network while this feature is new as v1.13.0 firmware and older will drop packets with multibyte path ID/hashes as only 1-byte hashes are supported. Consider your install base of firmware >=1.14 has reached a criticality for effective network flooding before implementing higher ID/hash sizes. --- @@ -578,7 +479,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Default:** `off` -**Note:** When it is enabled, repeaters will now reject flood packets which look like they are in a loop. This has been happening recently in some meshes when there is just a single 'bad' repeater firmware out there (prob some forked or custom firmware). If the payload is messed with, then forwarded, the same packet ends up causing a packet storm, repeated up to the max 64 hops. This feature was added in firmware 1.14 +**Note:** When it is enabled, repeaters will now reject flood packets which look like they are in a loop. This has been happening recently in some meshes when there is just a single 'bad' repeater firmware out there (probably some forked or custom firmware). If the payload is messed with, then forwarded, the same packet ends up causing a packet storm, repeated up to the max 64 hops. This feature was added in firmware 1.14 **Example:** If preference is `loop.detect minimal`, and a 1-byte path size packet is received, the repeater will see if its own ID/hash is already in the path. If it's already encoded 4 times, it will reject the packet. If the packet uses 2-byte path size, and repeater's own ID/hash is already encoded 2 times, it rejects. If the packet uses 3-byte path size, and the repeater's own ID/hash is already encoded 1 time, it rejects. @@ -594,6 +495,8 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Default:** `0.5` +**Note:** When multiple nearby repeaters all hear the same flood packet, each waits a random amount of time before retransmitting to avoid simultaneous collisions. This factor scales the size of that random window. Higher values reduce collision risk at the cost of added latency. `0` disables the window entirely. + --- #### View or change the retransmit delay factor for direct traffic @@ -604,116 +507,9 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Parameters:** - `value`: Direct transmit delay factor (0-2) -**Default:** `0.3` +**Default:** `0.2` -**Note:** Direct retry waits include the same airtime-based randomized delay calculation as direct retransmits, so this factor also controls retry echo windows. - ---- - -#### View or change whether direct retries use the recent repeater SNR gate -**Usage:** -- `get direct.retry.heard` -- `set direct.retry.heard ` - -**Parameters:** -- `state`: `on`|`off` - -**Default:** `on` - -**Note:** When enabled, the recent repeater table is the only direct retry eligibility gate. Prefixes missing from the table are assumed reachable; prefixes in the table below the active SNR gate are blocked. Neighbor data is not used. - ---- - -#### View or change adaptive coding rate for direct retry packets -**Usage:** -- `get direct.retry.cr` -- `set direct.retry.cr ,,,` -- `set direct.retry.cr off` - -**Parameters:** -- `cr4_min`: SNR in dB where retry packets use `CR4` -- `cr5_min`: SNR in dB where retry packets use `CR5` -- `cr7_min`: SNR in dB where retry packets use `CR7` -- `cr8_max`: SNR in dB where retry packets use `CR8` - -**Default:** `10.0,7.5,2.5,2.5` - -**Note:** DM retry packets use the next-hop SNR from a recent repeater table entry to pick a local transmit coding rate; if no recent entry is available, retry packets use `CR5`. With the default, SNR `10.0 dB` and up uses `CR4`, SNR `7.5 dB` and up uses `CR5`, SNR `2.5 dB` and down uses `CR8`, and the middle band uses `CR7`. `CR6` is never selected. Use `set direct.retry.cr off` to disable adaptive coding-rate overrides. If adaptive selection chooses `CR4`, retries after the third attempt use `CR5`. - ---- - -#### View or change the SNR margin used for direct retry eligibility -**Usage:** -- `get direct.retry.margin` -- `set direct.retry.margin ` - -**Parameters:** -- `value`: Rooftop preset margin in dB above the SF-specific receive floor (minimum `0`, maximum `40`, quarter-dB precision, default `5.0`) - -**Default:** `5.0` - -**Note:** `get direct.retry.margin` returns the active preset's effective margin. The retry gate uses the active SF floor of `SF5=-2.5`, `SF6=-5`, `SF7=-7.5`, `SF8=-10`, `SF9=-12.5`, `SF10=-15`, `SF11=-17.5`, `SF12=-20`, then adds this margin. - ---- - -#### View or change the retry preset -**Usage:** -- `get retry.preset` -- `set retry.preset ` - -**Parameters:** -- `value`: `infra`|`rooftop`|`mobile` or `0`|`1`|`2` - -**Default:** `rooftop` (`1`) - -**Presets:** -- `infra` (`0`): `275 ms` direct base wait, `4` direct retries, `150 ms` added per direct retry, SNR gate is SF floor + `15 dB`; flood retry defaults to `1` retry and path gate `1` -- `rooftop` (`1`): `175 ms` direct base wait, `15` direct retries, `100 ms` added per direct retry, SNR gate is SF floor + `5 dB`; flood retry defaults to `3` retries and path gate `2` -- `mobile` (`2`): `175 ms` direct base wait, `15` direct retries, `50 ms` added per direct retry, SNR gate is the SF floor; flood retry defaults to `3` retries and path gate `1` - -**Note:** Selecting a preset copies those values into the direct retry settings and resets flood retry defaults. You can refine `direct.retry.margin`, `direct.retry.count`, `direct.retry.base`, `direct.retry.step`, `flood.retry.count`, or `flood.retry.path` afterward. Retry delay is `direct.txdelay` jitter + base wait + packet-length airtime wait + per-attempt step. - ---- - -#### View or change the number of direct retry attempts -**Usage:** -- `get direct.retry.count` -- `set direct.retry.count ` - -**Parameters:** -- `value`: Maximum retry attempts after initial TX (`1`-`15`) - -**Default:** `15` - -**Note:** The effective value is capped by total direct path length: paths of `3` hops or less use at most `8` retries, `4` hops use at most `12`, and `5+` hops use at most `15`. A queued resend is canceled early when the next-hop echo is heard. - ---- - -#### View or change the base direct retry wait (milliseconds) -**Usage:** -- `get direct.retry.base` -- `set direct.retry.base ` - -**Parameters:** -- `value`: Base wait in milliseconds (`10`-`5000`) - -**Default:** `175` - -**Note:** The configured base is added to packet-length airtime and `direct.txdelay` jitter. Preset defaults are already reduced to account for the added `direct.txdelay` component. - ---- - -#### View or change the direct retry per-attempt add time (milliseconds) -**Usage:** -- `get direct.retry.step` -- `set direct.retry.step ` - -**Parameters:** -- `value`: Milliseconds added per retry attempt (`0`-`5000`) - -**Default:** `100` - -**Note:** This controls the linear add after the first retry wait. For example, `base=300` and `step=150` adds `0`, `150`, `300`, ... ms across retry attempts. +**Note:** Same collision-avoidance random window as `txdelay`, but applied to direct (non-flood, routed) traffic. The default is lower because direct packets are addressed to a specific next hop, so far fewer nodes compete to retransmit them. --- @@ -727,6 +523,8 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Default:** `0.0` +**Note:** When enabled, repeaters that received a flood packet with a weak signal are held in a delay queue before processing, while those that received it with a strong signal process it immediately. This gives strong-signal paths forwarding priority. By the time weak-signal nodes process their copy, the packet may have already propagated and will be suppressed as a duplicate, reducing redundant retransmissions. + --- #### View or change the duty cycle limit @@ -854,114 +652,18 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- -#### View or change the number of flood retry attempts +#### Limit the number of hops for an unscoped flood message **Usage:** -- `get flood.retry.count` -- `set flood.retry.count ` +- `get flood.max.unscoped` +- `set flood.max.unscoped ` **Parameters:** -- `value`: Maximum retry attempts after initial flood TX (`0`-`15`) +- `value`: Maximum flood hop count (0-64) for a packet without a scope (no region set) -**Default:** `3` for `rooftop` and `mobile`, `1` for `infra` +**Default:** `0xFF` - indicates it hasn't been set, will track flood.max until it is. -**Note:** `0` disables flood retry. +**Note:** An alternative to `region denyf *`, setting `flood.max.unscoped` to a lower value such as `3` would allow for local unscoped messages to propagate, while preventing noisy neighbors from flooding a local region. ---- - -#### View or change the flood retry path gate -**Usage:** -- `get flood.retry.path` -- `set flood.retry.path ` - -**Parameters:** -- `value`: Maximum flood path length eligible for retry (`0`-`63`), or `off` to disable the gate - -**Default:** `2` for `rooftop`, `1` for `infra` and `mobile` - -**Note:** Prefixes in `flood.retry.ignore` do not count toward this path length. - ---- - -#### View or change whether advert packets are flood-retried -**Usage:** -- `get flood.retry.advert` -- `set flood.retry.advert ` - -**Parameters:** -- `state`: `on` or `off` - -**Default:** `off` - -**Note:** When this is `off`, node advert packets (`PAYLOAD_TYPE_ADVERT`, type `4`) are not queued for flood retry. - ---- - -#### View or change flood retry target prefixes -**Usage:** -- `get flood.retry.prefixes` -- `set flood.retry.prefixes ` - -**Parameters:** -- `prefixes`: Comma-separated 3-byte hex prefixes, such as `A1B2C3,D4E5F6`; use `none` or `off` to clear - -**Default:** `none` - -**Note:** Prefixes are stored as 3 bytes. Flood retry skips packets whose path already contains a matching target prefix. When prefixes are configured, only a downstream echo from one of those target prefixes cancels a queued retry; when no prefixes are configured, any downstream echo cancels it. Matching works with 3-byte, 2-byte, or 1-byte flood paths by comparing the matching leading bytes. - ---- - -#### View or change flood retry bridge mode -**Usage:** -- `get flood.retry.bridge` -- `set flood.retry.bridge ` - -**Parameters:** -- `state`: `on` or `off` - -**Default:** `off` - -**Note:** Bridge mode uses bucket definitions instead of the single `flood.retry.prefixes` target list. It also has an implicit unconfigured catch-all bucket. If a flood comes from one fresh configured bucket, retry continues until every other fresh configured bucket plus the catch-all bucket has been heard or `flood.retry.count` is exhausted. If a flood comes from an unconfigured or pathless source, retry targets every fresh configured bucket. This means one configured bucket bridges between that bucket and everything else. Prefixes in `flood.retry.ignore` never count as heard bridge targets. - ---- - -#### View or change flood retry bridge buckets -**Usage:** -- `get flood.retry.bucket.` -- `set flood.retry.bucket ` - -**Parameters:** -- `bucket`: Bucket number (`1`-`6`) -- `prefixes`: Up to 17 comma-separated 3-byte hex prefixes, such as `AABBCC,223344`; use `none` or `off` to clear - -**Default:** all buckets empty - -**Note:** Prefixes are stored as 3 bytes but match 3-byte, 2-byte, and 1-byte flood paths by comparing leading bytes. Bucket prefixes are included in bridge retry logic only if they were heard in the recent repeater table within the last hour. - ---- - -#### View or change flood retry ignored prefixes -**Usage:** -- `get flood.retry.ignore` -- `set flood.retry.ignore ` - -**Parameters:** -- `prefixes`: Up to 8 comma-separated 3-byte hex prefixes, such as `AABBCC,223344`; use `none` or `off` to clear - -**Default:** empty - -**Note:** In non-bridge retry, an echo whose last hop matches an ignored prefix does not cancel a queued retry as successful. In bridge mode, ignored prefixes do not count as a heard bridge bucket or as the implicit catch-all bucket when bridge retry decides whether every target has repeated the flood. - ---- - -#### Limit the number of hops for an advert flood message -**Usage:** -- `get flood.max.advert` -- `set flood.max.advert ` - -**Parameters:** -- `value`: Maximum flood hop count (0-64) for an advert packet - -**Default:** `8` --- @@ -991,32 +693,6 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- -#### View or set direct path overrides for the current remote client -**Usage:** -- `get outpath` -- `set outpath ` -- `set outpath direct` -- `set outpath clear` -- `set outpath flood` -- `get altpath` -- `set altpath ` -- `set altpath clear` - -**Parameters:** -- `hopN_hex`: Hop hash, `2`, `4`, or `6` hex characters. All hops must use the same width. - -**Notes:** -- These commands require remote client context; they target the caller's ACL entry. -- The path hash size is inferred from the hop hash width. -- `outpath` overrides the primary direct route used for replies to the caller. -- `direct` sets a zero-hop direct route for a caller reachable without repeaters. -- `clear` forgets the current direct path and allows normal path discovery to repopulate it. -- `flood` forces replies to use flood packets until the client logs in again. -- `altpath` is an optional second direct route used for duplicate response attempts. -- `set altpath clear` removes the duplicate route so only one reply is sent. - ---- - #### View or change this room server's 'read-only' flag **Usage:** - `get allow.read.only` @@ -1104,27 +780,6 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- -#### View or set the direct path override for the current remote client -**Usage:** -- `get outpath` -- `set outpath ` -- `set outpath direct` -- `set outpath clear` -- `set outpath flood` - -**Parameters:** -- `hopN_hex`: Hop hash, `2`, `4`, or `6` hex characters. All hops must use the same width. - -**Notes:** -- These commands require remote client context (they target the caller's ACL entry). -- The path hash size is inferred from the hop hash width. -- `outpath` overrides the primary direct route used for replies to the caller. -- `direct` sets a zero-hop direct route for a caller reachable without repeaters. -- `clear` forgets the current direct path and allows normal path discovery to repopulate it. -- `flood` forces replies to use flood packets until the client logs in again. - ---- - #### Create a new region **Usage:** - `region put [parent_name]` @@ -1135,6 +790,47 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### Define region hierarchy (single line) +**Usage:** +- `region def [ ...]` + +**Parameters (tokens):** Space-separated. A logical **cursor** starts at the wildcard `*`. + +- **`name`** — Create `name` as a child of the current cursor (equivalent to `region put name` with the cursor as parent). Cursor moves to `name`. +- **`name|jump`** *(or `name,jump`)* — Create `name` as a child of the current cursor, then move the cursor to `jump` (must already exist on the node, or have been created earlier in this command). `jump` is **not** the parent of `name`; use this form to pop back up and start another branch. + +**Behavior:** Each created region defaults to flood-allowed (same as `region put`). The reply is the resulting region tree (same format as bare `region`); review it before running `region save` to persist. On error, the reply is `Err - ...` and any regions placed before the failure remain on the node, just like a partial chain of `region put`. + +**Existing regions:** `region def` does not clear the existing tree — if a name already exists, its parent is updated to the current cursor; otherwise a new region is created. To start from scratch, `region remove` the unwanted regions first. + +**Limits:** Repeater serial accepts one line up to **160 characters**. For larger trees, split across multiple `region def` commands; the cursor resets to `*` between commands, so lead the next command with `child|ancestor` to reposition. Each token splits at most once on `|` — `region def a|b|c|d` is not a flat-list shorthand; see the flat-list example below. + +**Example — linear chain** (each token becomes a child of the previous): +``` +region def a b c d e +region save +``` + +**Example — branched tree** (equivalent to `region put a`, `region put b a`, `region put c b`, `region put d c`, `region put e b`, `region put f e`): +``` +region def a b c d|b e f +region save +``` + +**Example — error and partial state:** +``` +region def a b c|nope d +``` +The reply is `Err - unknown jump: nope`. `a`, `b`, and `c` were placed before the failure; `d` was not. Run `region` to inspect, then re-run with a corrected jump or repair with `region remove` / `region put`. + +**Example — flat list** (each region a child of `*`). Use `|*` after each token to pop the cursor back to the root before the next token: +``` +region def a|* b|* c|* d|* e|* f +region save +``` + +--- + #### Remove a region **Usage:** - `region remove ` @@ -1155,7 +851,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Parameters:** - `filter`: `allowed`|`denied` -**Note:** Requires firmware 1.12.+ +**Note:** Requires firmware 1.12+ --- @@ -1312,7 +1008,7 @@ region save --- -#### View or change thevalue of a sensor +#### View or change the value of a sensor **Usage:** - `sensor get ` - `sensor set ` @@ -1432,3 +1128,71 @@ region save **Note:** Returns an error on boards without power management support. --- + +--- + +## Halo Direct Retry Commands + +These commands are available on the Halo direct-message retry branch. See `docs/halo_settings.md` for operating guidance and examples. + +### View or change the retry preset +**Usage:** +- `get retry.preset` +- `set retry.preset ` + +**Parameters:** +- `value`: `infra`|`rooftop`|`mobile` or `0`|`1`|`2` + +--- + +### View or change whether direct retries use the recent repeater SNR gate +**Usage:** +- `get direct.retry.heard` +- `set direct.retry.heard ` + +**Parameters:** +- `state`: `on`|`off` + +--- + +### View or change direct retry timing and count +**Usage:** +- `get direct.retry.margin` +- `set direct.retry.margin ` +- `get direct.retry.count` +- `set direct.retry.count <1-15>` +- `get direct.retry.base` +- `set direct.retry.base <10-5000>` +- `get direct.retry.step` +- `set direct.retry.step <0-5000>` + +--- + +### View or change adaptive coding rate for direct retry packets +**Usage:** +- `get direct.retry.cr` +- `set direct.retry.cr ,,,` +- `set direct.retry.cr off` + +--- + +### Get or set recent repeater prefix/SNR +**Usage:** +- `get recent.repeater` +- `get recent.repeater ` +- `get recent.repeater page ` +- `set recent.repeater ` +- `clear recent.repeater` + +--- + +### View or change direct reply path overrides +**Usage:** +- `get outpath` +- `set outpath ` +- `set outpath direct` +- `set outpath clear` +- `set outpath flood` +- `get altpath` +- `set altpath ` +- `set altpath clear` diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 36c66c3f..bb39c077 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -85,9 +85,6 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { uint32_t last_millis; uint64_t uptime_millis; unsigned long next_local_advert, next_flood_advert; - unsigned long next_battery_alert_check; - unsigned long last_battery_alert_sent; - bool battery_alert_sent; bool _logging; NodePrefs _prefs; ClientACL acl; @@ -102,15 +99,6 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { RegionEntry* recv_pkt_region; TransportKey default_scope; RateLimiter discover_limiter, anon_limiter; - struct FloodRetryBridgeState { - uint8_t key[MAX_HASH_SIZE]; - uint8_t source_bucket; - uint8_t target_mask; - uint8_t heard_mask; - uint8_t progress_marker; - bool active; - }; - mutable FloodRetryBridgeState flood_retry_bridge_states[MAX_FLOOD_RETRY_SLOTS]; uint32_t pending_discover_tag; unsigned long pending_discover_until; bool region_load_active; @@ -140,25 +128,6 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { uint8_t getDirectRetryPreset() const; uint8_t getDirectRetryConfiguredMaxAttempts() const; uint32_t getDirectRetryAttemptStepMillis() const; - bool hasFloodRetryPrefixes() const; - bool floodRetryPrefixMatches(const mesh::Packet* packet) const; - bool floodRetryLastHopMatches(const mesh::Packet* packet) const; - bool floodRetryPrefixIgnored(const uint8_t* prefix, uint8_t prefix_len) const; - uint8_t floodRetryEffectivePathLength(const mesh::Packet* packet, uint8_t max_hops = 0xFF) const; - bool floodRetryPrefixFresh(const uint8_t* prefix, uint8_t prefix_len) const; - int floodRetryBucketForPrefix(const uint8_t* prefix, uint8_t prefix_len, bool require_fresh, - bool include_other) const; - int floodRetryBucketForPathHop(const uint8_t* prefix, uint8_t prefix_len, uint8_t hop, - uint8_t progress_marker) const; - int floodRetrySourceBucket(const mesh::Packet* packet) const; - uint8_t floodRetryBridgeTargetMask(uint8_t source_bucket) const; - uint8_t floodRetryBridgeHeardMask(const mesh::Packet* packet, uint8_t source_bucket, - uint8_t progress_marker) const; - FloodRetryBridgeState* floodRetryBridgeStateFor(const mesh::Packet* packet, bool create) const; - void clearFloodRetryBridgeState(const mesh::Packet* packet); - void refreshFloodRetryHeardRecent(const mesh::Packet* packet); - void formatFloodRetryPath(char* dest, size_t dest_len, const mesh::Packet* packet) const; - bool formatFloodRetryHeard(char* dest, size_t dest_len, const mesh::Packet* packet) const; void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr); uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood); uint8_t handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data); @@ -166,8 +135,6 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { uint8_t handleAnonClockReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data); int handleRequest(ClientInfo* sender, uint32_t sender_timestamp, uint8_t* payload, size_t payload_len); mesh::Packet* createSelfAdvert(); - bool sendRepeatersFloodText(const char* text); - void checkBatteryAlert(); File openAppend(const char* fname); bool isLooped(const mesh::Packet* packet, const uint8_t max_counters[]); @@ -196,12 +163,6 @@ protected: uint8_t getDirectRetryMaxAttempts(const mesh::Packet* packet) const override; uint32_t getDirectRetryAttemptDelay(const mesh::Packet* packet, uint8_t attempt_idx) override; void onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) override; - bool allowFloodRetry(const mesh::Packet* packet) const override; - void onFloodRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) override; - bool hasFloodRetryTargetPrefix(const mesh::Packet* packet) const override; - uint8_t getFloodRetryMaxPathLength(const mesh::Packet* packet) const override; - uint8_t getFloodRetryMaxAttempts(const mesh::Packet* packet) const override; - bool isFloodRetryEchoTarget(const mesh::Packet* packet, uint8_t progress_marker) const override; int getInterferenceThreshold() const override { return _prefs.interference_threshold; diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index b9b1ab4d..03a89dd4 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -159,9 +159,4 @@ void loop() { } #endif } - - if (the_mesh.getNodePrefs()->reboot_interval > 0 && - the_mesh.millisHasNowPassed(the_mesh.getNodePrefs()->reboot_interval * 3600000)) { - board.reboot(); - } } diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 4dccd45a..a9f7aac0 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -5,8 +5,6 @@ namespace mesh { static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT = 15; static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX = 15; -static const uint8_t FLOOD_RETRY_MAX_ATTEMPTS_DEFAULT = 3; -static const uint8_t FLOOD_RETRY_MAX_ATTEMPTS_HARD_MAX = 15; static uint8_t decodeTraceHashSize(uint8_t flags, uint8_t route_bytes) { uint8_t code = flags & 0x03; @@ -44,19 +42,6 @@ void Mesh::begin() { _direct_retries[i].queued = false; _direct_retries[i].active = false; } - for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) { - _flood_retries[i].packet = NULL; - _flood_retries[i].trigger_packet = NULL; - _flood_retries[i].retry_started_at = 0; - _flood_retries[i].retry_at = 0; - _flood_retries[i].retry_delay = 0; - _flood_retries[i].retry_attempts_sent = 0; - _flood_retries[i].priority = 0; - _flood_retries[i].progress_marker = 0; - _flood_retries[i].waiting_final_echo = false; - _flood_retries[i].queued = false; - _flood_retries[i].active = false; - } Dispatcher::begin(); } @@ -93,37 +78,6 @@ void Mesh::loop() { clearDirectRetrySlot(i); } } - - for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) { - if (!_flood_retries[i].active) { - continue; - } - - if (_flood_retries[i].waiting_final_echo) { - if (!millisHasNowPassed(_flood_retries[i].retry_at)) { - continue; - } - - uint32_t elapsed_millis = _flood_retries[i].retry_started_at == 0 - ? 0 - : (uint32_t)(_ms->getMillis() - _flood_retries[i].retry_started_at); - onFloodRetryEvent("failed_all_tries", _flood_retries[i].packet, elapsed_millis, _flood_retries[i].retry_attempts_sent); - onFloodRetryEvent("failure", _flood_retries[i].packet, elapsed_millis, _flood_retries[i].retry_attempts_sent); - clearFloodRetrySlot(i); - continue; - } - - if (!_flood_retries[i].queued || !millisHasNowPassed(_flood_retries[i].retry_at)) { - continue; - } - - if (!isFloodRetryQueued(_flood_retries[i].packet)) { - if (_flood_retries[i].packet == getOutboundInFlight()) { - continue; - } - clearFloodRetrySlot(i); - } - } } bool Mesh::allowPacketForward(const mesh::Packet* packet) { @@ -152,39 +106,16 @@ uint32_t Mesh::getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_ // Keep the historical linear spacing while allowing the base wait to vary by platform/profile. return base + ((uint32_t)attempt_idx * 100UL); } -bool Mesh::allowFloodRetry(const Packet* packet) const { - return true; -} -bool Mesh::hasFloodRetryTargetPrefix(const Packet* packet) const { - return false; -} -uint8_t Mesh::getFloodRetryMaxPathLength(const Packet* packet) const { - return 2; -} -uint8_t Mesh::getFloodRetryMaxAttempts(const Packet* packet) const { - return FLOOD_RETRY_MAX_ATTEMPTS_DEFAULT; -} -uint32_t Mesh::getFloodRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx) { - if (packet == NULL) { - return _radio->getEstAirtimeFor(MAX_TRANS_UNIT); - } - - uint32_t max_packet_airtime = _radio->getEstAirtimeFor(MAX_TRANS_UNIT); - uint32_t packet_airtime = _radio->getEstAirtimeFor(packet->getRawLength()); - return max_packet_airtime + (20UL * packet_airtime); -} uint8_t Mesh::getExtraAckTransmitCount() const { return 0; } void Mesh::onSendComplete(Packet* packet) { armDirectRetryOnSendComplete(packet); - armFloodRetryOnSendComplete(packet); } void Mesh::onSendFail(Packet* packet) { clearPendingDirectRetryOnSendFail(packet); - clearPendingFloodRetryOnSendFail(packet); } uint32_t Mesh::getCADFailRetryDelay() const { @@ -202,8 +133,6 @@ int Mesh::searchChannelsByHash(const uint8_t* hash, GroupChannel channels[], int DispatcherAction Mesh::onRecvPacket(Packet* pkt) { if (pkt->isRouteDirect()) { cancelDirectRetryOnEcho(pkt); - } else if (pkt->isRouteFlood()) { - cancelFloodRetryOnEcho(pkt); } if (pkt->isRouteDirect() && pkt->getPayloadType() == PAYLOAD_TYPE_TRACE) { @@ -523,7 +452,6 @@ DispatcherAction Mesh::routeRecvPacket(Packet* packet) { uint32_t d = getRetransmitDelay(packet); uint8_t priority = packet->getPathHashCount(); - maybeScheduleFloodRetry(packet, priority); // as this propagates outwards, give it lower and lower priority return ACTION_RETRANSMIT_DELAYED(priority, d); // give priority to closer sources, than ones further away } @@ -952,242 +880,6 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { _direct_retries[slot_idx].active = true; } -void Mesh::clearFloodRetrySlot(int idx) { - if (_flood_retries[idx].waiting_final_echo && _flood_retries[idx].packet != NULL) { - releasePacket(_flood_retries[idx].packet); - } - _flood_retries[idx].packet = NULL; - _flood_retries[idx].trigger_packet = NULL; - _flood_retries[idx].retry_started_at = 0; - _flood_retries[idx].retry_at = 0; - _flood_retries[idx].retry_delay = 0; - _flood_retries[idx].retry_attempts_sent = 0; - _flood_retries[idx].priority = 0; - _flood_retries[idx].progress_marker = 0; - _flood_retries[idx].waiting_final_echo = false; - _flood_retries[idx].queued = false; - _flood_retries[idx].active = false; -} - -bool Mesh::isFloodRetryQueued(const Packet* packet) const { - for (int i = 0; i < _mgr->getOutboundTotal(); i++) { - if (_mgr->getOutboundByIdx(i) == packet) { - return true; - } - } - return false; -} - -bool Mesh::isFloodRetryEchoTarget(const Packet* packet, uint8_t progress_marker) const { - return packet->isRouteFlood() && packet->getPathHashCount() > progress_marker; -} - -bool Mesh::cancelFloodRetryOnEcho(const Packet* packet) { - uint8_t recv_key[MAX_HASH_SIZE]; - packet->calculatePacketHash(recv_key); - - bool cleared = false; - for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) { - if (!_flood_retries[i].active || memcmp(recv_key, _flood_retries[i].retry_key, MAX_HASH_SIZE) != 0) { - continue; - } - if (!isFloodRetryEchoTarget(packet, _flood_retries[i].progress_marker)) { - continue; - } - - uint32_t echo_millis = _flood_retries[i].retry_started_at == 0 - ? 0 - : (uint32_t)(_ms->getMillis() - _flood_retries[i].retry_started_at); - uint8_t retry_attempt = _flood_retries[i].waiting_final_echo - ? _flood_retries[i].retry_attempts_sent - : _flood_retries[i].retry_attempts_sent + 1; - onFloodRetryEvent("good", packet, echo_millis, retry_attempt); - - if (_flood_retries[i].queued) { - for (int j = 0; j < _mgr->getOutboundTotal(); j++) { - if (_mgr->getOutboundByIdx(j) == _flood_retries[i].packet) { - Packet* pending = _mgr->removeOutboundByIdx(j); - if (pending) { - releasePacket(pending); - } - break; - } - } - } - clearFloodRetrySlot(i); - cleared = true; - } - - return cleared; -} - -void Mesh::armFloodRetryOnSendComplete(const Packet* packet) { - for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) { - if (!_flood_retries[i].active) { - continue; - } - - if (_flood_retries[i].queued) { - if (_flood_retries[i].packet != packet) { - continue; - } - - uint32_t elapsed_millis = _flood_retries[i].retry_started_at == 0 - ? 0 - : (uint32_t)(_ms->getMillis() - _flood_retries[i].retry_started_at); - onFloodRetryEvent("resent", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent + 1); - _flood_retries[i].retry_attempts_sent++; - - uint8_t max_attempts = getFloodRetryMaxAttempts(packet); - if (max_attempts < 1) { - max_attempts = 1; - } else if (max_attempts > FLOOD_RETRY_MAX_ATTEMPTS_HARD_MAX) { - max_attempts = FLOOD_RETRY_MAX_ATTEMPTS_HARD_MAX; - } - if (_flood_retries[i].retry_attempts_sent >= max_attempts) { - Packet* final_wait = obtainNewPacket(); - if (final_wait == NULL) { - onFloodRetryEvent("dropped_no_packet", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent); - onFloodRetryEvent("failure", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent); - clearFloodRetrySlot(i); - continue; - } - - *final_wait = *packet; - _flood_retries[i].packet = final_wait; - _flood_retries[i].retry_at = futureMillis(_flood_retries[i].retry_delay); - _flood_retries[i].waiting_final_echo = true; - _flood_retries[i].queued = false; - continue; - } - - Packet* retry = obtainNewPacket(); - if (retry == NULL) { - onFloodRetryEvent("dropped_no_packet", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent + 1); - onFloodRetryEvent("failure", packet, elapsed_millis, _flood_retries[i].retry_attempts_sent + 1); - clearFloodRetrySlot(i); - continue; - } - - *retry = *packet; - uint32_t retry_delay = getFloodRetryAttemptDelay(packet, _flood_retries[i].retry_attempts_sent); - if (queueOutboundPacket(retry, _flood_retries[i].priority, retry_delay)) { - _flood_retries[i].packet = retry; - _flood_retries[i].retry_delay = retry_delay; - _flood_retries[i].retry_at = futureMillis(retry_delay); - _flood_retries[i].retry_started_at = _ms->getMillis(); - _flood_retries[i].waiting_final_echo = false; - onFloodRetryEvent("queued", retry, retry_delay, _flood_retries[i].retry_attempts_sent + 1); - } else { - onFloodRetryEvent("dropped_queue_full", retry, retry_delay, _flood_retries[i].retry_attempts_sent + 1); - onFloodRetryEvent("failure", retry, elapsed_millis, _flood_retries[i].retry_attempts_sent + 1); - releasePacket(retry); - clearFloodRetrySlot(i); - } - continue; - } - - if (_flood_retries[i].trigger_packet != packet) { - continue; - } - - Packet* retry = obtainNewPacket(); - if (retry == NULL) { - onFloodRetryEvent("dropped_no_packet", packet, _flood_retries[i].retry_delay, 1); - onFloodRetryEvent("failure", packet, 0, 1); - clearFloodRetrySlot(i); - continue; - } - - *retry = *packet; - if (queueOutboundPacket(retry, _flood_retries[i].priority, _flood_retries[i].retry_delay)) { - unsigned long now = _ms->getMillis(); - _flood_retries[i].packet = retry; - _flood_retries[i].trigger_packet = NULL; - _flood_retries[i].queued = true; - _flood_retries[i].waiting_final_echo = false; - _flood_retries[i].retry_at = futureMillis(_flood_retries[i].retry_delay); - _flood_retries[i].retry_started_at = now; - onFloodRetryEvent("queued", retry, _flood_retries[i].retry_delay, 1); - } else { - onFloodRetryEvent("dropped_queue_full", retry, _flood_retries[i].retry_delay, 1); - onFloodRetryEvent("failure", retry, 0, 1); - releasePacket(retry); - clearFloodRetrySlot(i); - } - } -} - -void Mesh::clearPendingFloodRetryOnSendFail(const Packet* packet) { - for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) { - if (!_flood_retries[i].active) { - continue; - } - - if (_flood_retries[i].queued) { - if (_flood_retries[i].packet == packet) { - onFloodRetryEvent("dropped_send_fail", packet, 0, _flood_retries[i].retry_attempts_sent + 1); - onFloodRetryEvent("failure", packet, 0, _flood_retries[i].retry_attempts_sent + 1); - clearFloodRetrySlot(i); - } - continue; - } - - if (_flood_retries[i].trigger_packet == packet) { - onFloodRetryEvent("dropped_send_fail", packet, 0, 1); - onFloodRetryEvent("failure", packet, 0, 1); - clearFloodRetrySlot(i); - } - } -} - -void Mesh::maybeScheduleFloodRetry(const Packet* packet, uint8_t priority) { - if (packet == NULL || !packet->isRouteFlood() || hasFloodRetryTargetPrefix(packet)) { - return; - } - - uint8_t max_path_len = getFloodRetryMaxPathLength(packet); - if (max_path_len != FLOOD_RETRY_PATH_GATE_DISABLED && packet->getPathHashCount() > max_path_len) { - return; - } - - uint8_t max_attempts = getFloodRetryMaxAttempts(packet); - if (max_attempts == 0) { - return; - } - - int slot_idx = -1; - for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) { - if (!_flood_retries[i].active) { - slot_idx = i; - break; - } - } - if (slot_idx < 0) { - onFloodRetryEvent("dropped_no_slot", packet, 0, 0); - onFloodRetryEvent("failure", packet, 0, 0); - return; - } - - if (!allowFloodRetry(packet)) { - return; - } - - uint32_t retry_delay = getFloodRetryAttemptDelay(packet, 0); - packet->calculatePacketHash(_flood_retries[slot_idx].retry_key); - _flood_retries[slot_idx].packet = NULL; - _flood_retries[slot_idx].trigger_packet = const_cast(packet); - _flood_retries[slot_idx].retry_started_at = 0; - _flood_retries[slot_idx].retry_at = 0; - _flood_retries[slot_idx].retry_delay = retry_delay; - _flood_retries[slot_idx].retry_attempts_sent = 0; - _flood_retries[slot_idx].priority = priority; - _flood_retries[slot_idx].progress_marker = packet->getPathHashCount(); - _flood_retries[slot_idx].waiting_final_echo = false; - _flood_retries[slot_idx].queued = false; - _flood_retries[slot_idx].active = true; -} - Packet* Mesh::createAdvert(const LocalIdentity& id, const uint8_t* app_data, size_t app_data_len) { if (app_data_len > MAX_ADVERT_DATA_SIZE) return NULL; diff --git a/src/Mesh.h b/src/Mesh.h index 6ae26642..314db237 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -8,14 +8,6 @@ namespace mesh { #define MAX_DIRECT_RETRY_SLOTS 6 #endif -#ifndef MAX_FLOOD_RETRY_SLOTS - #define MAX_FLOOD_RETRY_SLOTS 6 -#endif - -#ifndef FLOOD_RETRY_PATH_GATE_DISABLED - #define FLOOD_RETRY_PATH_GATE_DISABLED 0xFF -#endif - class GroupChannel { public: uint8_t hash[PATH_HASH_SIZE]; @@ -54,26 +46,7 @@ class Mesh : public Dispatcher { bool active; }; - struct FloodRetryEntry { - Packet* packet; - Packet* trigger_packet; - unsigned long retry_started_at; - unsigned long retry_at; - uint32_t retry_delay; - uint8_t retry_attempts_sent; - uint8_t retry_key[MAX_HASH_SIZE]; - uint8_t priority; - uint8_t progress_marker; - bool waiting_final_echo; - bool queued; - bool active; - }; - - RTCClock* _rtc; - RNG* _rng; - MeshTables* _tables; DirectRetryEntry _direct_retries[MAX_DIRECT_RETRY_SLOTS]; - FloodRetryEntry _flood_retries[MAX_FLOOD_RETRY_SLOTS]; void removePathPrefix(Packet* packet, uint8_t prefix_count); void routeDirectRecvAcks(Packet* packet, uint32_t delay_millis); @@ -87,12 +60,6 @@ class Mesh : public Dispatcher { uint8_t& progress_marker, bool& expect_path_growth) const; bool canDecodeDirectPayloadForSelf(const Packet* packet); void maybeScheduleDirectRetry(const Packet* packet, uint8_t priority); - void clearFloodRetrySlot(int idx); - bool isFloodRetryQueued(const Packet* packet) const; - bool cancelFloodRetryOnEcho(const Packet* packet); - void armFloodRetryOnSendComplete(const Packet* packet); - void clearPendingFloodRetryOnSendFail(const Packet* packet); - void maybeScheduleFloodRetry(const Packet* packet, uint8_t priority); //void routeRecvAcks(Packet* packet, uint32_t delay_millis); DispatcherAction forwardMultipartDirect(Packet* pkt); @@ -156,41 +123,6 @@ protected: */ virtual uint32_t getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx); - /** - * \brief Decide whether a FLOOD packet should retry when no downstream echo is overheard. - */ - virtual bool allowFloodRetry(const Packet* packet) const; - - /** - * \brief Return true when this FLOOD packet already carries an application-defined target prefix. - */ - virtual bool hasFloodRetryTargetPrefix(const Packet* packet) const; - - /** - * \returns maximum flood path hash count eligible for retry, or FLOOD_RETRY_PATH_GATE_DISABLED. - */ - virtual uint8_t getFloodRetryMaxPathLength(const Packet* packet) const; - - /** - * \returns maximum number of FLOOD retry transmissions after the initial TX. - */ - virtual uint8_t getFloodRetryMaxAttempts(const Packet* packet) const; - - /** - * \brief Return true when a received FLOOD echo is enough to cancel a pending retry. - */ - virtual bool isFloodRetryEchoTarget(const Packet* packet, uint8_t progress_marker) const; - - /** - * \returns delay before a specific flood retry attempt, where attempt_idx=0 is the first retry. - */ - virtual uint32_t getFloodRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx); - - /** - * \brief Optional hook for logging flood-retry lifecycle events. - */ - virtual void onFloodRetryEvent(const char* event, const Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) { } - /** * \returns number of extra (Direct) ACK transmissions wanted. */ From 765ec1447badc7b6efdbfeaa19a7449361f08bbb Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 15 Jun 2026 17:24:22 -0700 Subject: [PATCH 128/214] Add direct retry controls --- docs/cli_commands.md | 68 ------- docs/halo_settings.md | 136 -------------- examples/simple_repeater/MyMesh.cpp | 226 +++++++++++++++++++++++ examples/simple_repeater/MyMesh.h | 24 +-- examples/simple_repeater/main.cpp | 6 +- src/Mesh.cpp | 132 +++++-------- src/Mesh.h | 19 +- src/helpers/ClientACL.cpp | 13 +- src/helpers/ClientACL.h | 5 +- src/helpers/CommonCLI.cpp | 277 +++++++++++++++++++++++++++- src/helpers/CommonCLI.h | 51 +++++ src/helpers/SimpleMeshTables.h | 108 +++-------- 12 files changed, 649 insertions(+), 416 deletions(-) delete mode 100644 docs/halo_settings.md diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 5c5ee670..c06f5e12 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -1128,71 +1128,3 @@ region save **Note:** Returns an error on boards without power management support. --- - ---- - -## Halo Direct Retry Commands - -These commands are available on the Halo direct-message retry branch. See `docs/halo_settings.md` for operating guidance and examples. - -### View or change the retry preset -**Usage:** -- `get retry.preset` -- `set retry.preset ` - -**Parameters:** -- `value`: `infra`|`rooftop`|`mobile` or `0`|`1`|`2` - ---- - -### View or change whether direct retries use the recent repeater SNR gate -**Usage:** -- `get direct.retry.heard` -- `set direct.retry.heard ` - -**Parameters:** -- `state`: `on`|`off` - ---- - -### View or change direct retry timing and count -**Usage:** -- `get direct.retry.margin` -- `set direct.retry.margin ` -- `get direct.retry.count` -- `set direct.retry.count <1-15>` -- `get direct.retry.base` -- `set direct.retry.base <10-5000>` -- `get direct.retry.step` -- `set direct.retry.step <0-5000>` - ---- - -### View or change adaptive coding rate for direct retry packets -**Usage:** -- `get direct.retry.cr` -- `set direct.retry.cr ,,,` -- `set direct.retry.cr off` - ---- - -### Get or set recent repeater prefix/SNR -**Usage:** -- `get recent.repeater` -- `get recent.repeater ` -- `get recent.repeater page ` -- `set recent.repeater ` -- `clear recent.repeater` - ---- - -### View or change direct reply path overrides -**Usage:** -- `get outpath` -- `set outpath ` -- `set outpath direct` -- `set outpath clear` -- `set outpath flood` -- `get altpath` -- `set altpath ` -- `set altpath clear` diff --git a/docs/halo_settings.md b/docs/halo_settings.md deleted file mode 100644 index 9324487d..00000000 --- a/docs/halo_settings.md +++ /dev/null @@ -1,136 +0,0 @@ -# Halo Direct Message Retry Settings - -This file covers only CLI settings and helper commands added for Halo direct-message retry behavior. Use `docs/cli_commands.md` for the general MeshCore CLI. - -Halo retry applies to direct-routed packets. A queued resend is canceled when the next-hop echo is heard. - -## Quick Start - -```text -set retry.preset rooftop -set direct.retry.heard on -get retry.preset -get direct.retry.heard -get direct.retry.count -get direct.retry.base -get direct.retry.step -``` - -Use prefixes from the analyzer, neighbors list, or `get recent.repeater` after the repeater has been online for a few hours. - -## Added Halo Settings - -| Setting | What it does | How to use | Example | -| --- | --- | --- | --- | -| `recent.repeater` | Shows, seeds, or clears the recent repeater prefix/SNR table used by direct retry. | `get recent.repeater`, `get recent.repeater `, `set recent.repeater `, `clear recent.repeater` | `set recent.repeater A1B2C3 -8.5` | -| `outpath` | Overrides the primary direct route used for replies to the current remote client. | `get outpath`, `set outpath `, `set outpath direct`, `set outpath clear`, `set outpath flood` | `set outpath A1B2C3,D4E5F6` | -| `altpath` | Optional second direct route used for duplicate response attempts to the current remote client. | `get altpath`, `set altpath `, `set altpath clear` | `set altpath A1B2C3,D4E5F6` | -| `retry.preset` | Applies direct retry defaults. Values: `infra`, `rooftop`, `mobile` or `0`, `1`, `2`. | `get retry.preset`, `set retry.preset ` | `set retry.preset rooftop` | -| `direct.retry.heard` | Uses the recent repeater table as the direct retry eligibility gate. | `get direct.retry.heard`, `set direct.retry.heard on/off` | `set direct.retry.heard on` | -| `direct.retry.margin` | SNR margin in dB above the SF-specific receive floor. | `get direct.retry.margin`, `set direct.retry.margin <0-40>` | `set direct.retry.margin 5` | -| `direct.retry.count` | Maximum direct retry attempts after initial TX. | `get direct.retry.count`, `set direct.retry.count <1-15>` | `set direct.retry.count 15` | -| `direct.retry.base` | Base wait in milliseconds before retry. | `get direct.retry.base`, `set direct.retry.base <10-5000>` | `set direct.retry.base 175` | -| `direct.retry.step` | Milliseconds added per retry attempt. | `get direct.retry.step`, `set direct.retry.step <0-5000>` | `set direct.retry.step 100` | -| `direct.retry.cr` | Adaptive coding-rate thresholds for direct retry packets. Uses `CR4`, `CR5`, `CR7`, or `CR8`; `CR6` is never selected. | `get direct.retry.cr`, `set direct.retry.cr ,,,`, `set direct.retry.cr off` | `set direct.retry.cr 10.0,7.5,2.5,0` | - -## Recent Repeater Table - -Direct retry uses the recent repeater table when `direct.retry.heard` is `on`. - -Show learned rows: - -```text -get recent.repeater -get recent.repeater 2 -get recent.repeater page 3 -``` - -Seed or correct a prefix: - -```text -set recent.repeater A1B2C3 8.5 -``` - -Clear learned and manually seeded rows: - -```text -clear recent.repeater -``` - -Rows are sorted by prefix width, then SNR. A full direct retry failure lowers the matching row by `0.25 dB`. - -Serial CLI pages contain up to `128` rows. Remote LoRa CLI pages contain up to `7` rows. - -## Direct Path Overrides - -`outpath` and `altpath` apply to the current remote client ACL entry. They need remote client context, so they are not useful from the local serial CLI. - -Set paths with comma-separated hop hashes. Each hop must be `2`, `4`, or `6` hex characters, and all hops in one path must use the same width. - -```text -get outpath -set outpath A1B2C3,D4E5F6 -set outpath direct -set outpath clear -set outpath flood - -get altpath -set altpath A1B2C3,D4E5F6 -set altpath clear -``` - -`set outpath direct` sets a zero-hop direct route for a client reachable without repeaters. `set outpath clear` forgets the override and lets normal path discovery fill it again. `set outpath flood` forces replies to use flood packets until the client logs in again. `altpath` sends a duplicate reply over a second direct route; clearing it returns replies to a single route. - -## Direct Retry Details - -The default adaptive coding-rate profile is `10.0,7.5,2.5,2.5`. SNR `10.0 dB` and up uses `CR4`, `7.5 dB` and up uses `CR5`, `2.5 dB` and down uses `CR8`, and the middle band uses `CR7`. If no recent repeater table entry is available, retry packets use `CR5`. - -Use `set direct.retry.cr off` to disable adaptive coding-rate overrides. If adaptive selection chooses `CR4`, retries after the third attempt use `CR5`. - -Preset details: - -| Preset | Base | Count | Step | SNR gate | -| --- | ---: | ---: | ---: | --- | -| `infra` | `275 ms` | `4` | `150 ms` | SF floor + `15 dB` | -| `rooftop` | `175 ms` | `15` | `100 ms` | SF floor + `5 dB` | -| `mobile` | `175 ms` | `15` | `50 ms` | SF floor | - -Example for a quiet fixed repeater: - -```text -set retry.preset rooftop -set direct.retry.heard on -set direct.retry.margin 5 -``` - -Example for a moving or weak-link node: - -```text -set retry.preset mobile -set direct.retry.margin 0 -``` - -## Troubleshooting - -If direct retries are too aggressive: - -```text -set direct.retry.count 4 -set direct.retry.margin 10 -``` - -If direct retries are too sparse: - -```text -set direct.retry.count 15 -set direct.retry.margin 0 -``` - -If direct retry is skipping a path you expect it to retry: - -```text -get direct.retry.heard -get recent.repeater -``` - -Either disable the heard gate with `set direct.retry.heard off`, or seed the next-hop prefix with `set recent.repeater `. diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 5cc3a9a1..45f81ed2 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -549,6 +549,208 @@ uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) { return getRNG()->nextInt(0, 5*t + 1); } +bool MyMesh::extractDirectRetryPrefix(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const { + if (packet == NULL || !packet->isRouteDirect() || packet->getPathHashCount() == 0) { + return false; + } + prefix_len = packet->getPathHashSize(); + memcpy(prefix, packet->path, prefix_len); + return true; +} + +int8_t MyMesh::getDirectRetryMinSNRX4() const { + switch (active_sf) { + case 7: return -30; + case 8: return -40; + case 9: return -50; + case 10: return -60; + case 11: return -70; + case 12: return -80; + default: return -60; + } +} + +uint8_t MyMesh::getDirectRetryCodingRateForSNR(int8_t snr_x4) const { + if (!_prefs.direct_retry_cr_enabled) return 0; + if (snr_x4 >= _prefs.direct_retry_cr4_snr_x4) return 4; + if (snr_x4 >= _prefs.direct_retry_cr5_snr_x4) return 5; + if (snr_x4 >= _prefs.direct_retry_cr7_snr_x4) return 7; + return 8; +} + +uint8_t MyMesh::getDirectRetryPreset() const { + return _prefs.retry_preset; +} + +uint8_t MyMesh::getDirectRetryConfiguredMaxAttempts() const { + return constrain(_prefs.direct_retry_attempts, 1, 15); +} + +uint32_t MyMesh::getDirectRetryAttemptStepMillis() const { + return _prefs.direct_retry_step_ms; +} + +bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const { + (void)packet; + if (next_hop_hash == NULL || next_hop_hash_len == 0) { + return true; + } + const SimpleMeshTables* tables = static_cast(getTables()); + const SimpleMeshTables::RecentRepeaterInfo* repeater = tables != NULL + ? tables->findRecentRepeaterByHash(next_hop_hash, next_hop_hash_len) + : NULL; + + if (repeater == NULL) { + // Retry unknown repeaters too. If they fail, onDirectRetryFailed() seeds the + // recent-repeater table below the +2.00 dB starting point. + return true; + } + int16_t retry_floor_x4 = (int16_t)getDirectRetryMinSNRX4() + (int16_t)_prefs.direct_retry_snr_margin_x4; + return (int16_t)repeater->snr_x4 >= retry_floor_x4; +} + +void MyMesh::configureDirectRetryPacket(mesh::Packet* retry, const mesh::Packet* original, uint8_t retry_attempt) { + (void)retry_attempt; + int8_t snr_x4 = 8; // unknown repeaters start at +2.00 dB + const SimpleMeshTables* tables = static_cast(getTables()); + if (tables != NULL) { + uint8_t prefix[MAX_HASH_SIZE]; + uint8_t prefix_len = 0; + if (extractDirectRetryPrefix(original, prefix, prefix_len)) { + const SimpleMeshTables::RecentRepeaterInfo* repeater = tables->findRecentRepeaterByHash(prefix, prefix_len); + if (repeater != NULL) { + snr_x4 = repeater->snr_x4; + } + } + } + + retry->tx_cr = getDirectRetryCodingRateForSNR(snr_x4); +} + +bool MyMesh::maybeShortCircuitDirect(mesh::Packet* packet) { + (void)packet; + return false; +} + +uint32_t MyMesh::getDirectRetryEchoDelay(const mesh::Packet* packet) const { + (void)packet; + return 200; +} + +uint8_t MyMesh::getDirectRetryMaxAttempts(const mesh::Packet* packet) const { + (void)packet; + return getDirectRetryConfiguredMaxAttempts(); +} + +uint32_t MyMesh::getDirectRetryAttemptDelay(const mesh::Packet* packet, uint8_t attempt_idx) { + (void)packet; + return _prefs.direct_retry_base_ms + ((uint32_t)attempt_idx * getDirectRetryAttemptStepMillis()); +} + +void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) { +#if MESH_DEBUG + MESH_DEBUG_PRINTLN("direct retry %s attempt=%u delay=%lu type=%u route=%s", + event ? event : "?", + (uint32_t)retry_attempt, + (unsigned long)delay_millis, + packet ? (uint32_t)packet->getPayloadType() : 0, + packet && packet->isRouteDirect() ? "D" : "F"); +#endif + if (_logging) { + File f = openAppend(PACKET_LOG_FILE); + if (f) { + f.print(getLogDateTime()); + f.printf(": direct retry %s attempt=%u delay=%lu type=%u route=%s\n", + event ? event : "?", + (uint32_t)retry_attempt, + (unsigned long)delay_millis, + packet ? (uint32_t)packet->getPayloadType() : 0, + packet && packet->isRouteDirect() ? "D" : "F"); + f.close(); + } + } +} + +void MyMesh::onDirectRetryFailed(const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) { + if (next_hop_hash == NULL || next_hop_hash_len == 0) { + return; + } + + SimpleMeshTables* tables = static_cast(getTables()); + if (tables != NULL) { + if (!tables->decrementRecentRepeaterSnrX4(next_hop_hash, next_hop_hash_len, 1)) { + tables->setRecentRepeater(next_hop_hash, next_hop_hash_len, 7, false, true); + } + } +} + +void MyMesh::onDirectRetrySucceeded(const uint8_t* next_hop_hash, uint8_t next_hop_hash_len, int8_t snr_x4) { + if (next_hop_hash == NULL || next_hop_hash_len == 0) { + return; + } + + SimpleMeshTables* tables = static_cast(getTables()); + if (tables != NULL) { + tables->setRecentRepeater(next_hop_hash, next_hop_hash_len, snr_x4, false, true); + } +} + +static void formatLocalSnrX4(char* dest, size_t dest_len, int16_t snr_x4) { + int16_t v = snr_x4; + const char* sign = ""; + if (v < 0) { + sign = "-"; + v = -v; + } + snprintf(dest, dest_len, "%s%d.%02d", sign, v / 4, (v % 4) * 25); +} + +void MyMesh::formatRecentRepeatersReply(char *reply, int page) { + const SimpleMeshTables* tables = static_cast(getTables()); + if (tables == NULL) { + strcpy(reply, "Error: unsupported"); + return; + } + int count = tables->getRecentRepeaterCount(); + if (count <= 0) { + strcpy(reply, "> -none-"); + return; + } + + const int page_size = 4; + int pages = (count + page_size - 1) / page_size; + if (page < 1) page = 1; + if (page > pages) page = pages; + + int len = snprintf(reply, 160, "> %d/%d ", page, pages); + int start = (page - 1) * page_size; + for (int i = 0; i < page_size && len < 150; i++) { + const SimpleMeshTables::RecentRepeaterInfo* info = tables->getRecentRepeaterNewestByIdx(start + i); + if (info == NULL) break; + char prefix[MAX_ROUTE_HASH_BYTES * 2 + 1]; + char snr[12]; + mesh::Utils::toHex(prefix, info->prefix, info->prefix_len); + prefix[info->prefix_len * 2] = 0; + formatLocalSnrX4(snr, sizeof(snr), info->snr_x4); + len += snprintf(&reply[len], 160 - len, "%s%s,%s", + i == 0 ? "" : " ", + prefix, + snr); + } +} + +bool MyMesh::setRecentRepeater(const uint8_t* prefix, uint8_t prefix_len, int8_t snr_x4) { + SimpleMeshTables* tables = static_cast(getTables()); + return tables != NULL && tables->setRecentRepeater(prefix, prefix_len, snr_x4, false, true); +} + +void MyMesh::clearRecentRepeaters() { + SimpleMeshTables* tables = static_cast(getTables()); + if (tables != NULL) { + tables->clearRecentRepeaters(); + } +} + bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) { // just try to determine region for packet (apply later in allowPacketForward()) if (pkt->getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD) { @@ -865,6 +1067,9 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc next_local_advert = next_flood_advert = 0; dirty_contacts_expiry = 0; set_radio_at = revert_radio_at = 0; + active_bw = 0.0f; + active_sf = 0; + active_cr = 0; _logging = false; region_load_active = false; @@ -894,6 +1099,18 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_max_advert = 8; _prefs.interference_threshold = 0; // disabled _prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on') + _prefs.retry_preset = RETRY_PRESET_ROOFTOP; + _prefs.direct_retry_attempts = DIRECT_RETRY_ROOFTOP_COUNT; + _prefs.direct_retry_base_ms = DIRECT_RETRY_ROOFTOP_BASE_MS; + _prefs.direct_retry_step_ms = DIRECT_RETRY_ROOFTOP_STEP_MS; + _prefs.direct_retry_snr_margin_x4 = DIRECT_RETRY_ROOFTOP_MARGIN_X4; + _prefs.direct_retry_cr4_snr_x4 = DIRECT_RETRY_CR4_MIN_SNR_X4_DEFAULT; + _prefs.direct_retry_cr5_snr_x4 = DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT; + _prefs.direct_retry_cr7_snr_x4 = DIRECT_RETRY_CR7_MIN_SNR_X4_DEFAULT; + _prefs.direct_retry_cr8_snr_x4 = DIRECT_RETRY_CR8_MAX_SNR_X4_DEFAULT; + _prefs.direct_retry_cr_enabled = 1; + _prefs.direct_retry_prefs_magic[0] = DIRECT_RETRY_PREFS_MAGIC_0; + _prefs.direct_retry_prefs_magic[1] = DIRECT_RETRY_PREFS_MAGIC_1; // bridge defaults _prefs.bridge_enabled = 1; // enabled @@ -962,6 +1179,9 @@ void MyMesh::begin(FILESYSTEM *fs) { #endif radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); + active_bw = _prefs.bw; + active_sf = _prefs.sf; + active_cr = _prefs.cr; radio_driver.setTxPower(_prefs.tx_power_dbm); radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); @@ -1289,12 +1509,18 @@ void MyMesh::loop() { if (set_radio_at && millisHasNowPassed(set_radio_at)) { // apply pending (temporary) radio params set_radio_at = 0; // clear timer radio_driver.setParams(pending_freq, pending_bw, pending_sf, pending_cr); + active_bw = pending_bw; + active_sf = pending_sf; + active_cr = pending_cr; MESH_DEBUG_PRINTLN("Temp radio params"); } if (revert_radio_at && millisHasNowPassed(revert_radio_at)) { // revert radio params to orig revert_radio_at = 0; // clear timer radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); + active_bw = _prefs.bw; + active_sf = _prefs.sf; + active_cr = _prefs.cr; MESH_DEBUG_PRINTLN("Radio params restored"); } diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index bb39c077..055b4b83 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -163,6 +163,8 @@ protected: uint8_t getDirectRetryMaxAttempts(const mesh::Packet* packet) const override; uint32_t getDirectRetryAttemptDelay(const mesh::Packet* packet, uint8_t attempt_idx) override; void onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) override; + void onDirectRetryFailed(const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) override; + void onDirectRetrySucceeded(const uint8_t* next_hop_hash, uint8_t next_hop_hash_len, int8_t snr_x4) override; int getInterferenceThreshold() const override { return _prefs.interference_threshold; @@ -194,20 +196,6 @@ protected: void onControlDataRecv(mesh::Packet* packet) override; void sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, uint8_t path_hash_size); - mesh::Packet* createPacketCopy(const mesh::Packet* packet, const char* caller); - mesh::Packet* createAltPathCopy(const mesh::Packet* packet, - const uint8_t* primary_path, uint8_t primary_path_len, - const uint8_t* alt_path, uint8_t alt_path_len); - void sendFloodReplyWithAltPath(mesh::Packet* packet, - const uint8_t* direct_path, uint8_t direct_path_len, - const uint8_t* alt_path, uint8_t alt_path_len, - unsigned long delay_millis, uint8_t path_hash_size); - void sendDirectWithAltPath(mesh::Packet* packet, - const uint8_t* path, uint8_t path_len, - const uint8_t* alt_path, uint8_t alt_path_len, - uint32_t delay_millis); - void sendFloodScopedWithSelfPath(const TransportKey& scope, mesh::Packet* pkt, - uint32_t delay_millis, uint8_t path_hash_size); public: MyMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables); @@ -248,6 +236,9 @@ public: void formatStatsReply(char *reply) override; void formatRadioStatsReply(char *reply) override; void formatPacketStatsReply(char *reply) override; + void formatRecentRepeatersReply(char *reply, int page) override; + bool setRecentRepeater(const uint8_t* prefix, uint8_t prefix_len, int8_t snr_x4) override; + void clearRecentRepeaters() override; void startRegionsLoad() override; bool saveRegions() override; void onDefaultRegionChanged(const RegionEntry* r) override; @@ -257,10 +248,7 @@ public: void saveIdentity(const mesh::LocalIdentity& new_id) override; void clearStats() override; - void handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char* command, char* reply); - void handleCommand(uint32_t sender_timestamp, char* command, char* reply) { - handleCommand(sender_timestamp, NULL, command, reply); - } + void handleCommand(uint32_t sender_timestamp, char* command, char* reply); void loop(); #if defined(WITH_BRIDGE) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 03a89dd4..2ce056f5 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -53,7 +53,7 @@ void setup() { halt(); } - fast_rng.begin(radio_get_rng_seed()); + fast_rng.begin(radio_driver.getRngSeed()); FILESYSTEM* fs; #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) @@ -99,6 +99,8 @@ void setup() { #if ENABLE_ADVERT_ON_BOOT == 1 the_mesh.sendSelfAdvertisement(16000, false); #endif + + board.onBootComplete(); } void loop() { @@ -120,7 +122,7 @@ void loop() { Serial.print('\n'); command[len - 1] = 0; // replace newline with C string null terminator char reply[160]; - the_mesh.handleCommand(0, NULL, command, reply); // NOTE: there is no sender_timestamp via serial! + the_mesh.handleCommand(0, command, reply); // NOTE: there is no sender_timestamp via serial! if (reply[0]) { Serial.print(" -> "); Serial.println(reply); } diff --git a/src/Mesh.cpp b/src/Mesh.cpp index a9f7aac0..0bac461b 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -35,6 +35,7 @@ void Mesh::begin() { _direct_retries[i].retry_at = 0; _direct_retries[i].retry_delay = 0; _direct_retries[i].retry_attempts_sent = 0; + _direct_retries[i].next_hop_hash_len = 0; _direct_retries[i].priority = 0; _direct_retries[i].progress_marker = 0; _direct_retries[i].expect_path_growth = false; @@ -63,6 +64,7 @@ void Mesh::loop() { : (uint32_t)(_ms->getMillis() - _direct_retries[i].retry_started_at); onDirectRetryEvent("failed_all_tries", _direct_retries[i].packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); onDirectRetryEvent("failure", _direct_retries[i].packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); + onDirectRetryFailed(_direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len); clearDirectRetrySlot(i); continue; } @@ -183,12 +185,7 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { } } - if (canDecodeDirectPayloadForSelf(pkt)) { - // Some path sources include the final node hash, and some packets are - // heard before all planned hops are consumed. Only stop forwarding once - // this node proves it can decrypt the payload. - removePathPrefix(pkt, pkt->getPathHashCount()); - } else if (self_id.isHashMatch(pkt->path, pkt->getPathHashSize()) || maybeShortCircuitDirect(pkt)) { + if (self_id.isHashMatch(pkt->path, pkt->getPathHashSize()) || maybeShortCircuitDirect(pkt)) { if (allowPacketForward(pkt)) { if (pkt->getPayloadType() == PAYLOAD_TYPE_MULTIPART) { return forwardMultipartDirect(pkt); @@ -479,13 +476,10 @@ DispatcherAction Mesh::forwardMultipartDirect(Packet* pkt) { void Mesh::routeDirectRecvAcks(Packet* packet, uint32_t delay_millis) { if (!packet->isMarkedDoNotRetransmit()) { - uint32_t crc; - memcpy(&crc, packet->payload, 4); - uint8_t extra = getExtraAckTransmitCount(); while (extra > 0) { delay_millis += getDirectRetransmitDelay(packet) + 300; - auto a1 = createMultiAck(crc, extra); + auto a1 = createMultiAck(packet->payload, packet->payload_len, extra); if (a1) { a1->path_len = Packet::copyPath(a1->path, packet->path, packet->path_len); a1->header &= ~PH_ROUTE_MASK; @@ -496,7 +490,7 @@ void Mesh::routeDirectRecvAcks(Packet* packet, uint32_t delay_millis) { extra--; } - auto a2 = createAck(crc); + auto a2 = createAck(packet->payload, packet->payload_len); if (a2) { a2->path_len = Packet::copyPath(a2->path, packet->path, packet->path_len); a2->header &= ~PH_ROUTE_MASK; @@ -508,9 +502,6 @@ void Mesh::routeDirectRecvAcks(Packet* packet, uint32_t delay_millis) { } void Mesh::clearDirectRetrySlot(int idx) { - if (_direct_retries[idx].waiting_final_echo && _direct_retries[idx].packet != NULL) { - releasePacket(_direct_retries[idx].packet); - } _direct_retries[idx].packet = NULL; _direct_retries[idx].trigger_packet = NULL; _direct_retries[idx].retry_started_at = 0; @@ -518,6 +509,8 @@ void Mesh::clearDirectRetrySlot(int idx) { _direct_retries[idx].retry_at = 0; _direct_retries[idx].retry_delay = 0; _direct_retries[idx].retry_attempts_sent = 0; + memset(_direct_retries[idx].next_hop_hash, 0, sizeof(_direct_retries[idx].next_hop_hash)); + _direct_retries[idx].next_hop_hash_len = 0; _direct_retries[idx].priority = 0; _direct_retries[idx].progress_marker = 0; _direct_retries[idx].expect_path_growth = false; @@ -558,6 +551,7 @@ bool Mesh::cancelDirectRetryOnEcho(const Packet* packet) { } int8_t echo_snr_x4 = packet->_snr; + onDirectRetrySucceeded(_direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len, echo_snr_x4); if (_direct_retries[i].queued || _direct_retries[i].waiting_final_echo) { if (_direct_retries[i].packet != NULL) { // Success quality comes from the received downstream echo, not the original upstream RX. @@ -620,16 +614,9 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { max_attempts = DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX; } if (_direct_retries[i].retry_attempts_sent >= max_attempts) { - Packet* final_wait = obtainNewPacket(); - if (final_wait == NULL) { - onDirectRetryEvent("dropped_no_packet", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); - onDirectRetryEvent("failure", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); - clearDirectRetrySlot(i); - continue; - } - - *final_wait = *packet; - _direct_retries[i].packet = final_wait; + // Dispatcher releases the retry packet after this hook. Keep only retry metadata + // for the final echo window so pool exhaustion cannot force a premature failure. + _direct_retries[i].packet = NULL; _direct_retries[i].retry_at = futureMillis(_direct_retries[i].retry_delay); _direct_retries[i].waiting_final_echo = true; _direct_retries[i].queued = false; @@ -783,62 +770,6 @@ bool Mesh::getDirectRetryTarget(const Packet* packet, const uint8_t*& next_hop_h } } -bool Mesh::canDecodeDirectPayloadForSelf(const Packet* packet) { - if (packet == NULL || !packet->isRouteDirect() || packet->getPathHashCount() == 0 || packet->payload_len < 1) { - return false; - } - - switch (packet->getPayloadType()) { - case PAYLOAD_TYPE_PATH: - case PAYLOAD_TYPE_REQ: - case PAYLOAD_TYPE_RESPONSE: - case PAYLOAD_TYPE_TXT_MSG: { - if (packet->payload_len < 2) { - return false; - } - - int i = 0; - uint8_t dest_hash = packet->payload[i++]; - uint8_t src_hash = packet->payload[i++]; - if (i + CIPHER_MAC_SIZE >= packet->payload_len || !self_id.isHashMatch(&dest_hash)) { - return false; - } - - int num = searchPeersByHash(&src_hash); - for (int j = 0; j < num; j++) { - uint8_t secret[PUB_KEY_SIZE]; - getPeerSharedSecret(secret, j); - - uint8_t data[MAX_PACKET_PAYLOAD]; - if (Utils::MACThenDecrypt(secret, data, &packet->payload[i], packet->payload_len - i) > 0) { - return true; - } - } - return false; - } - - case PAYLOAD_TYPE_ANON_REQ: { - int i = 0; - uint8_t dest_hash = packet->payload[i++]; - if (i + PUB_KEY_SIZE + CIPHER_MAC_SIZE >= packet->payload_len || !self_id.isHashMatch(&dest_hash)) { - return false; - } - - Identity sender(&packet->payload[i]); - i += PUB_KEY_SIZE; - - uint8_t secret[PUB_KEY_SIZE]; - self_id.calcSharedSecret(secret, sender); - - uint8_t data[MAX_PACKET_PAYLOAD]; - return Utils::MACThenDecrypt(secret, data, &packet->payload[i], packet->payload_len - i) > 0; - } - - default: - return false; - } -} - void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { const uint8_t* next_hop_hash; uint8_t next_hop_hash_len; @@ -849,6 +780,18 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { return; } + uint8_t retry_key[MAX_HASH_SIZE]; + calculateDirectRetryKey(packet, retry_key); + + for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { + if (_direct_retries[i].active + && memcmp(retry_key, _direct_retries[i].retry_key, MAX_HASH_SIZE) == 0 + && _direct_retries[i].progress_marker == progress_marker + && _direct_retries[i].expect_path_growth == expect_path_growth) { + return; + } + } + int slot_idx = -1; for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { if (!_direct_retries[i].active) { @@ -864,7 +807,7 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { // Only store retry metadata here; allocate the retry packet after the initial TX really completes. uint32_t retry_delay = getDirectRetryAttemptDelay(packet, 0); - calculateDirectRetryKey(packet, _direct_retries[slot_idx].retry_key); + memcpy(_direct_retries[slot_idx].retry_key, retry_key, MAX_HASH_SIZE); _direct_retries[slot_idx].packet = NULL; _direct_retries[slot_idx].trigger_packet = const_cast(packet); _direct_retries[slot_idx].retry_started_at = 0; @@ -872,6 +815,9 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { _direct_retries[slot_idx].retry_at = 0; _direct_retries[slot_idx].retry_delay = retry_delay; _direct_retries[slot_idx].retry_attempts_sent = 0; + memset(_direct_retries[slot_idx].next_hop_hash, 0, sizeof(_direct_retries[slot_idx].next_hop_hash)); + memcpy(_direct_retries[slot_idx].next_hop_hash, next_hop_hash, next_hop_hash_len); + _direct_retries[slot_idx].next_hop_hash_len = next_hop_hash_len; _direct_retries[slot_idx].priority = priority; _direct_retries[slot_idx].progress_marker = progress_marker; _direct_retries[slot_idx].expect_path_growth = expect_path_growth; @@ -1036,7 +982,9 @@ Packet* Mesh::createGroupDatagram(uint8_t type, const GroupChannel& channel, con return packet; } -Packet* Mesh::createAck(uint32_t ack_crc) { +Packet* Mesh::createAck(const uint8_t* ack_hash, uint8_t ack_len) { + if (ack_len > sizeof(Packet::payload)) return NULL; + Packet* packet = obtainNewPacket(); if (packet == NULL) { MESH_DEBUG_PRINTLN("%s Mesh::createAck(): error, packet pool empty", getLogDateTime()); @@ -1044,13 +992,19 @@ Packet* Mesh::createAck(uint32_t ack_crc) { } packet->header = (PAYLOAD_TYPE_ACK << PH_TYPE_SHIFT); // ROUTE_TYPE_* set later - memcpy(packet->payload, &ack_crc, 4); - packet->payload_len = 4; + memcpy(packet->payload, ack_hash, ack_len); + packet->payload_len = ack_len; return packet; } -Packet* Mesh::createMultiAck(uint32_t ack_crc, uint8_t remaining) { +Packet* Mesh::createAck(uint32_t ack_crc) { + return createAck((const uint8_t*)&ack_crc, 4); +} + +Packet* Mesh::createMultiAck(const uint8_t* ack_hash, uint8_t ack_len, uint8_t remaining) { + if (ack_len + 1 > sizeof(Packet::payload)) return NULL; + Packet* packet = obtainNewPacket(); if (packet == NULL) { MESH_DEBUG_PRINTLN("%s Mesh::createMultiAck(): error, packet pool empty", getLogDateTime()); @@ -1059,12 +1013,16 @@ Packet* Mesh::createMultiAck(uint32_t ack_crc, uint8_t remaining) { packet->header = (PAYLOAD_TYPE_MULTIPART << PH_TYPE_SHIFT); // ROUTE_TYPE_* set later packet->payload[0] = (remaining << 4) | PAYLOAD_TYPE_ACK; - memcpy(&packet->payload[1], &ack_crc, 4); - packet->payload_len = 5; + memcpy(&packet->payload[1], ack_hash, ack_len); + packet->payload_len = ack_len + 1; return packet; } +Packet* Mesh::createMultiAck(uint32_t ack_crc, uint8_t remaining) { + return createMultiAck((const uint8_t*)&ack_crc, 4, remaining); +} + Packet* Mesh::createRawData(const uint8_t* data, size_t len) { if (len > sizeof(Packet::payload)) return NULL; // invalid arg diff --git a/src/Mesh.h b/src/Mesh.h index 314db237..d1b66f62 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -29,6 +29,10 @@ public: * and provides virtual methods for sub-classes on handling incoming, and also preparing outbound Packets. */ class Mesh : public Dispatcher { + RNG* _rng; + RTCClock* _rtc; + MeshTables* _tables; + struct DirectRetryEntry { Packet* packet; Packet* trigger_packet; @@ -38,6 +42,8 @@ class Mesh : public Dispatcher { uint32_t retry_delay; uint8_t retry_attempts_sent; uint8_t retry_key[MAX_HASH_SIZE]; + uint8_t next_hop_hash[MAX_HASH_SIZE]; + uint8_t next_hop_hash_len; uint8_t priority; uint8_t progress_marker; bool expect_path_growth; @@ -58,7 +64,6 @@ class Mesh : public Dispatcher { void clearPendingDirectRetryOnSendFail(const Packet* packet); bool getDirectRetryTarget(const Packet* packet, const uint8_t*& next_hop_hash, uint8_t& next_hop_hash_len, uint8_t& progress_marker, bool& expect_path_growth) const; - bool canDecodeDirectPayloadForSelf(const Packet* packet); void maybeScheduleDirectRetry(const Packet* packet, uint8_t priority); //void routeRecvAcks(Packet* packet, uint32_t delay_millis); DispatcherAction forwardMultipartDirect(Packet* pkt); @@ -133,6 +138,16 @@ protected: */ virtual void onDirectRetryEvent(const char* event, const Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) { } + /** + * \brief Optional hook for link-quality feedback when all direct-retry attempts fail. + */ + virtual void onDirectRetryFailed(const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) { } + + /** + * \brief Optional hook for link-quality feedback when a direct-retry echo is heard. + */ + virtual void onDirectRetrySucceeded(const uint8_t* next_hop_hash, uint8_t next_hop_hash_len, int8_t snr_x4) { } + /** * \brief Optional hook to set local-only transmit options on a retry packet before it is queued. */ @@ -253,7 +268,9 @@ public: Packet* createDatagram(uint8_t type, const Identity& dest, const uint8_t* secret, const uint8_t* data, size_t len); Packet* createAnonDatagram(uint8_t type, const LocalIdentity& sender, const Identity& dest, const uint8_t* secret, const uint8_t* data, size_t data_len); Packet* createGroupDatagram(uint8_t type, const GroupChannel& channel, const uint8_t* data, size_t data_len); + Packet* createAck(const uint8_t* ack_hash, uint8_t ack_len); Packet* createAck(uint32_t ack_crc); + Packet* createMultiAck(const uint8_t* ack_hash, uint8_t ack_len, uint8_t remaining); Packet* createMultiAck(uint32_t ack_crc, uint8_t remaining); Packet* createPathReturn(const uint8_t* dest_hash, const uint8_t* secret, const uint8_t* path, uint8_t path_len, uint8_t extra_type, const uint8_t*extra, size_t extra_len); Packet* createPathReturn(const Identity& dest, const uint8_t* secret, const uint8_t* path, uint8_t path_len, uint8_t extra_type, const uint8_t*extra, size_t extra_len); diff --git a/src/helpers/ClientACL.cpp b/src/helpers/ClientACL.cpp index 1d880823..12823827 100644 --- a/src/helpers/ClientACL.cpp +++ b/src/helpers/ClientACL.cpp @@ -1,7 +1,5 @@ #include "ClientACL.h" -static const uint8_t CONTACT_RECORD_VERSION_ALT_PATH = 1; - static File openWrite(FILESYSTEM* _fs, const char* filename) { #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) _fs->remove(filename); @@ -30,7 +28,6 @@ void ClientACL::load(FILESYSTEM* fs, const mesh::LocalIdentity& self_id) { uint8_t unused[2]; memset(&c, 0, sizeof(c)); - c.alt_path_len = OUT_PATH_UNKNOWN; bool success = (file.read(pub_key, 32) == 32); success = success && (file.read((uint8_t *) &c.permissions, 1) == 1); @@ -39,10 +36,6 @@ void ClientACL::load(FILESYSTEM* fs, const mesh::LocalIdentity& self_id) { success = success && (file.read((uint8_t *)&c.out_path_len, 1) == 1); success = success && (file.read(c.out_path, 64) == 64); success = success && (file.read(c.shared_secret, PUB_KEY_SIZE) == PUB_KEY_SIZE); // will be recalculated below - if (success && unused[0] >= CONTACT_RECORD_VERSION_ALT_PATH) { - success = success && (file.read((uint8_t *)&c.alt_path_len, 1) == 1); - success = success && (file.read(c.alt_path, 64) == 64); - } if (!success) break; // EOF @@ -64,8 +57,7 @@ void ClientACL::save(FILESYSTEM* fs, bool (*filter)(ClientInfo*)) { File file = openWrite(_fs, "/s_contacts"); if (file) { uint8_t unused[2]; - unused[0] = CONTACT_RECORD_VERSION_ALT_PATH; - unused[1] = 0; + memset(unused, 0, sizeof(unused)); for (int i = 0; i < num_clients; i++) { auto c = &clients[i]; @@ -78,8 +70,6 @@ void ClientACL::save(FILESYSTEM* fs, bool (*filter)(ClientInfo*)) { success = success && (file.write((uint8_t *)&c->out_path_len, 1) == 1); success = success && (file.write(c->out_path, 64) == 64); success = success && (file.write(c->shared_secret, PUB_KEY_SIZE) == PUB_KEY_SIZE); - success = success && (file.write((uint8_t *)&c->alt_path_len, 1) == 1); - success = success && (file.write(c->alt_path, 64) == 64); if (!success) break; // write failed } @@ -125,7 +115,6 @@ ClientInfo* ClientACL::putClient(const mesh::Identity& id, uint8_t init_perms) { c->permissions = init_perms; c->id = id; c->out_path_len = OUT_PATH_UNKNOWN; - c->alt_path_len = OUT_PATH_UNKNOWN; return c; } diff --git a/src/helpers/ClientACL.h b/src/helpers/ClientACL.h index 356574de..b758f706 100644 --- a/src/helpers/ClientACL.h +++ b/src/helpers/ClientACL.h @@ -10,16 +10,13 @@ #define PERM_ACL_READ_WRITE 2 #define PERM_ACL_ADMIN 3 -#define OUT_PATH_FORCE_FLOOD 0xFE -#define OUT_PATH_UNKNOWN 0xFF +#define OUT_PATH_UNKNOWN 0xFF struct ClientInfo { mesh::Identity id; uint8_t permissions; uint8_t out_path_len; uint8_t out_path[MAX_PATH_SIZE]; - uint8_t alt_path_len; - uint8_t alt_path[MAX_PATH_SIZE]; uint8_t shared_secret[PUB_KEY_SIZE]; uint32_t last_timestamp; // by THEIR clock (transient) uint32_t last_activity; // by OUR clock (transient) diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 82e53743..687b7f7a 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -27,6 +27,113 @@ static bool isValidName(const char *n) { return true; } +static bool looksNumeric(const char* s) { + if (s == NULL) return false; + while (*s == ' ') s++; + if (*s == '-' || *s == '+') s++; + bool saw_digit = false; + while (*s) { + if (*s >= '0' && *s <= '9') { + saw_digit = true; + } else if (*s != '.') { + break; + } + s++; + } + return saw_digit; +} + +static int16_t parseSnrDbX4(const char* s) { + float db = atof(s); + return (int16_t)(db * 4.0f + (db >= 0.0f ? 0.5f : -0.5f)); +} + +static void formatSnrDbX4(char* dest, size_t dest_len, int16_t snr_x4) { + int16_t v = snr_x4; + const char* sign = ""; + if (v < 0) { + sign = "-"; + v = -v; + } + snprintf(dest, dest_len, "%s%d.%02d", sign, v / 4, (v % 4) * 25); +} + +static const char* retryPresetName(uint8_t preset) { + switch (preset) { + case RETRY_PRESET_INFRA: return "infra"; + case RETRY_PRESET_ROOFTOP: return "rooftop"; + case RETRY_PRESET_MOBILE: return "mobile"; + default: return "custom"; + } +} + +static void markDirectRetryPrefsValid(NodePrefs* prefs) { + prefs->direct_retry_prefs_magic[0] = DIRECT_RETRY_PREFS_MAGIC_0; + prefs->direct_retry_prefs_magic[1] = DIRECT_RETRY_PREFS_MAGIC_1; +} + +static void applyDirectRetryPreset(NodePrefs* prefs, uint8_t preset) { + prefs->retry_preset = preset; + if (preset == RETRY_PRESET_INFRA) { + prefs->direct_retry_attempts = DIRECT_RETRY_INFRA_COUNT; + prefs->direct_retry_base_ms = DIRECT_RETRY_INFRA_BASE_MS; + prefs->direct_retry_step_ms = DIRECT_RETRY_INFRA_STEP_MS; + prefs->direct_retry_snr_margin_x4 = DIRECT_RETRY_INFRA_MARGIN_X4; + } else if (preset == RETRY_PRESET_MOBILE) { + prefs->direct_retry_attempts = DIRECT_RETRY_MOBILE_COUNT; + prefs->direct_retry_base_ms = DIRECT_RETRY_MOBILE_BASE_MS; + prefs->direct_retry_step_ms = DIRECT_RETRY_MOBILE_STEP_MS; + prefs->direct_retry_snr_margin_x4 = DIRECT_RETRY_MOBILE_MARGIN_X4; + } else { + prefs->retry_preset = RETRY_PRESET_ROOFTOP; + prefs->direct_retry_attempts = DIRECT_RETRY_ROOFTOP_COUNT; + prefs->direct_retry_base_ms = DIRECT_RETRY_ROOFTOP_BASE_MS; + prefs->direct_retry_step_ms = DIRECT_RETRY_ROOFTOP_STEP_MS; + prefs->direct_retry_snr_margin_x4 = DIRECT_RETRY_ROOFTOP_MARGIN_X4; + } + markDirectRetryPrefsValid(prefs); +} + +static void setDefaultDirectRetryPrefs(NodePrefs* prefs) { + applyDirectRetryPreset(prefs, RETRY_PRESET_ROOFTOP); + prefs->direct_retry_cr_enabled = 1; + prefs->direct_retry_cr4_snr_x4 = DIRECT_RETRY_CR4_MIN_SNR_X4_DEFAULT; + prefs->direct_retry_cr5_snr_x4 = DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT; + prefs->direct_retry_cr7_snr_x4 = DIRECT_RETRY_CR7_MIN_SNR_X4_DEFAULT; + prefs->direct_retry_cr8_snr_x4 = DIRECT_RETRY_CR8_MAX_SNR_X4_DEFAULT; + markDirectRetryPrefsValid(prefs); +} + +static bool directRetryPrefsValid(const NodePrefs* prefs) { + return prefs->direct_retry_prefs_magic[0] == DIRECT_RETRY_PREFS_MAGIC_0 + && prefs->direct_retry_prefs_magic[1] == DIRECT_RETRY_PREFS_MAGIC_1; +} + +static bool parseRetryPreset(const char* s, uint8_t& preset) { + if (strcmp(s, "infra") == 0 || strcmp(s, "0") == 0) { + preset = RETRY_PRESET_INFRA; + return true; + } + if (strcmp(s, "rooftop") == 0 || strcmp(s, "1") == 0) { + preset = RETRY_PRESET_ROOFTOP; + return true; + } + if (strcmp(s, "mobile") == 0 || strcmp(s, "2") == 0) { + preset = RETRY_PRESET_MOBILE; + return true; + } + return false; +} + +static bool parseHashPrefix(const char* text, uint8_t* prefix, uint8_t& prefix_len) { + size_t hex_len = strlen(text); + if (hex_len == 0 || (hex_len & 1) || hex_len > MAX_HASH_SIZE * 2) { + return false; + } + prefix_len = hex_len / 2; + return mesh::Utils::fromHex(prefix, prefix_len, text); +} + void CommonCLI::loadPrefs(FILESYSTEM* fs) { if (fs->exists("/com_prefs")) { loadPrefsInt(fs, "/com_prefs"); // new filename @@ -93,7 +200,18 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292 file.read((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 293 file.read((uint8_t *)&_prefs->cad_enabled, sizeof(_prefs->cad_enabled)); // 294 - // next: 295 + file.read((uint8_t *)&_prefs->retry_preset, sizeof(_prefs->retry_preset)); // 295 + file.read((uint8_t *)&_prefs->direct_retry_attempts, sizeof(_prefs->direct_retry_attempts)); // 296 + file.read((uint8_t *)&_prefs->direct_retry_base_ms, sizeof(_prefs->direct_retry_base_ms)); // 297 + file.read((uint8_t *)&_prefs->direct_retry_step_ms, sizeof(_prefs->direct_retry_step_ms)); // 299 + file.read((uint8_t *)&_prefs->direct_retry_snr_margin_x4, sizeof(_prefs->direct_retry_snr_margin_x4)); // 301 + file.read((uint8_t *)&_prefs->direct_retry_cr4_snr_x4, sizeof(_prefs->direct_retry_cr4_snr_x4)); // 303 + file.read((uint8_t *)&_prefs->direct_retry_cr5_snr_x4, sizeof(_prefs->direct_retry_cr5_snr_x4)); // 304 + file.read((uint8_t *)&_prefs->direct_retry_cr7_snr_x4, sizeof(_prefs->direct_retry_cr7_snr_x4)); // 305 + file.read((uint8_t *)&_prefs->direct_retry_cr8_snr_x4, sizeof(_prefs->direct_retry_cr8_snr_x4)); // 306 + file.read((uint8_t *)&_prefs->direct_retry_cr_enabled, sizeof(_prefs->direct_retry_cr_enabled)); // 307 + file.read((uint8_t *)&_prefs->direct_retry_prefs_magic, sizeof(_prefs->direct_retry_prefs_magic)); // 308 + // next: 310 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -125,6 +243,17 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean _prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean _prefs->cad_enabled = constrain(_prefs->cad_enabled, 0, 1); // boolean + if (!directRetryPrefsValid(_prefs)) { + setDefaultDirectRetryPrefs(_prefs); + } + if (_prefs->retry_preset > RETRY_PRESET_MOBILE && _prefs->retry_preset != RETRY_PRESET_CUSTOM) { + _prefs->retry_preset = RETRY_PRESET_CUSTOM; + } + _prefs->direct_retry_attempts = constrain(_prefs->direct_retry_attempts, 1, 15); + _prefs->direct_retry_base_ms = constrain(_prefs->direct_retry_base_ms, 10, 5000); + _prefs->direct_retry_step_ms = constrain(_prefs->direct_retry_step_ms, 0, 5000); + _prefs->direct_retry_snr_margin_x4 = constrain(_prefs->direct_retry_snr_margin_x4, 0, 160); + _prefs->direct_retry_cr_enabled = constrain(_prefs->direct_retry_cr_enabled, 0, 1); file.close(); } @@ -190,7 +319,19 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292 file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 293 file.write((uint8_t *)&_prefs->cad_enabled, sizeof(_prefs->cad_enabled)); // 294 - // next: 295 + markDirectRetryPrefsValid(_prefs); + file.write((uint8_t *)&_prefs->retry_preset, sizeof(_prefs->retry_preset)); // 295 + file.write((uint8_t *)&_prefs->direct_retry_attempts, sizeof(_prefs->direct_retry_attempts)); // 296 + file.write((uint8_t *)&_prefs->direct_retry_base_ms, sizeof(_prefs->direct_retry_base_ms)); // 297 + file.write((uint8_t *)&_prefs->direct_retry_step_ms, sizeof(_prefs->direct_retry_step_ms)); // 299 + file.write((uint8_t *)&_prefs->direct_retry_snr_margin_x4, sizeof(_prefs->direct_retry_snr_margin_x4)); // 301 + file.write((uint8_t *)&_prefs->direct_retry_cr4_snr_x4, sizeof(_prefs->direct_retry_cr4_snr_x4)); // 303 + file.write((uint8_t *)&_prefs->direct_retry_cr5_snr_x4, sizeof(_prefs->direct_retry_cr5_snr_x4)); // 304 + file.write((uint8_t *)&_prefs->direct_retry_cr7_snr_x4, sizeof(_prefs->direct_retry_cr7_snr_x4)); // 305 + file.write((uint8_t *)&_prefs->direct_retry_cr8_snr_x4, sizeof(_prefs->direct_retry_cr8_snr_x4)); // 306 + file.write((uint8_t *)&_prefs->direct_retry_cr_enabled, sizeof(_prefs->direct_retry_cr_enabled)); // 307 + file.write((uint8_t *)&_prefs->direct_retry_prefs_magic, sizeof(_prefs->direct_retry_prefs_magic)); // 308 + // next: 310 file.close(); } @@ -301,6 +442,9 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else if (memcmp(command, "clear stats", 11) == 0) { _callbacks->clearStats(); strcpy(reply, "(OK - stats reset)"); + } else if (memcmp(command, "clear recent.repeater", 21) == 0 && (command[21] == 0 || command[21] == ' ')) { + _callbacks->clearRecentRepeaters(); + strcpy(reply, "OK"); } else if (memcmp(command, "get ", 4) == 0) { handleGetCmd(sender_timestamp, command, reply); } else if (memcmp(command, "set ", 4) == 0) { @@ -680,6 +824,104 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { strcpy(reply, "Error, must be 0-2"); } + } else if (memcmp(config, "retry.preset ", 13) == 0) { + uint8_t preset; + if (parseRetryPreset(&config[13], preset)) { + applyDirectRetryPreset(_prefs, preset); + savePrefs(); + sprintf(reply, "OK - %s", retryPresetName(_prefs->retry_preset)); + } else { + strcpy(reply, "Error, must be infra, rooftop, or mobile"); + } + } else if (memcmp(config, "direct.retry.margin ", 20) == 0) { + if (!looksNumeric(&config[20])) { + strcpy(reply, "Error, must be 0-40 dB"); + } else { + int16_t margin_x4 = parseSnrDbX4(&config[20]); + if (margin_x4 >= 0 && margin_x4 <= 160) { + _prefs->direct_retry_snr_margin_x4 = (uint16_t)margin_x4; + _prefs->retry_preset = RETRY_PRESET_CUSTOM; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be 0-40 dB"); + } + } + } else if (memcmp(config, "direct.retry.count ", 19) == 0) { + int attempts = _atoi(&config[19]); + if (attempts >= 1 && attempts <= 15) { + _prefs->direct_retry_attempts = (uint8_t)attempts; + _prefs->retry_preset = RETRY_PRESET_CUSTOM; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be 1-15"); + } + } else if (memcmp(config, "direct.retry.base ", 18) == 0) { + int base_ms = _atoi(&config[18]); + if (base_ms >= 10 && base_ms <= 5000) { + _prefs->direct_retry_base_ms = (uint16_t)base_ms; + _prefs->retry_preset = RETRY_PRESET_CUSTOM; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be 10-5000 ms"); + } + } else if (memcmp(config, "direct.retry.step ", 18) == 0) { + int step_ms = _atoi(&config[18]); + if (step_ms >= 0 && step_ms <= 5000) { + _prefs->direct_retry_step_ms = (uint16_t)step_ms; + _prefs->retry_preset = RETRY_PRESET_CUSTOM; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be 0-5000 ms"); + } + } else if (memcmp(config, "direct.retry.cr ", 16) == 0) { + if (memcmp(&config[16], "off", 3) == 0) { + _prefs->direct_retry_cr_enabled = 0; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(tmp, &config[16]); + const char *parts[4]; + int num = mesh::Utils::parseTextParts(tmp, parts, 4, ','); + if (num == 4 && looksNumeric(parts[0]) && looksNumeric(parts[1]) && looksNumeric(parts[2]) && looksNumeric(parts[3])) { + int16_t cr4 = parseSnrDbX4(parts[0]); + int16_t cr5 = parseSnrDbX4(parts[1]); + int16_t cr7 = parseSnrDbX4(parts[2]); + int16_t cr8 = parseSnrDbX4(parts[3]); + if (cr4 >= -128 && cr4 <= 127 && cr5 >= -128 && cr5 <= 127 && cr7 >= -128 && cr7 <= 127 && cr8 >= -128 && cr8 <= 127) { + _prefs->direct_retry_cr4_snr_x4 = (int8_t)cr4; + _prefs->direct_retry_cr5_snr_x4 = (int8_t)cr5; + _prefs->direct_retry_cr7_snr_x4 = (int8_t)cr7; + _prefs->direct_retry_cr8_snr_x4 = (int8_t)cr8; + _prefs->direct_retry_cr_enabled = 1; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, SNR must fit -32.00..31.75 dB"); + } + } else { + strcpy(reply, "Error, use CR4,CR5,CR7,CR8 SNRs or off"); + } + } + } else if (memcmp(config, "recent.repeater ", 16) == 0) { + strcpy(tmp, &config[16]); + const char *parts[2]; + int num = mesh::Utils::parseTextParts(tmp, parts, 2, ' '); + uint8_t prefix[MAX_HASH_SIZE]; + uint8_t prefix_len = 0; + int16_t snr_x4 = num > 1 && looksNumeric(parts[1]) ? parseSnrDbX4(parts[1]) : 0; + if (num != 2 || !parseHashPrefix(parts[0], prefix, prefix_len)) { + strcpy(reply, "Error, use: set recent.repeater "); + } else if (snr_x4 < -128 || snr_x4 > 127) { + strcpy(reply, "Error, SNR must fit -32.00..31.75 dB"); + } else if (_callbacks->setRecentRepeater(prefix, prefix_len, (int8_t)snr_x4)) { + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, table rejected prefix"); + } } else if (memcmp(config, "owner.info ", 11) == 0) { config += 11; char *dp = _prefs->owner_info; @@ -862,6 +1104,37 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "> %d", (uint32_t)_prefs->flood_max); } else if (memcmp(config, "direct.txdelay", 14) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor)); + } else if (memcmp(config, "retry.preset", 12) == 0) { + sprintf(reply, "> %s", retryPresetName(_prefs->retry_preset)); + } else if (memcmp(config, "direct.retry.margin", 19) == 0) { + char margin[12]; + formatSnrDbX4(margin, sizeof(margin), _prefs->direct_retry_snr_margin_x4); + sprintf(reply, "> %s", margin); + } else if (memcmp(config, "direct.retry.count", 18) == 0) { + sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_attempts); + } else if (memcmp(config, "direct.retry.base", 17) == 0) { + sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_base_ms); + } else if (memcmp(config, "direct.retry.step", 17) == 0) { + sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_step_ms); + } else if (memcmp(config, "direct.retry.cr", 15) == 0) { + if (!_prefs->direct_retry_cr_enabled) { + strcpy(reply, "> off"); + } else { + char cr4[12], cr5[12], cr7[12], cr8[12]; + formatSnrDbX4(cr4, sizeof(cr4), _prefs->direct_retry_cr4_snr_x4); + formatSnrDbX4(cr5, sizeof(cr5), _prefs->direct_retry_cr5_snr_x4); + formatSnrDbX4(cr7, sizeof(cr7), _prefs->direct_retry_cr7_snr_x4); + formatSnrDbX4(cr8, sizeof(cr8), _prefs->direct_retry_cr8_snr_x4); + sprintf(reply, "> %s,%s,%s,%s", cr4, cr5, cr7, cr8); + } + } else if (memcmp(config, "recent.repeater", 15) == 0) { + int page = 1; + const char* cursor = &config[15]; + while (*cursor == ' ') cursor++; + if (memcmp(cursor, "page ", 5) == 0) cursor += 5; + if (*cursor) page = _atoi(cursor); + if (page < 1) page = 1; + _callbacks->formatRecentRepeatersReply(reply, page); } else if (memcmp(config, "owner.info", 10) == 0) { auto start = reply; *reply++ = '>'; diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 10cb00c7..7fa71405 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -19,6 +19,34 @@ #define LOOP_DETECT_MODERATE 2 #define LOOP_DETECT_STRICT 3 +#define RETRY_PRESET_INFRA 0 +#define RETRY_PRESET_ROOFTOP 1 +#define RETRY_PRESET_MOBILE 2 +#define RETRY_PRESET_CUSTOM 0xFF + +#define DIRECT_RETRY_INFRA_BASE_MS 275 +#define DIRECT_RETRY_INFRA_COUNT 4 +#define DIRECT_RETRY_INFRA_STEP_MS 150 +#define DIRECT_RETRY_INFRA_MARGIN_X4 60 + +#define DIRECT_RETRY_ROOFTOP_BASE_MS 175 +#define DIRECT_RETRY_ROOFTOP_COUNT 15 +#define DIRECT_RETRY_ROOFTOP_STEP_MS 100 +#define DIRECT_RETRY_ROOFTOP_MARGIN_X4 20 + +#define DIRECT_RETRY_MOBILE_BASE_MS 175 +#define DIRECT_RETRY_MOBILE_COUNT 15 +#define DIRECT_RETRY_MOBILE_STEP_MS 50 +#define DIRECT_RETRY_MOBILE_MARGIN_X4 0 + +#define DIRECT_RETRY_CR4_MIN_SNR_X4_DEFAULT 40 +#define DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT 30 +#define DIRECT_RETRY_CR7_MIN_SNR_X4_DEFAULT 10 +#define DIRECT_RETRY_CR8_MAX_SNR_X4_DEFAULT 10 + +#define DIRECT_RETRY_PREFS_MAGIC_0 0xD1 +#define DIRECT_RETRY_PREFS_MAGIC_1 0x52 + struct NodePrefs { // persisted to file float airtime_factor; char node_name[32]; @@ -65,6 +93,17 @@ struct NodePrefs { // persisted to file uint8_t path_hash_mode; // which path mode to use when sending uint8_t loop_detect; uint8_t cad_enabled; // hardware Channel Activity Detection before TX (boolean) + uint8_t retry_preset; + uint8_t direct_retry_attempts; + uint16_t direct_retry_base_ms; + uint16_t direct_retry_step_ms; + uint16_t direct_retry_snr_margin_x4; + int8_t direct_retry_cr4_snr_x4; + int8_t direct_retry_cr5_snr_x4; + int8_t direct_retry_cr7_snr_x4; + int8_t direct_retry_cr8_snr_x4; + uint8_t direct_retry_cr_enabled; + uint8_t direct_retry_prefs_magic[2]; }; class CommonCLICallbacks { @@ -88,6 +127,18 @@ public: virtual void formatStatsReply(char *reply) = 0; virtual void formatRadioStatsReply(char *reply) = 0; virtual void formatPacketStatsReply(char *reply) = 0; + virtual void formatRecentRepeatersReply(char *reply, int page) { + (void)page; + if (reply != NULL) reply[0] = 0; + } + virtual bool setRecentRepeater(const uint8_t* prefix, uint8_t prefix_len, int8_t snr_x4) { + (void)prefix; + (void)prefix_len; + (void)snr_x4; + return false; + } + virtual void clearRecentRepeaters() { + } virtual mesh::LocalIdentity& getSelfId() = 0; virtual void saveIdentity(const mesh::LocalIdentity& new_id) = 0; virtual void clearStats() = 0; diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index 2d2125dd..f1d52733 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -9,8 +9,7 @@ #include #endif -#define MAX_PACKET_HASHES 128 -#define MAX_PACKET_ACKS 64 +#define MAX_PACKET_HASHES (128+32) #ifndef MAX_RECENT_REPEATERS // Platform defaults. Can be overridden with -D MAX_RECENT_REPEATERS=. #if defined(ESP32) || defined(ESP32_PLATFORM) @@ -39,8 +38,6 @@ public: private: uint8_t _hashes[MAX_PACKET_HASHES*MAX_HASH_SIZE]; int _next_idx; - uint32_t _acks[MAX_PACKET_ACKS]; - int _next_ack_idx; uint32_t _direct_dups, _flood_dups; RecentRepeaterInfo _recent_repeaters[MAX_RECENT_REPEATERS]; int _next_recent_repeater_idx; @@ -48,20 +45,6 @@ private: RecentRepeaterAllowFn _recent_repeater_allow_fn; void* _recent_repeater_allow_ctx; - bool hasSeenAck(uint32_t ack) const { - for (int i = 0; i < MAX_PACKET_ACKS; i++) { - if (ack == _acks[i]) { - return true; - } - } - return false; - } - - void storeAck(uint32_t ack) { - _acks[_next_ack_idx] = ack; - _next_ack_idx = (_next_ack_idx + 1) % MAX_PACKET_ACKS; - } - bool hasSeenHash(const uint8_t* hash) const { const uint8_t* sp = _hashes; for (int i = 0; i < MAX_PACKET_HASHES; i++, sp += MAX_HASH_SIZE) { @@ -148,8 +131,6 @@ public: SimpleMeshTables() { memset(_hashes, 0, sizeof(_hashes)); _next_idx = 0; - memset(_acks, 0, sizeof(_acks)); - _next_ack_idx = 0; _direct_dups = _flood_dups = 0; memset(_recent_repeaters, 0, sizeof(_recent_repeaters)); _next_recent_repeater_idx = 0; @@ -162,8 +143,6 @@ public: void restoreFrom(File f) { f.read(_hashes, sizeof(_hashes)); f.read((uint8_t *) &_next_idx, sizeof(_next_idx)); - f.read((uint8_t *) &_acks[0], sizeof(_acks)); - f.read((uint8_t *) &_next_ack_idx, sizeof(_next_ack_idx)); // Recent repeater entries are intentionally not restored across boots. // This avoids struct-layout migration issues and keeps stale path quality // stats from persisting indefinitely. @@ -173,31 +152,10 @@ public: void saveTo(File f) { f.write(_hashes, sizeof(_hashes)); f.write((const uint8_t *) &_next_idx, sizeof(_next_idx)); - f.write((const uint8_t *) &_acks[0], sizeof(_acks)); - f.write((const uint8_t *) &_next_ack_idx, sizeof(_next_ack_idx)); } #endif bool hasSeen(const mesh::Packet* packet) override { - if (packet->getPayloadType() == PAYLOAD_TYPE_ACK) { - recordRecentRepeater(packet); - - uint32_t ack; - memcpy(&ack, packet->payload, 4); - - if (hasSeenAck(ack)) { - if (packet->isRouteDirect()) { - _direct_dups++; // keep some stats - } else { - _flood_dups++; - } - return true; - } - - storeAck(ack); - return false; - } - uint8_t hash[MAX_HASH_SIZE]; packet->calculatePacketHash(hash); @@ -217,15 +175,6 @@ public: void markSent(const mesh::Packet* packet) override { // Outbound packets must be marked as already-sent without teaching the recent-heard cache about ourselves. - if (packet->getPayloadType() == PAYLOAD_TYPE_ACK) { - uint32_t ack; - memcpy(&ack, packet->payload, 4); - if (!hasSeenAck(ack)) { - storeAck(ack); - } - return; - } - uint8_t hash[MAX_HASH_SIZE]; packet->calculatePacketHash(hash); if (!hasSeenHash(hash)) { @@ -234,25 +183,14 @@ public: } void clear(const mesh::Packet* packet) override { - if (packet->getPayloadType() == PAYLOAD_TYPE_ACK) { - uint32_t ack; - memcpy(&ack, packet->payload, 4); - for (int i = 0; i < MAX_PACKET_ACKS; i++) { - if (ack == _acks[i]) { - _acks[i] = 0; - break; - } - } - } else { - uint8_t hash[MAX_HASH_SIZE]; - packet->calculatePacketHash(hash); + uint8_t hash[MAX_HASH_SIZE]; + packet->calculatePacketHash(hash); - uint8_t* sp = _hashes; - for (int i = 0; i < MAX_PACKET_HASHES; i++, sp += MAX_HASH_SIZE) { - if (memcmp(hash, sp, MAX_HASH_SIZE) == 0) { - memset(sp, 0, MAX_HASH_SIZE); - break; - } + uint8_t* sp = _hashes; + for (int i = 0; i < MAX_PACKET_HASHES; i++, sp += MAX_HASH_SIZE) { + if (memcmp(hash, sp, MAX_HASH_SIZE) == 0) { + memset(sp, 0, MAX_HASH_SIZE); + break; } } } @@ -282,17 +220,13 @@ public: return false; } - // Keep one slot for overlapping prefixes so 1/2/3-byte paths share the same entry. + // Keep exact prefixes distinct so a 1-byte path prefix does not collapse + // independent 2/3-byte repeaters that share the same first byte. for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { RecentRepeaterInfo& existing = _recent_repeaters[i]; - if (existing.prefix_len == 0 || !prefixesOverlap(existing.prefix, existing.prefix_len, prefix, prefix_len)) { + if (existing.prefix_len != prefix_len || memcmp(existing.prefix, prefix, prefix_len) != 0) { continue; } - if (prefix_len > existing.prefix_len) { - memset(existing.prefix, 0, sizeof(existing.prefix)); - memcpy(existing.prefix, prefix, prefix_len); - existing.prefix_len = prefix_len; - } if (snr_locked) { existing.snr_x4 = snr_x4; existing.snr_locked = 1; @@ -352,14 +286,9 @@ public: for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { RecentRepeaterInfo& existing = _recent_repeaters[i]; - if (existing.prefix_len == 0 || !prefixesOverlap(existing.prefix, existing.prefix_len, prefix, prefix_len)) { + if (existing.prefix_len != prefix_len || memcmp(existing.prefix, prefix, prefix_len) != 0) { continue; } - if (prefix_len > existing.prefix_len) { - memset(existing.prefix, 0, sizeof(existing.prefix)); - memcpy(existing.prefix, prefix, prefix_len); - existing.prefix_len = prefix_len; - } if (!existing.snr_locked) { int16_t lowered = (int16_t)existing.snr_x4 - (int16_t)amount_x4; if (lowered < -128) { @@ -432,18 +361,25 @@ public: return NULL; } - // Search newest-to-oldest and allow 1/2/3-byte prefixes to overlap-match. + // Prefer exact matches. If none exists, fall back to the newest longest + // overlapping prefix so coarse learned prefixes can still inform CR. + const RecentRepeaterInfo* best = NULL; for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { int idx = (_next_recent_repeater_idx - 1 - i + MAX_RECENT_REPEATERS) % MAX_RECENT_REPEATERS; const RecentRepeaterInfo* info = &_recent_repeaters[idx]; if (info->prefix_len == 0) { continue; } - if (prefixesOverlap(info->prefix, info->prefix_len, hash, hash_len)) { + if (info->prefix_len == hash_len && memcmp(info->prefix, hash, hash_len) == 0) { return info; } + if (prefixesOverlap(info->prefix, info->prefix_len, hash, hash_len)) { + if (best == NULL || info->prefix_len > best->prefix_len) { + best = info; + } + } } - return NULL; + return best; } void clearRecentRepeaters() { memset(_recent_repeaters, 0, sizeof(_recent_repeaters)); From cb5e2e7c3d49ef921a63ce446918958ed46ff12d Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 15 Jun 2026 17:42:53 -0700 Subject: [PATCH 129/214] Document direct retry CLI --- docs/cli_commands.md | 224 ++++++++++++++++++++++++++++ examples/simple_repeater/MyMesh.cpp | 7 +- src/helpers/CommonCLI.cpp | 30 +++- src/helpers/CommonCLI.h | 1 + 4 files changed, 255 insertions(+), 7 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index c06f5e12..2d31a18b 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -950,6 +950,230 @@ region save - Enables flooding for all child regions automatically - Useful for global networks with specific regional rules +--- +### Direct Retry + +Direct retry resends direct-routed packets when the downstream echo is not heard. It applies to direct messages and TRACE packets. It does not change ACK handling. + +#### View or change direct retry state +**Usage:** +- `get direct.retry` +- `set direct.retry ` + +**Parameters:** +- `state`: `on`|`off` + +**Default:** `on` + +**Examples:** +``` +get direct.retry +set direct.retry on +set direct.retry off +``` + +--- + +#### View or apply a direct retry preset +**Usage:** +- `get retry.preset` +- `set retry.preset ` + +**Parameters:** +- `preset`: `infra`|`rooftop`|`mobile` + +**Notes:** +- `infra`: fewer, slower retries for stable fixed infrastructure. +- `rooftop`: default long retry window for weak rooftop links. +- `mobile`: long retry count with shorter spacing for moving or changing links. +- Changing `direct.retry.count`, `direct.retry.base`, `direct.retry.step`, or `direct.retry.margin` makes the preset report as `custom`. + +**Examples:** +``` +get retry.preset +set retry.preset infra +set retry.preset rooftop +set retry.preset mobile +``` + +--- + +#### View or change direct retry count +**Usage:** +- `get direct.retry.count` +- `set direct.retry.count ` + +**Parameters:** +- `count`: Maximum retry attempts after the original send, from `1` to `15`. + +**Default:** `15` with the `rooftop` preset + +**Examples:** +``` +get direct.retry.count +set direct.retry.count 1 +set direct.retry.count 4 +set direct.retry.count 15 +``` + +--- + +#### View or change direct retry base delay +**Usage:** +- `get direct.retry.base` +- `set direct.retry.base ` + +**Parameters:** +- `ms`: First retry wait in milliseconds, from `10` to `5000`. + +**Default:** `175` with the `rooftop` preset + +**Explanation:** +- The first retry waits `base` milliseconds after the failed echo window. +- Larger values reduce channel pressure and give slow repeaters more time. +- Smaller values recover faster but create tighter retry bursts. + +**Examples:** +``` +get direct.retry.base +set direct.retry.base 175 +set direct.retry.base 275 +set direct.retry.base 500 +``` + +--- + +#### View or change direct retry step delay +**Usage:** +- `get direct.retry.step` +- `set direct.retry.step ` + +**Parameters:** +- `ms`: Extra milliseconds added for each subsequent retry, from `0` to `5000`. + +**Default:** `100` with the `rooftop` preset + +**Explanation:** +- Retry delay is `base + attempt_index * step`. +- With `base=175` and `step=100`, retries wait about `175`, `275`, `375`, `475` ms, and so on. +- `step=0` keeps every retry at the same delay. +- Larger steps spread retries over time and are safer on busy channels. + +**Examples:** +``` +get direct.retry.step +set direct.retry.step 0 +set direct.retry.step 50 +set direct.retry.step 100 +set direct.retry.step 250 +``` + +--- + +#### View or change direct retry SNR margin +**Usage:** +- `get direct.retry.margin` +- `set direct.retry.margin ` + +**Parameters:** +- `snr_db`: Extra SNR margin above the SF receive floor, from `0` to `40`. + +**Default:** `5.00` with the `rooftop` preset + +**Notes:** +- Unknown repeaters are still retried. +- Known repeaters below the receive floor plus this margin are skipped. +- Failed attempts lower the recent repeater SNR estimate by `0.25 dB`. + +**Examples:** +``` +get direct.retry.margin +set direct.retry.margin 0 +set direct.retry.margin 2.5 +set direct.retry.margin 5 +set direct.retry.margin 10 +``` + +--- + +#### View or change adaptive direct retry coding rate +**Usage:** +- `get direct.retry.cr` +- `set direct.retry.cr off` +- `set direct.retry.cr ,,,` + +**Parameters:** +- `cr4_min`: Minimum SNR in dB to retry at CR4. +- `cr5_min`: Minimum SNR in dB to retry at CR5. +- `cr7_min`: Minimum SNR in dB to retry at CR7. +- `cr8_max`: Maximum SNR in dB that forces CR8. + +**Default:** `10.00,7.50,2.50,2.50` + +**Explanation:** +- Higher SNR uses faster coding rates. +- Lower SNR uses more robust coding rates. +- CR6 is intentionally skipped. +- `off` disables per-packet retry CR overrides and uses the current radio CR. +- Unknown repeaters start at `+2.00 dB` for adaptive CR selection. +- A failed unknown repeater is seeded at `+1.75 dB`. +- Each later failure lowers the SNR estimate by `0.25 dB`. + +**Examples:** +``` +get direct.retry.cr +set direct.retry.cr off +set direct.retry.cr 10.0,7.5,2.5,2.5 +set direct.retry.cr 12.0,8.0,4.0,1.0 +set direct.retry.cr 8.0,5.0,1.5,0 +set direct.retry.cr 6.0,3.0,0,-2.0 +set direct.retry.cr 20.0,12.0,6.0,2.0 +set direct.retry.cr 4.0,2.0,0,-4.0 +``` + +**Example profiles:** +- Conservative weak-link profile: +``` +set direct.retry.cr 12.0,8.0,4.0,1.0 +``` +- Balanced rooftop profile: +``` +set direct.retry.cr 10.0,7.5,2.5,2.5 +``` +- Faster strong-link profile: +``` +set direct.retry.cr 6.0,3.0,0,-2.0 +``` +- Very cautious noisy-link profile: +``` +set direct.retry.cr 20.0,12.0,6.0,2.0 +``` + +--- + +#### View, seed, or clear the recent repeater table +**Usage:** +- `get recent.repeater` +- `get recent.repeater ` +- `get recent.repeater page ` +- `set recent.repeater ` +- `clear recent.repeater` + +**Parameters:** +- `prefix`: Repeater path-hash prefix as hex. +- `snr_db`: SNR in dB. +- `page`: 1-based result page. + +**Examples:** +``` +get recent.repeater +get recent.repeater 2 +get recent.repeater page 3 +set recent.repeater A1B2C3 8.5 +set recent.repeater 71CE82 -3.25 +clear recent.repeater +``` + --- ### GPS (When GPS support is compiled in) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 45f81ed2..f8946e64 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -575,7 +575,8 @@ uint8_t MyMesh::getDirectRetryCodingRateForSNR(int8_t snr_x4) const { if (snr_x4 >= _prefs.direct_retry_cr4_snr_x4) return 4; if (snr_x4 >= _prefs.direct_retry_cr5_snr_x4) return 5; if (snr_x4 >= _prefs.direct_retry_cr7_snr_x4) return 7; - return 8; + if (snr_x4 <= _prefs.direct_retry_cr8_snr_x4) return 8; + return 7; } uint8_t MyMesh::getDirectRetryPreset() const { @@ -592,6 +593,9 @@ uint32_t MyMesh::getDirectRetryAttemptStepMillis() const { bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const { (void)packet; + if (!_prefs.direct_retry_enabled) { + return false; + } if (next_hop_hash == NULL || next_hop_hash_len == 0) { return true; } @@ -1108,6 +1112,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.direct_retry_cr5_snr_x4 = DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT; _prefs.direct_retry_cr7_snr_x4 = DIRECT_RETRY_CR7_MIN_SNR_X4_DEFAULT; _prefs.direct_retry_cr8_snr_x4 = DIRECT_RETRY_CR8_MAX_SNR_X4_DEFAULT; + _prefs.direct_retry_enabled = 1; _prefs.direct_retry_cr_enabled = 1; _prefs.direct_retry_prefs_magic[0] = DIRECT_RETRY_PREFS_MAGIC_0; _prefs.direct_retry_prefs_magic[1] = DIRECT_RETRY_PREFS_MAGIC_1; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 687b7f7a..4dfbbf6d 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -101,6 +101,7 @@ static void setDefaultDirectRetryPrefs(NodePrefs* prefs) { prefs->direct_retry_cr5_snr_x4 = DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT; prefs->direct_retry_cr7_snr_x4 = DIRECT_RETRY_CR7_MIN_SNR_X4_DEFAULT; prefs->direct_retry_cr8_snr_x4 = DIRECT_RETRY_CR8_MAX_SNR_X4_DEFAULT; + prefs->direct_retry_enabled = 1; markDirectRetryPrefsValid(prefs); } @@ -209,9 +210,10 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->direct_retry_cr5_snr_x4, sizeof(_prefs->direct_retry_cr5_snr_x4)); // 304 file.read((uint8_t *)&_prefs->direct_retry_cr7_snr_x4, sizeof(_prefs->direct_retry_cr7_snr_x4)); // 305 file.read((uint8_t *)&_prefs->direct_retry_cr8_snr_x4, sizeof(_prefs->direct_retry_cr8_snr_x4)); // 306 - file.read((uint8_t *)&_prefs->direct_retry_cr_enabled, sizeof(_prefs->direct_retry_cr_enabled)); // 307 - file.read((uint8_t *)&_prefs->direct_retry_prefs_magic, sizeof(_prefs->direct_retry_prefs_magic)); // 308 - // next: 310 + file.read((uint8_t *)&_prefs->direct_retry_enabled, sizeof(_prefs->direct_retry_enabled)); // 307 + file.read((uint8_t *)&_prefs->direct_retry_cr_enabled, sizeof(_prefs->direct_retry_cr_enabled)); // 308 + file.read((uint8_t *)&_prefs->direct_retry_prefs_magic, sizeof(_prefs->direct_retry_prefs_magic)); // 309 + // next: 311 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -253,6 +255,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->direct_retry_base_ms = constrain(_prefs->direct_retry_base_ms, 10, 5000); _prefs->direct_retry_step_ms = constrain(_prefs->direct_retry_step_ms, 0, 5000); _prefs->direct_retry_snr_margin_x4 = constrain(_prefs->direct_retry_snr_margin_x4, 0, 160); + _prefs->direct_retry_enabled = constrain(_prefs->direct_retry_enabled, 0, 1); _prefs->direct_retry_cr_enabled = constrain(_prefs->direct_retry_cr_enabled, 0, 1); file.close(); @@ -329,9 +332,10 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->direct_retry_cr5_snr_x4, sizeof(_prefs->direct_retry_cr5_snr_x4)); // 304 file.write((uint8_t *)&_prefs->direct_retry_cr7_snr_x4, sizeof(_prefs->direct_retry_cr7_snr_x4)); // 305 file.write((uint8_t *)&_prefs->direct_retry_cr8_snr_x4, sizeof(_prefs->direct_retry_cr8_snr_x4)); // 306 - file.write((uint8_t *)&_prefs->direct_retry_cr_enabled, sizeof(_prefs->direct_retry_cr_enabled)); // 307 - file.write((uint8_t *)&_prefs->direct_retry_prefs_magic, sizeof(_prefs->direct_retry_prefs_magic)); // 308 - // next: 310 + file.write((uint8_t *)&_prefs->direct_retry_enabled, sizeof(_prefs->direct_retry_enabled)); // 307 + file.write((uint8_t *)&_prefs->direct_retry_cr_enabled, sizeof(_prefs->direct_retry_cr_enabled)); // 308 + file.write((uint8_t *)&_prefs->direct_retry_prefs_magic, sizeof(_prefs->direct_retry_prefs_magic)); // 309 + // next: 311 file.close(); } @@ -833,6 +837,18 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { strcpy(reply, "Error, must be infra, rooftop, or mobile"); } + } else if (memcmp(config, "direct.retry ", 13) == 0) { + if (memcmp(&config[13], "on", 2) == 0) { + _prefs->direct_retry_enabled = 1; + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(&config[13], "off", 3) == 0) { + _prefs->direct_retry_enabled = 0; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be on or off"); + } } else if (memcmp(config, "direct.retry.margin ", 20) == 0) { if (!looksNumeric(&config[20])) { strcpy(reply, "Error, must be 0-40 dB"); @@ -1106,6 +1122,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor)); } else if (memcmp(config, "retry.preset", 12) == 0) { sprintf(reply, "> %s", retryPresetName(_prefs->retry_preset)); + } else if (memcmp(config, "direct.retry", 12) == 0 && (config[12] == 0 || config[12] == ' ')) { + sprintf(reply, "> %s", _prefs->direct_retry_enabled ? "on" : "off"); } else if (memcmp(config, "direct.retry.margin", 19) == 0) { char margin[12]; formatSnrDbX4(margin, sizeof(margin), _prefs->direct_retry_snr_margin_x4); diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 7fa71405..76a7cf46 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -102,6 +102,7 @@ struct NodePrefs { // persisted to file int8_t direct_retry_cr5_snr_x4; int8_t direct_retry_cr7_snr_x4; int8_t direct_retry_cr8_snr_x4; + uint8_t direct_retry_enabled; uint8_t direct_retry_cr_enabled; uint8_t direct_retry_prefs_magic[2]; }; From 21c5e2701bf771f0fd5de638a57edf3104dbbd1c Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 15 Jun 2026 18:06:58 -0700 Subject: [PATCH 130/214] Tune unknown retry SNR --- docs/cli_commands.md | 13 +++++++++++-- examples/simple_repeater/MyMesh.cpp | 6 +++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 2d31a18b..6adcc4c7 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -1115,8 +1115,8 @@ set direct.retry.margin 10 - Lower SNR uses more robust coding rates. - CR6 is intentionally skipped. - `off` disables per-packet retry CR overrides and uses the current radio CR. -- Unknown repeaters start at `+2.00 dB` for adaptive CR selection. -- A failed unknown repeater is seeded at `+1.75 dB`. +- Unknown repeaters start at `+3.00 dB` for adaptive CR selection. +- A failed unknown repeater is seeded at `+2.75 dB`. - Each later failure lowers the SNR estimate by `0.25 dB`. **Examples:** @@ -1164,6 +1164,15 @@ set direct.retry.cr 20.0,12.0,6.0,2.0 - `snr_db`: SNR in dB. - `page`: 1-based result page. +**SNR details:** +- Recent repeater SNR is stored internally in quarter-dB units. +- Heard repeater samples update an existing table entry with a weighted blend: `75%` existing SNR and `25%` new heard SNR, rounded up. +- Direct retry success also feeds the heard echo SNR back into the same weighted table. +- Direct retry failure is not weighted: each final echo-timeout failure lowers that repeater's SNR by `0.25 dB`. +- Unknown repeaters start at `+3.00 dB` for adaptive CR selection. +- If an unknown repeater fails, it is seeded into the table at `+2.75 dB`. +- `set recent.repeater ` seeds a missing prefix or adds another weighted sample for an existing prefix. + **Examples:** ``` get recent.repeater diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index f8946e64..05c308bc 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -606,7 +606,7 @@ bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_ho if (repeater == NULL) { // Retry unknown repeaters too. If they fail, onDirectRetryFailed() seeds the - // recent-repeater table below the +2.00 dB starting point. + // recent-repeater table below the +3.00 dB starting point. return true; } int16_t retry_floor_x4 = (int16_t)getDirectRetryMinSNRX4() + (int16_t)_prefs.direct_retry_snr_margin_x4; @@ -615,7 +615,7 @@ bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_ho void MyMesh::configureDirectRetryPacket(mesh::Packet* retry, const mesh::Packet* original, uint8_t retry_attempt) { (void)retry_attempt; - int8_t snr_x4 = 8; // unknown repeaters start at +2.00 dB + int8_t snr_x4 = 12; // unknown repeaters start at +3.00 dB const SimpleMeshTables* tables = static_cast(getTables()); if (tables != NULL) { uint8_t prefix[MAX_HASH_SIZE]; @@ -683,7 +683,7 @@ void MyMesh::onDirectRetryFailed(const uint8_t* next_hop_hash, uint8_t next_hop_ SimpleMeshTables* tables = static_cast(getTables()); if (tables != NULL) { if (!tables->decrementRecentRepeaterSnrX4(next_hop_hash, next_hop_hash_len, 1)) { - tables->setRecentRepeater(next_hop_hash, next_hop_hash_len, 7, false, true); + tables->setRecentRepeater(next_hop_hash, next_hop_hash_len, 11, false, true); } } } From e35886780c9d3ce6a11c38d561e2e94abb6361ac Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 15 Jun 2026 18:10:40 -0700 Subject: [PATCH 131/214] Simplify recent repeater paging --- docs/cli_commands.md | 2 -- src/helpers/CommonCLI.cpp | 1 - 2 files changed, 3 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 6adcc4c7..50a14b77 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -1155,7 +1155,6 @@ set direct.retry.cr 20.0,12.0,6.0,2.0 **Usage:** - `get recent.repeater` - `get recent.repeater ` -- `get recent.repeater page ` - `set recent.repeater ` - `clear recent.repeater` @@ -1177,7 +1176,6 @@ set direct.retry.cr 20.0,12.0,6.0,2.0 ``` get recent.repeater get recent.repeater 2 -get recent.repeater page 3 set recent.repeater A1B2C3 8.5 set recent.repeater 71CE82 -3.25 clear recent.repeater diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 4dfbbf6d..78cd45b9 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -1149,7 +1149,6 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep int page = 1; const char* cursor = &config[15]; while (*cursor == ' ') cursor++; - if (memcmp(cursor, "page ", 5) == 0) cursor += 5; if (*cursor) page = _atoi(cursor); if (page < 1) page = 1; _callbacks->formatRecentRepeatersReply(reply, page); From 4ed9415866d635cb6cbfcbb9d14076276b4bc245 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 15 Jun 2026 18:15:00 -0700 Subject: [PATCH 132/214] Clean up Mesh comments --- src/Mesh.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Mesh.h b/src/Mesh.h index d1b66f62..4511b394 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -190,7 +190,6 @@ protected: /** * \brief A path TO peer (sender_idx) has been received. (also with optional 'extra' data encoded) - * NOTE: these can be received multiple times (per sender), via differen routes * \param sender_idx index of peer, [0..n) where n is what searchPeersByHash() returned * \param secret the pre-calculated shared-secret (handy for sending response packet) * \returns true, if path was accepted and that reciprocal path should be sent @@ -213,7 +212,6 @@ protected: /** * \brief A path TO 'sender' has been received. (also with optional 'extra' data encoded) - * NOTE: these can be received multiple times (per sender), via differen routes */ virtual void onPathRecv(Packet* packet, Identity& sender, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) { } @@ -295,12 +293,10 @@ public: void sendDirect(Packet* packet, const uint8_t* path, uint8_t path_len, uint32_t delay_millis=0); /** - * \brief send a locally-generated Packet to just neigbor nodes (zero hops) */ void sendZeroHop(Packet* packet, uint32_t delay_millis=0); /** - * \brief send a locally-generated Packet to just neigbor nodes (zero hops), with specific transort codes * \param transport_codes array of 2 codes to attach to packet */ void sendZeroHop(Packet* packet, uint16_t* transport_codes, uint32_t delay_millis=0); From 7d2ff381207c3b84ee083ebc0fe45bfb653c5151 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 16 Jun 2026 13:15:52 -0700 Subject: [PATCH 133/214] Trim direct retry cleanup --- examples/simple_repeater/MyMesh.cpp | 13 ------------- examples/simple_repeater/MyMesh.h | 3 --- src/Mesh.cpp | 5 ++--- src/Mesh.h | 10 ++++++++-- 4 files changed, 10 insertions(+), 21 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 05c308bc..4261edd9 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -579,10 +579,6 @@ uint8_t MyMesh::getDirectRetryCodingRateForSNR(int8_t snr_x4) const { return 7; } -uint8_t MyMesh::getDirectRetryPreset() const { - return _prefs.retry_preset; -} - uint8_t MyMesh::getDirectRetryConfiguredMaxAttempts() const { return constrain(_prefs.direct_retry_attempts, 1, 15); } @@ -631,11 +627,6 @@ void MyMesh::configureDirectRetryPacket(mesh::Packet* retry, const mesh::Packet* retry->tx_cr = getDirectRetryCodingRateForSNR(snr_x4); } -bool MyMesh::maybeShortCircuitDirect(mesh::Packet* packet) { - (void)packet; - return false; -} - uint32_t MyMesh::getDirectRetryEchoDelay(const mesh::Packet* packet) const { (void)packet; return 200; @@ -1071,7 +1062,6 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc next_local_advert = next_flood_advert = 0; dirty_contacts_expiry = 0; set_radio_at = revert_radio_at = 0; - active_bw = 0.0f; active_sf = 0; active_cr = 0; _logging = false; @@ -1184,7 +1174,6 @@ void MyMesh::begin(FILESYSTEM *fs) { #endif radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); - active_bw = _prefs.bw; active_sf = _prefs.sf; active_cr = _prefs.cr; radio_driver.setTxPower(_prefs.tx_power_dbm); @@ -1514,7 +1503,6 @@ void MyMesh::loop() { if (set_radio_at && millisHasNowPassed(set_radio_at)) { // apply pending (temporary) radio params set_radio_at = 0; // clear timer radio_driver.setParams(pending_freq, pending_bw, pending_sf, pending_cr); - active_bw = pending_bw; active_sf = pending_sf; active_cr = pending_cr; MESH_DEBUG_PRINTLN("Temp radio params"); @@ -1523,7 +1511,6 @@ void MyMesh::loop() { if (revert_radio_at && millisHasNowPassed(revert_radio_at)) { // revert radio params to orig revert_radio_at = 0; // clear timer radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); - active_bw = _prefs.bw; active_sf = _prefs.sf; active_cr = _prefs.cr; MESH_DEBUG_PRINTLN("Radio params restored"); diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 055b4b83..fddbed3e 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -110,7 +110,6 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { unsigned long set_radio_at, revert_radio_at; float pending_freq; float pending_bw; - float active_bw; // live BW, including temporary radio overrides uint8_t pending_sf; uint8_t active_sf; // live SF, including temporary radio overrides uint8_t pending_cr; @@ -125,7 +124,6 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { bool extractDirectRetryPrefix(const mesh::Packet* packet, uint8_t* prefix, uint8_t& prefix_len) const; int8_t getDirectRetryMinSNRX4() const; uint8_t getDirectRetryCodingRateForSNR(int8_t snr_x4) const; - uint8_t getDirectRetryPreset() const; uint8_t getDirectRetryConfiguredMaxAttempts() const; uint32_t getDirectRetryAttemptStepMillis() const; void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr); @@ -157,7 +155,6 @@ protected: uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override; uint8_t getDefaultTxCodingRate() const override { return active_cr; } bool allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const override; - bool maybeShortCircuitDirect(mesh::Packet* packet) override; void configureDirectRetryPacket(mesh::Packet* retry, const mesh::Packet* original, uint8_t retry_attempt) override; uint32_t getDirectRetryEchoDelay(const mesh::Packet* packet) const override; uint8_t getDirectRetryMaxAttempts(const mesh::Packet* packet) const override; diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 0bac461b..a4b2750d 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -185,7 +185,7 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { } } - if (self_id.isHashMatch(pkt->path, pkt->getPathHashSize()) || maybeShortCircuitDirect(pkt)) { + if (self_id.isHashMatch(pkt->path, pkt->getPathHashSize())) { if (allowPacketForward(pkt)) { if (pkt->getPayloadType() == PAYLOAD_TYPE_MULTIPART) { return forwardMultipartDirect(pkt); @@ -448,9 +448,8 @@ DispatcherAction Mesh::routeRecvPacket(Packet* packet) { packet->setPathHashCount(n + 1); uint32_t d = getRetransmitDelay(packet); - uint8_t priority = packet->getPathHashCount(); // as this propagates outwards, give it lower and lower priority - return ACTION_RETRANSMIT_DELAYED(priority, d); // give priority to closer sources, than ones further away + return ACTION_RETRANSMIT_DELAYED(packet->getPathHashCount(), d); // give priority to closer sources, than ones further away } return ACTION_RELEASE; } diff --git a/src/Mesh.h b/src/Mesh.h index 4511b394..523c204b 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -111,8 +111,6 @@ protected: /** * \brief Allow subclasses to rewrite a non-TRACE DIRECT packet path when this node can safely skip ahead. */ - virtual bool maybeShortCircuitDirect(Packet* packet) { return false; } - /** * \returns milliseconds to wait for the next-hop echo before queueing one retry of the DIRECT packet. */ @@ -190,6 +188,8 @@ protected: /** * \brief A path TO peer (sender_idx) has been received. (also with optional 'extra' data encoded) + * NOTE: these can be received multiple times (per sender), via different routes + * NOTE: these can be received multiple times (per sender), via differen routes * \param sender_idx index of peer, [0..n) where n is what searchPeersByHash() returned * \param secret the pre-calculated shared-secret (handy for sending response packet) * \returns true, if path was accepted and that reciprocal path should be sent @@ -212,6 +212,8 @@ protected: /** * \brief A path TO 'sender' has been received. (also with optional 'extra' data encoded) + * NOTE: these can be received multiple times (per sender), via different routes + * NOTE: these can be received multiple times (per sender), via differen routes */ virtual void onPathRecv(Packet* packet, Identity& sender, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) { } @@ -293,10 +295,14 @@ public: void sendDirect(Packet* packet, const uint8_t* path, uint8_t path_len, uint32_t delay_millis=0); /** + * \brief send a locally-generated Packet to just neighbor nodes (zero hops) + * \brief send a locally-generated Packet to just neigbor nodes (zero hops) */ void sendZeroHop(Packet* packet, uint32_t delay_millis=0); /** + * \brief send a locally-generated Packet to just neighbor nodes (zero hops), with specific transport codes + * \brief send a locally-generated Packet to just neigbor nodes (zero hops), with specific transort codes * \param transport_codes array of 2 codes to attach to packet */ void sendZeroHop(Packet* packet, uint16_t* transport_codes, uint32_t delay_millis=0); From f58cdef8a82d50c704af970f7d5553c83022e7f4 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 16 Jun 2026 13:21:51 -0700 Subject: [PATCH 134/214] Revert Mesh comment noise --- src/Mesh.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mesh.h b/src/Mesh.h index 523c204b..81842aa1 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -82,7 +82,7 @@ protected: /** * \brief Called _before_ the packet is dispatched to the on..Recv() methods. - * \returns true, if given packet should be NOT be processed. + * \returns true, if given packet should NOT be processed. */ virtual bool filterRecvFloodPacket(Packet* packet) { return false; } From 4a9d6bae52f228ad7c4af90ba356c19ce5447c09 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 16 Jun 2026 13:25:04 -0700 Subject: [PATCH 135/214] Remove duplicate Mesh comments --- src/Mesh.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Mesh.h b/src/Mesh.h index 81842aa1..04c8b84b 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -189,7 +189,6 @@ protected: /** * \brief A path TO peer (sender_idx) has been received. (also with optional 'extra' data encoded) * NOTE: these can be received multiple times (per sender), via different routes - * NOTE: these can be received multiple times (per sender), via differen routes * \param sender_idx index of peer, [0..n) where n is what searchPeersByHash() returned * \param secret the pre-calculated shared-secret (handy for sending response packet) * \returns true, if path was accepted and that reciprocal path should be sent @@ -213,7 +212,6 @@ protected: /** * \brief A path TO 'sender' has been received. (also with optional 'extra' data encoded) * NOTE: these can be received multiple times (per sender), via different routes - * NOTE: these can be received multiple times (per sender), via differen routes */ virtual void onPathRecv(Packet* packet, Identity& sender, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) { } @@ -296,13 +294,11 @@ public: /** * \brief send a locally-generated Packet to just neighbor nodes (zero hops) - * \brief send a locally-generated Packet to just neigbor nodes (zero hops) */ void sendZeroHop(Packet* packet, uint32_t delay_millis=0); /** * \brief send a locally-generated Packet to just neighbor nodes (zero hops), with specific transport codes - * \brief send a locally-generated Packet to just neigbor nodes (zero hops), with specific transort codes * \param transport_codes array of 2 codes to attach to packet */ void sendZeroHop(Packet* packet, uint16_t* transport_codes, uint32_t delay_millis=0); From 1f14f744af90244e6160f764d490bc1f6df28810 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 16 Jun 2026 13:28:46 -0700 Subject: [PATCH 136/214] Clean Mesh header comments --- src/Mesh.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Mesh.h b/src/Mesh.h index 04c8b84b..b342aed2 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -29,8 +29,8 @@ public: * and provides virtual methods for sub-classes on handling incoming, and also preparing outbound Packets. */ class Mesh : public Dispatcher { - RNG* _rng; RTCClock* _rtc; + RNG* _rng; MeshTables* _tables; struct DirectRetryEntry { @@ -104,13 +104,10 @@ protected: /** * \brief Decide whether a DIRECT packet should get one delayed retry if the next hop echo is not overheard. - * Sub-classes can use neighbour tables or other link-quality data to opt in selectively. + * Sub-classes can use recent repeater or other link-quality data to opt in selectively. */ virtual bool allowDirectRetry(const Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const; - /** - * \brief Allow subclasses to rewrite a non-TRACE DIRECT packet path when this node can safely skip ahead. - */ /** * \returns milliseconds to wait for the next-hop echo before queueing one retry of the DIRECT packet. */ From 9c510ab7a65db218fca16b101c60914d628f42fd Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 16 Jun 2026 13:30:49 -0700 Subject: [PATCH 137/214] Add Packet EOF newline --- src/Packet.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Packet.cpp b/src/Packet.cpp index 3aab6349..a542a5a2 100644 --- a/src/Packet.cpp +++ b/src/Packet.cpp @@ -87,3 +87,4 @@ bool Packet::readFrom(const uint8_t src[], uint8_t len) { } } + From 8d46ed4f21b6b7abc113bb102024635057d00fe1 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 16 Jun 2026 16:25:04 -0700 Subject: [PATCH 138/214] Enable retry CR radio overrides --- docs/cli_commands.md | 8 +++--- src/Dispatcher.cpp | 23 +++++++++++++--- src/Dispatcher.h | 6 ++++- src/helpers/CommonCLI.cpp | 28 ++++++++++++++++---- src/helpers/radiolib/CustomLLCC68Wrapper.h | 4 +++ src/helpers/radiolib/CustomLR1110Wrapper.h | 4 +++ src/helpers/radiolib/CustomSTM32WLxWrapper.h | 4 +++ src/helpers/radiolib/CustomSX1262Wrapper.h | 4 +++ src/helpers/radiolib/CustomSX1268Wrapper.h | 4 +++ src/helpers/radiolib/CustomSX1276Wrapper.h | 4 +++ src/helpers/radiolib/RadioLibWrappers.h | 2 ++ 11 files changed, 78 insertions(+), 13 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 50a14b77..9c816cf0 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -1155,12 +1155,12 @@ set direct.retry.cr 20.0,12.0,6.0,2.0 **Usage:** - `get recent.repeater` - `get recent.repeater ` -- `set recent.repeater ` +- `set recent.repeater [snr_db]` - `clear recent.repeater` **Parameters:** - `prefix`: Repeater path-hash prefix as hex. -- `snr_db`: SNR in dB. +- `snr_db`: Optional SNR in dB. If omitted or invalid, defaults to `3.0`. - `page`: 1-based result page. **SNR details:** @@ -1170,7 +1170,8 @@ set direct.retry.cr 20.0,12.0,6.0,2.0 - Direct retry failure is not weighted: each final echo-timeout failure lowers that repeater's SNR by `0.25 dB`. - Unknown repeaters start at `+3.00 dB` for adaptive CR selection. - If an unknown repeater fails, it is seeded into the table at `+2.75 dB`. -- `set recent.repeater ` seeds a missing prefix or adds another weighted sample for an existing prefix. +- `set recent.repeater [snr_db]` seeds a missing prefix or adds another weighted sample for an existing prefix. +- Successful `set recent.repeater` replies include the stored prefix and SNR, for example `OK - set A1B2C3 at 3.0 SNR`. **Examples:** ``` @@ -1178,6 +1179,7 @@ get recent.repeater get recent.repeater 2 set recent.repeater A1B2C3 8.5 set recent.repeater 71CE82 -3.25 +set recent.repeater A1B2C3 clear recent.repeater ``` diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index b870be9e..63112f85 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -52,11 +52,15 @@ void Dispatcher::updateTxBudget() { } } -void Dispatcher::restoreOutboundCodingRate() { +void Dispatcher::restoreOutboundTxOverrides() { if (outbound_restore_cr != 0) { _radio->setCodingRate(outbound_restore_cr); outbound_restore_cr = 0; } + if (outbound_restore_preamble_len != 0) { + _radio->setPreambleLength(outbound_restore_preamble_len); + outbound_restore_preamble_len = 0; + } } int Dispatcher::calcRxDelay(float score, uint32_t air_time) const { @@ -113,7 +117,7 @@ void Dispatcher::loop() { } _radio->onSendFinished(); - restoreOutboundCodingRate(); + restoreOutboundTxOverrides(); logTx(outbound, 2 + outbound->getPathByteLen() + outbound->payload_len); onSendComplete(outbound); if (outbound->isRouteFlood()) { @@ -127,7 +131,7 @@ void Dispatcher::loop() { MESH_DEBUG_PRINTLN("%s Dispatcher::loop(): WARNING: outbound packed send timed out!", getLogDateTime()); _radio->onSendFinished(); - restoreOutboundCodingRate(); + restoreOutboundTxOverrides(); logTxFail(outbound, 2 + outbound->getPathByteLen() + outbound->payload_len); onSendFail(outbound); @@ -342,6 +346,7 @@ void Dispatcher::checkSend() { uint32_t max_airtime = _radio->getEstAirtimeFor(len)*3/2; outbound_restore_cr = 0; + outbound_restore_preamble_len = 0; uint8_t default_cr = getDefaultTxCodingRate(); if (outbound->tx_cr >= 4 && outbound->tx_cr <= 8 && default_cr >= 4 && default_cr <= 8 && outbound->tx_cr != default_cr) { @@ -352,12 +357,22 @@ void Dispatcher::checkSend() { MESH_DEBUG_PRINTLN("%s Dispatcher::checkSend(): WARN: failed to set packet CR%d", getLogDateTime(), (uint32_t)outbound->tx_cr); } } + bool has_direct_path = outbound->getPathHashCount() > 0 + || (outbound->getPayloadType() == PAYLOAD_TYPE_TRACE && outbound->payload_len > 9); + if (outbound->isRouteDirect() && has_direct_path + && (outbound->tx_cr == 4 || outbound->tx_cr == 5)) { + uint16_t default_preamble_len = _radio->getDefaultPreambleLength(); + if (default_preamble_len > 16 && _radio->setPreambleLength(16)) { + outbound_restore_preamble_len = default_preamble_len; + max_airtime = _radio->getEstAirtimeFor(len)*3/2; + } + } outbound_start = _ms->getMillis(); bool success = _radio->startSendRaw(raw, len); if (!success) { MESH_DEBUG_PRINTLN("%s Dispatcher::loop(): ERROR: send start failed!", getLogDateTime()); - restoreOutboundCodingRate(); + restoreOutboundTxOverrides(); logTxFail(outbound, outbound->getRawLength()); onSendFail(outbound); diff --git a/src/Dispatcher.h b/src/Dispatcher.h index e6daae4b..cb86bb4b 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -51,6 +51,8 @@ public: * \returns true if the radio accepted the coding rate. */ virtual bool setCodingRate(uint8_t cr) { return false; } + virtual uint16_t getDefaultPreambleLength() const { return 0; } + virtual bool setPreambleLength(uint16_t len) { return false; } /** * \returns true if the previous 'startSendRaw()' completed successfully. @@ -124,6 +126,7 @@ typedef uint32_t DispatcherAction; class Dispatcher { Packet* outbound; // current outbound packet unsigned long outbound_expiry, outbound_start, total_air_time, rx_air_time; + uint16_t outbound_restore_preamble_len; uint8_t outbound_restore_cr; unsigned long next_tx_time; unsigned long cad_busy_start; @@ -137,7 +140,7 @@ class Dispatcher { unsigned long duty_cycle_window_ms; void processRecvPacket(Packet* pkt); - void restoreOutboundCodingRate(); + void restoreOutboundTxOverrides(); void updateTxBudget(); protected: @@ -150,6 +153,7 @@ protected: : _radio(&radio), _ms(&ms), _mgr(&mgr) { outbound = NULL; + outbound_restore_preamble_len = 0; outbound_restore_cr = 0; total_air_time = rx_air_time = 0; next_tx_time = ms.getMillis(); diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 78cd45b9..4c179223 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -9,6 +9,8 @@ #define BRIDGE_MAX_BAUD 115200 #endif +#define RECENT_REPEATER_PREFIX_MAX_BYTES 3 + // Believe it or not, this std C function is busted on some platforms! static uint32_t _atoi(const char* sp) { uint32_t n = 0; @@ -128,13 +130,21 @@ static bool parseRetryPreset(const char* s, uint8_t& preset) { static bool parseHashPrefix(const char* text, uint8_t* prefix, uint8_t& prefix_len) { size_t hex_len = strlen(text); - if (hex_len == 0 || (hex_len & 1) || hex_len > MAX_HASH_SIZE * 2) { + if (hex_len == 0 || (hex_len & 1) || hex_len > RECENT_REPEATER_PREFIX_MAX_BYTES * 2) { return false; } prefix_len = hex_len / 2; return mesh::Utils::fromHex(prefix, prefix_len, text); } +static void formatSnrDbX4Short(char* dest, size_t dest_len, int16_t snr_x4) { + formatSnrDbX4(dest, dest_len, snr_x4); + size_t len = strlen(dest); + if (len > 3 && dest[len - 1] == '0') { + dest[len - 1] = 0; + } +} + void CommonCLI::loadPrefs(FILESYSTEM* fs) { if (fs->exists("/com_prefs")) { loadPrefsInt(fs, "/com_prefs"); // new filename @@ -928,13 +938,21 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep int num = mesh::Utils::parseTextParts(tmp, parts, 2, ' '); uint8_t prefix[MAX_HASH_SIZE]; uint8_t prefix_len = 0; - int16_t snr_x4 = num > 1 && looksNumeric(parts[1]) ? parseSnrDbX4(parts[1]) : 0; - if (num != 2 || !parseHashPrefix(parts[0], prefix, prefix_len)) { - strcpy(reply, "Error, use: set recent.repeater "); + int16_t snr_x4 = 12; // default to +3.0 dB when omitted or invalid + if (num > 1 && looksNumeric(parts[1])) { + snr_x4 = parseSnrDbX4(parts[1]); + } + if (num < 1 || !parseHashPrefix(parts[0], prefix, prefix_len)) { + strcpy(reply, "Error, use: set recent.repeater [snr_db]"); } else if (snr_x4 < -128 || snr_x4 > 127) { strcpy(reply, "Error, SNR must fit -32.00..31.75 dB"); } else if (_callbacks->setRecentRepeater(prefix, prefix_len, (int8_t)snr_x4)) { - strcpy(reply, "OK"); + char prefix_hex[RECENT_REPEATER_PREFIX_MAX_BYTES * 2 + 1]; + char snr[12]; + mesh::Utils::toHex(prefix_hex, prefix, prefix_len); + prefix_hex[prefix_len * 2] = 0; + formatSnrDbX4Short(snr, sizeof(snr), snr_x4); + sprintf(reply, "OK - set %s at %s SNR", prefix_hex, snr); } else { strcpy(reply, "Error, table rejected prefix"); } diff --git a/src/helpers/radiolib/CustomLLCC68Wrapper.h b/src/helpers/radiolib/CustomLLCC68Wrapper.h index 8861f76d..620ef864 100644 --- a/src/helpers/radiolib/CustomLLCC68Wrapper.h +++ b/src/helpers/radiolib/CustomLLCC68Wrapper.h @@ -16,6 +16,10 @@ public: updatePreamble(sf); } + bool setCodingRate(uint8_t cr) override { + return ((CustomLLCC68 *)_radio)->setCodingRate(cr) == RADIOLIB_ERR_NONE; + } + bool isReceivingPacket() override { return ((CustomLLCC68 *)_radio)->isReceiving(); } diff --git a/src/helpers/radiolib/CustomLR1110Wrapper.h b/src/helpers/radiolib/CustomLR1110Wrapper.h index 13efd25b..4d30f515 100644 --- a/src/helpers/radiolib/CustomLR1110Wrapper.h +++ b/src/helpers/radiolib/CustomLR1110Wrapper.h @@ -16,6 +16,10 @@ public: updatePreamble(sf); } + bool setCodingRate(uint8_t cr) override { + return ((CustomLR1110 *)_radio)->setCodingRate(cr) == RADIOLIB_ERR_NONE; + } + void doResetAGC() override { lr11x0ResetAGC((LR11x0 *)_radio, ((CustomLR1110 *)_radio)->getFreqMHz()); } bool isReceivingPacket() override { return ((CustomLR1110 *)_radio)->isReceiving(); diff --git a/src/helpers/radiolib/CustomSTM32WLxWrapper.h b/src/helpers/radiolib/CustomSTM32WLxWrapper.h index 97bf6820..45e275a3 100644 --- a/src/helpers/radiolib/CustomSTM32WLxWrapper.h +++ b/src/helpers/radiolib/CustomSTM32WLxWrapper.h @@ -17,6 +17,10 @@ public: updatePreamble(sf); } + bool setCodingRate(uint8_t cr) override { + return ((CustomSTM32WLx *)_radio)->setCodingRate(cr) == RADIOLIB_ERR_NONE; + } + bool isReceivingPacket() override { return ((CustomSTM32WLx *)_radio)->isReceiving(); } diff --git a/src/helpers/radiolib/CustomSX1262Wrapper.h b/src/helpers/radiolib/CustomSX1262Wrapper.h index cc7bb223..66f7adbf 100644 --- a/src/helpers/radiolib/CustomSX1262Wrapper.h +++ b/src/helpers/radiolib/CustomSX1262Wrapper.h @@ -20,6 +20,10 @@ public: updatePreamble(sf); } + bool setCodingRate(uint8_t cr) override { + return ((CustomSX1262 *)_radio)->setCodingRate(cr) == RADIOLIB_ERR_NONE; + } + bool isReceivingPacket() override { return ((CustomSX1262 *)_radio)->isReceiving(); } diff --git a/src/helpers/radiolib/CustomSX1268Wrapper.h b/src/helpers/radiolib/CustomSX1268Wrapper.h index 9ddea78f..b87e1cd6 100644 --- a/src/helpers/radiolib/CustomSX1268Wrapper.h +++ b/src/helpers/radiolib/CustomSX1268Wrapper.h @@ -20,6 +20,10 @@ public: updatePreamble(sf); } + bool setCodingRate(uint8_t cr) override { + return ((CustomSX1268 *)_radio)->setCodingRate(cr) == RADIOLIB_ERR_NONE; + } + bool isReceivingPacket() override { return ((CustomSX1268 *)_radio)->isReceiving(); } diff --git a/src/helpers/radiolib/CustomSX1276Wrapper.h b/src/helpers/radiolib/CustomSX1276Wrapper.h index 9d75ce12..cb073f91 100644 --- a/src/helpers/radiolib/CustomSX1276Wrapper.h +++ b/src/helpers/radiolib/CustomSX1276Wrapper.h @@ -19,6 +19,10 @@ public: updatePreamble(sf); } + bool setCodingRate(uint8_t cr) override { + return ((CustomSX1276 *)_radio)->setCodingRate(cr) == RADIOLIB_ERR_NONE; + } + bool isReceivingPacket() override { return ((CustomSX1276 *)_radio)->isReceiving(); } diff --git a/src/helpers/radiolib/RadioLibWrappers.h b/src/helpers/radiolib/RadioLibWrappers.h index 9943bcab..07740234 100644 --- a/src/helpers/radiolib/RadioLibWrappers.h +++ b/src/helpers/radiolib/RadioLibWrappers.h @@ -40,6 +40,8 @@ public: } virtual void setParams(float freq, float bw, uint8_t sf, uint8_t cr) = 0; + uint16_t getDefaultPreambleLength() const override { return preambleLengthForSF(_preamble_sf); } + bool setPreambleLength(uint16_t len) override { return _radio->setPreambleLength(len) == RADIOLIB_ERR_NONE; } uint32_t getRngSeed(); void setTxPower(int8_t dbm); From 61e0e28e9a33297a3a2312d8138321bb13547e49 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Wed, 17 Jun 2026 16:58:37 +0700 Subject: [PATCH 139/214] Added ThinkNode_M5_companion_radio_ble_ps --- variants/thinknode_m5/platformio.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/variants/thinknode_m5/platformio.ini b/variants/thinknode_m5/platformio.ini index 8572d1eb..4ff37c0e 100644 --- a/variants/thinknode_m5/platformio.ini +++ b/variants/thinknode_m5/platformio.ini @@ -165,6 +165,10 @@ lib_deps = densaugeo/base64 @ ~1.4.0 end2endzone/NonBlockingRTTTL@^1.3.0 +[env:ThinkNode_M5_companion_radio_ble_ps] +extends = env:ThinkNode_M5_companion_radio_ble +platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 + [env:ThinkNode_M5_companion_radio_usb] extends = ThinkNode_M5 build_flags = From 19cfc43269e0fddb01e7573fc98786ee2a8f0b38 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Wed, 17 Jun 2026 16:32:04 -0700 Subject: [PATCH 140/214] Trim recent repeater API and document retry defaults --- docs/cli_commands.md | 8 ++ examples/simple_repeater/MyMesh.cpp | 8 +- src/helpers/SimpleMeshTables.h | 158 +++++++++------------------- 3 files changed, 64 insertions(+), 110 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 9c816cf0..6657097e 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -965,6 +965,9 @@ Direct retry resends direct-routed packets when the downstream echo is not heard **Default:** `on` +**Notes:** +- New installs and older preference files without direct retry settings default to `on` with the `rooftop` preset. + **Examples:** ``` get direct.retry @@ -1115,6 +1118,7 @@ set direct.retry.margin 10 - Lower SNR uses more robust coding rates. - CR6 is intentionally skipped. - `off` disables per-packet retry CR overrides and uses the current radio CR. +- Direct path retry packets sent at CR4 or CR5 temporarily use a shorter 16-symbol preamble, then restore the radio's default preamble. - Unknown repeaters start at `+3.00 dB` for adaptive CR selection. - A failed unknown repeater is seeded at `+2.75 dB`. - Each later failure lowers the SNR estimate by `0.25 dB`. @@ -1163,6 +1167,10 @@ set direct.retry.cr 20.0,12.0,6.0,2.0 - `snr_db`: Optional SNR in dB. If omitted or invalid, defaults to `3.0`. - `page`: 1-based result page. +**Output order:** +- `get recent.repeater` lists 3-byte prefixes first, then 2-byte prefixes, then 1-byte prefixes. +- Within each prefix length, entries are sorted from highest SNR to lowest SNR. + **SNR details:** - Recent repeater SNR is stored internally in quarter-dB units. - Heard repeater samples update an existing table entry with a weighted blend: `75%` existing SNR and `25%` new heard SNR, rounded up. diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 4261edd9..4765e7af 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -674,7 +674,7 @@ void MyMesh::onDirectRetryFailed(const uint8_t* next_hop_hash, uint8_t next_hop_ SimpleMeshTables* tables = static_cast(getTables()); if (tables != NULL) { if (!tables->decrementRecentRepeaterSnrX4(next_hop_hash, next_hop_hash_len, 1)) { - tables->setRecentRepeater(next_hop_hash, next_hop_hash_len, 11, false, true); + tables->setRecentRepeater(next_hop_hash, next_hop_hash_len, 11); } } } @@ -686,7 +686,7 @@ void MyMesh::onDirectRetrySucceeded(const uint8_t* next_hop_hash, uint8_t next_h SimpleMeshTables* tables = static_cast(getTables()); if (tables != NULL) { - tables->setRecentRepeater(next_hop_hash, next_hop_hash_len, snr_x4, false, true); + tables->setRecentRepeater(next_hop_hash, next_hop_hash_len, snr_x4); } } @@ -720,7 +720,7 @@ void MyMesh::formatRecentRepeatersReply(char *reply, int page) { int len = snprintf(reply, 160, "> %d/%d ", page, pages); int start = (page - 1) * page_size; for (int i = 0; i < page_size && len < 150; i++) { - const SimpleMeshTables::RecentRepeaterInfo* info = tables->getRecentRepeaterNewestByIdx(start + i); + const SimpleMeshTables::RecentRepeaterInfo* info = tables->getRecentRepeaterBySortedIdx(start + i); if (info == NULL) break; char prefix[MAX_ROUTE_HASH_BYTES * 2 + 1]; char snr[12]; @@ -736,7 +736,7 @@ void MyMesh::formatRecentRepeatersReply(char *reply, int page) { bool MyMesh::setRecentRepeater(const uint8_t* prefix, uint8_t prefix_len, int8_t snr_x4) { SimpleMeshTables* tables = static_cast(getTables()); - return tables != NULL && tables->setRecentRepeater(prefix, prefix_len, snr_x4, false, true); + return tables != NULL && tables->setRecentRepeater(prefix, prefix_len, snr_x4); } void MyMesh::clearRecentRepeaters() { diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index f1d52733..fb717bfc 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -1,9 +1,6 @@ #pragma once #include -#if ARDUINO - #include -#endif #ifdef ESP32 #include @@ -24,15 +21,11 @@ class SimpleMeshTables : public mesh::MeshTables { public: - typedef bool (*RecentRepeaterAllowFn)(const uint8_t* prefix, uint8_t prefix_len, void* ctx); - struct RecentRepeaterInfo { // Identity and link quality for a next-hop path prefix. uint8_t prefix[MAX_ROUTE_HASH_BYTES]; uint8_t prefix_len; int8_t snr_x4; - uint8_t snr_locked; - uint32_t last_heard_millis; }; private: @@ -40,10 +33,6 @@ private: int _next_idx; uint32_t _direct_dups, _flood_dups; RecentRepeaterInfo _recent_repeaters[MAX_RECENT_REPEATERS]; - int _next_recent_repeater_idx; - int8_t _recent_repeater_min_snr_x4; - RecentRepeaterAllowFn _recent_repeater_allow_fn; - void* _recent_repeater_allow_ctx; bool hasSeenHash(const uint8_t* hash) const { const uint8_t* sp = _hashes; @@ -115,15 +104,27 @@ private: return false; } + bool recentRepeaterComesBefore(const RecentRepeaterInfo& a, int a_idx, + const RecentRepeaterInfo& b, int b_idx) const { + if (a.prefix_len != b.prefix_len) { + return a.prefix_len > b.prefix_len; // 3-byte prefixes, then 2-byte, then 1-byte. + } + if (a.snr_x4 != b.snr_x4) { + return a.snr_x4 > b.snr_x4; // Highest SNR first within each prefix length. + } + int cmp = memcmp(a.prefix, b.prefix, a.prefix_len); + if (cmp != 0) { + return cmp < 0; + } + return a_idx < b_idx; + } + void recordRecentRepeater(const mesh::Packet* packet) { uint8_t prefix[MAX_ROUTE_HASH_BYTES] = {0}; uint8_t prefix_len = 0; if (!extractRecentRepeater(packet, prefix, prefix_len) || prefix_len == 0) { return; } - if (packet->_snr < _recent_repeater_min_snr_x4) { - return; - } setRecentRepeater(prefix, prefix_len, packet->_snr); } @@ -133,10 +134,6 @@ public: _next_idx = 0; _direct_dups = _flood_dups = 0; memset(_recent_repeaters, 0, sizeof(_recent_repeaters)); - _next_recent_repeater_idx = 0; - _recent_repeater_min_snr_x4 = -128; - _recent_repeater_allow_fn = NULL; - _recent_repeater_allow_ctx = NULL; } #ifdef ESP32 @@ -147,7 +144,6 @@ public: // This avoids struct-layout migration issues and keeps stale path quality // stats from persisting indefinitely. memset(_recent_repeaters, 0, sizeof(_recent_repeaters)); - _next_recent_repeater_idx = 0; } void saveTo(File f) { f.write(_hashes, sizeof(_hashes)); @@ -198,15 +194,7 @@ public: uint32_t getNumDirectDups() const { return _direct_dups; } uint32_t getNumFloodDups() const { return _flood_dups; } - void setRecentRepeaterMinSNRX4(int8_t min_snr_x4) { - _recent_repeater_min_snr_x4 = min_snr_x4; - } - void setRecentRepeaterAllowFilter(RecentRepeaterAllowFn fn, void* ctx) { - _recent_repeater_allow_fn = fn; - _recent_repeater_allow_ctx = ctx; - } - bool setRecentRepeater(const uint8_t* prefix, uint8_t prefix_len, int8_t snr_x4, bool snr_locked = false, - bool bypass_allow_filter = false) { + bool setRecentRepeater(const uint8_t* prefix, uint8_t prefix_len, int8_t snr_x4) { if (prefix == NULL || prefix_len == 0) { return false; } @@ -215,11 +203,6 @@ public: prefix_len = MAX_ROUTE_HASH_BYTES; } - if (!bypass_allow_filter && _recent_repeater_allow_fn != NULL - && !_recent_repeater_allow_fn(prefix, prefix_len, _recent_repeater_allow_ctx)) { - return false; - } - // Keep exact prefixes distinct so a 1-byte path prefix does not collapse // independent 2/3-byte repeaters that share the same first byte. for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { @@ -227,26 +210,14 @@ public: if (existing.prefix_len != prefix_len || memcmp(existing.prefix, prefix, prefix_len) != 0) { continue; } - if (snr_locked) { - existing.snr_x4 = snr_x4; - existing.snr_locked = 1; - } else if (!existing.snr_locked) { - existing.snr_x4 = weightedSnrX4RoundUp(existing.snr_x4, snr_x4); - } -#if ARDUINO - existing.last_heard_millis = millis(); -#else - existing.last_heard_millis = 0; -#endif + existing.snr_x4 = weightedSnrX4RoundUp(existing.snr_x4, snr_x4); return true; } int slot_idx = -1; - // Prefer empty slots first while preserving newest-order iteration. for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { - int idx = (_next_recent_repeater_idx + i) % MAX_RECENT_REPEATERS; - if (_recent_repeaters[idx].prefix_len == 0) { - slot_idx = idx; + if (_recent_repeaters[i].prefix_len == 0) { + slot_idx = i; break; } } @@ -267,13 +238,6 @@ public: memcpy(slot.prefix, prefix, prefix_len); slot.prefix_len = prefix_len; slot.snr_x4 = snr_x4; - slot.snr_locked = snr_locked ? 1 : 0; -#if ARDUINO - slot.last_heard_millis = millis(); -#else - slot.last_heard_millis = 0; -#endif - _next_recent_repeater_idx = (slot_idx + 1) % MAX_RECENT_REPEATERS; return true; } bool decrementRecentRepeaterSnrX4(const uint8_t* prefix, uint8_t prefix_len, uint8_t amount_x4 = 1) { @@ -289,27 +253,15 @@ public: if (existing.prefix_len != prefix_len || memcmp(existing.prefix, prefix, prefix_len) != 0) { continue; } - if (!existing.snr_locked) { - int16_t lowered = (int16_t)existing.snr_x4 - (int16_t)amount_x4; - if (lowered < -128) { - lowered = -128; - } - existing.snr_x4 = (int8_t)lowered; + int16_t lowered = (int16_t)existing.snr_x4 - (int16_t)amount_x4; + if (lowered < -128) { + lowered = -128; } + existing.snr_x4 = (int8_t)lowered; return true; } return false; } - const RecentRepeaterInfo* getLatestRecentRepeater() const { - for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { - int idx = (_next_recent_repeater_idx - 1 - i + MAX_RECENT_REPEATERS) % MAX_RECENT_REPEATERS; - const RecentRepeaterInfo* info = &_recent_repeaters[idx]; - if (info->prefix_len > 0) { - return info; - } - } - return NULL; - } int getRecentRepeaterCount() const { int count = 0; for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { @@ -319,41 +271,36 @@ public: } return count; } - const RecentRepeaterInfo* getRecentRepeaterNewestByIdx(int idx_wanted) const { + const RecentRepeaterInfo* getRecentRepeaterBySortedIdx(int idx_wanted) const { if (idx_wanted < 0) { return NULL; } - int idx_seen = 0; - for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { - int idx = (_next_recent_repeater_idx - 1 - i + MAX_RECENT_REPEATERS) % MAX_RECENT_REPEATERS; - const RecentRepeaterInfo* info = &_recent_repeaters[idx]; - if (info->prefix_len == 0) { - continue; + + const RecentRepeaterInfo* last = NULL; + int last_idx = -1; + for (int rank = 0; rank <= idx_wanted; rank++) { + const RecentRepeaterInfo* best = NULL; + int best_idx = -1; + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + const RecentRepeaterInfo* info = &_recent_repeaters[i]; + if (info->prefix_len == 0) { + continue; + } + if (last != NULL && !recentRepeaterComesBefore(*last, last_idx, *info, i)) { + continue; + } + if (best == NULL || recentRepeaterComesBefore(*info, i, *best, best_idx)) { + best = info; + best_idx = i; + } } - if (idx_seen == idx_wanted) { - return info; + if (best == NULL) { + return NULL; } - idx_seen++; + last = best; + last_idx = best_idx; } - return NULL; - } - const RecentRepeaterInfo* getRecentRepeaterOldestByIdx(int idx_wanted) const { - if (idx_wanted < 0) { - return NULL; - } - int idx_seen = 0; - for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { - int idx = (_next_recent_repeater_idx + i) % MAX_RECENT_REPEATERS; - const RecentRepeaterInfo* info = &_recent_repeaters[idx]; - if (info->prefix_len == 0) { - continue; - } - if (idx_seen == idx_wanted) { - return info; - } - idx_seen++; - } - return NULL; + return last; } const RecentRepeaterInfo* findRecentRepeaterByHash(const uint8_t* hash, uint8_t hash_len) const { @@ -361,12 +308,11 @@ public: return NULL; } - // Prefer exact matches. If none exists, fall back to the newest longest - // overlapping prefix so coarse learned prefixes can still inform CR. + // Prefer exact matches. If none exists, fall back to the longest overlapping + // prefix, using highest SNR to break ties. const RecentRepeaterInfo* best = NULL; for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { - int idx = (_next_recent_repeater_idx - 1 - i + MAX_RECENT_REPEATERS) % MAX_RECENT_REPEATERS; - const RecentRepeaterInfo* info = &_recent_repeaters[idx]; + const RecentRepeaterInfo* info = &_recent_repeaters[i]; if (info->prefix_len == 0) { continue; } @@ -374,7 +320,8 @@ public: return info; } if (prefixesOverlap(info->prefix, info->prefix_len, hash, hash_len)) { - if (best == NULL || info->prefix_len > best->prefix_len) { + if (best == NULL || info->prefix_len > best->prefix_len + || (info->prefix_len == best->prefix_len && info->snr_x4 > best->snr_x4)) { best = info; } } @@ -383,7 +330,6 @@ public: } void clearRecentRepeaters() { memset(_recent_repeaters, 0, sizeof(_recent_repeaters)); - _next_recent_repeater_idx = 0; } void resetStats() { _direct_dups = _flood_dups = 0; } From 6b8135aa36154e6d544dbd820b437c2700d08759 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Wed, 17 Jun 2026 16:44:58 -0700 Subject: [PATCH 141/214] Fix direct retry review nits --- docs/cli_commands.md | 2 +- examples/simple_repeater/MyMesh.cpp | 2 +- src/Dispatcher.cpp | 6 +++++- src/Packet.cpp | 1 - 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 6657097e..cba67c7e 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -953,7 +953,7 @@ region save --- ### Direct Retry -Direct retry resends direct-routed packets when the downstream echo is not heard. It applies to direct messages and TRACE packets. It does not change ACK handling. +Direct retry resends direct-routed packets when the downstream echo is not heard. It applies to direct messages, ACK packets, multipart packets carrying ACK payloads, and TRACE packets. #### View or change direct retry state **Usage:** diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 4765e7af..2afaae2e 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -574,8 +574,8 @@ uint8_t MyMesh::getDirectRetryCodingRateForSNR(int8_t snr_x4) const { if (!_prefs.direct_retry_cr_enabled) return 0; if (snr_x4 >= _prefs.direct_retry_cr4_snr_x4) return 4; if (snr_x4 >= _prefs.direct_retry_cr5_snr_x4) return 5; - if (snr_x4 >= _prefs.direct_retry_cr7_snr_x4) return 7; if (snr_x4 <= _prefs.direct_retry_cr8_snr_x4) return 8; + if (snr_x4 >= _prefs.direct_retry_cr7_snr_x4) return 7; return 7; } diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index 63112f85..aee81a04 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -348,19 +348,23 @@ void Dispatcher::checkSend() { outbound_restore_cr = 0; outbound_restore_preamble_len = 0; uint8_t default_cr = getDefaultTxCodingRate(); + bool using_low_retry_cr = false; if (outbound->tx_cr >= 4 && outbound->tx_cr <= 8 && default_cr >= 4 && default_cr <= 8 && outbound->tx_cr != default_cr) { if (_radio->setCodingRate(outbound->tx_cr)) { outbound_restore_cr = default_cr; + using_low_retry_cr = outbound->tx_cr == 4 || outbound->tx_cr == 5; max_airtime = _radio->getEstAirtimeFor(len)*3/2; } else { MESH_DEBUG_PRINTLN("%s Dispatcher::checkSend(): WARN: failed to set packet CR%d", getLogDateTime(), (uint32_t)outbound->tx_cr); } + } else if (outbound->tx_cr == 4 || outbound->tx_cr == 5) { + using_low_retry_cr = outbound->tx_cr == default_cr; } bool has_direct_path = outbound->getPathHashCount() > 0 || (outbound->getPayloadType() == PAYLOAD_TYPE_TRACE && outbound->payload_len > 9); if (outbound->isRouteDirect() && has_direct_path - && (outbound->tx_cr == 4 || outbound->tx_cr == 5)) { + && using_low_retry_cr) { uint16_t default_preamble_len = _radio->getDefaultPreambleLength(); if (default_preamble_len > 16 && _radio->setPreambleLength(16)) { outbound_restore_preamble_len = default_preamble_len; diff --git a/src/Packet.cpp b/src/Packet.cpp index a542a5a2..3aab6349 100644 --- a/src/Packet.cpp +++ b/src/Packet.cpp @@ -87,4 +87,3 @@ bool Packet::readFrom(const uint8_t src[], uint8_t len) { } } - From 22e45d559db7f09d2040623ba68259952d8540de Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Sat, 20 Jun 2026 12:17:40 +0700 Subject: [PATCH 142/214] Power off board including radio, display, GPS and components --- examples/companion_radio/ui-new/UITask.cpp | 3 +- examples/companion_radio/ui-orig/UITask.cpp | 2 +- examples/companion_radio/ui-tiny/UITask.cpp | 3 +- src/helpers/ESP32Board.cpp | 42 +++++++++++++++++++ src/helpers/ESP32Board.h | 26 +----------- src/helpers/NRF52Board.cpp | 32 ++++++++++++++ src/helpers/NRF52Board.h | 1 + variants/heltec_t096/T096Board.cpp | 10 ++--- variants/heltec_t1/T1Board.cpp | 30 +------------ variants/heltec_t114/T114Board.h | 7 +--- variants/lilygo_techo_card/TechoCardBoard.cpp | 2 +- variants/sensecap_solar/SenseCapSolarBoard.h | 2 +- 12 files changed, 88 insertions(+), 72 deletions(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 7c842019..403ea463 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -697,8 +697,7 @@ void UITask::shutdown(bool restart){ if (restart) { _board->reboot(); } else { - _display->turnOff(); - radio_driver.powerOff(); + // Power off board including radio, display, GPS and components _board->powerOff(); } } diff --git a/examples/companion_radio/ui-orig/UITask.cpp b/examples/companion_radio/ui-orig/UITask.cpp index 55290467..b48f6412 100644 --- a/examples/companion_radio/ui-orig/UITask.cpp +++ b/examples/companion_radio/ui-orig/UITask.cpp @@ -307,7 +307,7 @@ void UITask::shutdown(bool restart){ if (restart) { _board->reboot(); } else { - radio_driver.powerOff(); + // Power off board including radio, display, GPS and components _board->powerOff(); } } diff --git a/examples/companion_radio/ui-tiny/UITask.cpp b/examples/companion_radio/ui-tiny/UITask.cpp index 0119475e..452c02d4 100644 --- a/examples/companion_radio/ui-tiny/UITask.cpp +++ b/examples/companion_radio/ui-tiny/UITask.cpp @@ -566,8 +566,7 @@ void UITask::shutdown(bool restart){ if (restart) { _board->reboot(); } else { - _display->turnOff(); - radio_driver.powerOff(); + // Power off board including radio, display, GPS and components _board->powerOff(); } } diff --git a/src/helpers/ESP32Board.cpp b/src/helpers/ESP32Board.cpp index e0ca1d0e..7da2c7ac 100644 --- a/src/helpers/ESP32Board.cpp +++ b/src/helpers/ESP32Board.cpp @@ -1,6 +1,7 @@ #ifdef ESP_PLATFORM #include "ESP32Board.h" +#include #if defined(ADMIN_PASSWORD) && !defined(DISABLE_WIFI_OTA) // Repeater or Room Server only #include @@ -44,4 +45,45 @@ bool ESP32Board::startOTAUpdate(const char* id, char reply[]) { } #endif +void ESP32Board::powerOff() { + enterDeepSleep(0); // Do not wakeup +} + +void ESP32Board::enterDeepSleep(uint32_t secs) { + // Power off the display if any +#ifdef DISPLAY_CLASS + display.turnOff(); +#endif + + // Power off LoRa + radio_driver.powerOff(); + + // Keep LoRa inactive during deepsleep + digitalWrite(P_LORA_NSS, HIGH); +#if CONFIG_IDF_TARGET_ESP32C3 + gpio_hold_en((gpio_num_t)P_LORA_NSS); +#else + rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); +#endif + + // Power off GPS if any + if (sensors.getLocationProvider() != NULL) { + sensors.getLocationProvider()->stop(); + } + + // Flush serial buffers + Serial.flush(); + delay(100); + + // Clear stale wakeup sources to avoid ghost wakeup + // This is required when Power Management and automatic lightsleep are enabled + esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); + + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000ULL); + } + + // Finally set ESP32 into deepsleep + esp_deep_sleep_start(); // CPU halts here and never returns! +} #endif diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index 058c800a..1f02b27a 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -63,30 +63,8 @@ public: return raw / 4; } - void powerOff() override { - enterDeepSleep(0); // Do not wakeup - } - - void enterDeepSleep(uint32_t secs) { - // Clear stale wakeup sources to avoid ghost wakeup - // This is required when Power Management and automatic lightsleep are enabled - esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000ULL); - } - - // Keep LoRa inactive during deepsleep - digitalWrite(P_LORA_NSS, HIGH); -#if CONFIG_IDF_TARGET_ESP32C3 - gpio_hold_en((gpio_num_t)P_LORA_NSS); -#else - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); -#endif - - // Finally set ESP32 into deepsleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } + virtual void powerOff() override; + void enterDeepSleep(uint32_t secs); uint32_t getIRQGpio() override { return P_LORA_DIO_1; // default for SX1262 diff --git a/src/helpers/NRF52Board.cpp b/src/helpers/NRF52Board.cpp index 17265f04..beee3212 100644 --- a/src/helpers/NRF52Board.cpp +++ b/src/helpers/NRF52Board.cpp @@ -1,5 +1,6 @@ #if defined(NRF52_PLATFORM) #include "NRF52Board.h" +#include #include #include @@ -297,6 +298,37 @@ float NRF52Board::getMCUTemperature() { return temp * 0.25f; // Convert to *C } +void NRF52Board::powerOff() { + // Power off the display if any +#ifdef DISPLAY_CLASS + display.turnOff(); +#endif + + // Power off LoRa + radio_driver.powerOff(); + + // Keep LoRa inactive during deepsleep + digitalWrite(P_LORA_NSS, HIGH); + + // Power off GPS if any + if(sensors.getLocationProvider() != NULL) { + sensors.getLocationProvider()->stop(); + } + + // Flush serial buffers + Serial.flush(); + delay(100); + + // Enter SYSTEMOFF + uint8_t sd_enabled = 0; + sd_softdevice_is_enabled(&sd_enabled); + if (sd_enabled) { // SoftDevice is enabled + sd_power_system_off(); + } else { // SoftDevice is not enable + NRF_POWER->SYSTEMOFF = POWER_SYSTEMOFF_SYSTEMOFF_Enter; + } +} + bool NRF52Board::getBootloaderVersion(char* out, size_t max_len) { static const char BOOTLOADER_MARKER[] = "UF2 Bootloader "; const uint8_t* flash = (const uint8_t*)0x000FB000; // earliest known info.txt location is 0xFB90B, latest is 0xFCC4B diff --git a/src/helpers/NRF52Board.h b/src/helpers/NRF52Board.h index 17065cf4..cbf4cd49 100644 --- a/src/helpers/NRF52Board.h +++ b/src/helpers/NRF52Board.h @@ -50,6 +50,7 @@ public: virtual uint8_t getStartupReason() const override { return startup_reason; } virtual float getMCUTemperature() override; virtual void reboot() override { NVIC_SystemReset(); } + virtual void powerOff() override; virtual bool getBootloaderVersion(char* version, size_t max_len) override; virtual bool startOTAUpdate(const char *id, char reply[]) override; virtual void sleep(uint32_t secs) override; diff --git a/variants/heltec_t096/T096Board.cpp b/variants/heltec_t096/T096Board.cpp index c7f69722..af048e38 100644 --- a/variants/heltec_t096/T096Board.cpp +++ b/variants/heltec_t096/T096Board.cpp @@ -112,13 +112,9 @@ void T096Board::variant_shutdown() { } void T096Board::powerOff() { -#if ENV_INCLUDE_GPS == 1 - pinMode(PIN_GPS_EN, OUTPUT); - digitalWrite(PIN_GPS_EN, !PIN_GPS_EN_ACTIVE); -#endif - loRaFEMControl.setSleepModeEnable(); - variant_shutdown(); - sd_power_system_off(); + loRaFEMControl.setSleepModeEnable(); + nrf_gpio_cfg_default(PIN_GPS_EN); // 363uA down to 39uA + NRF52Board::powerOff(); } const char* T096Board::getManufacturerName() const { diff --git a/variants/heltec_t1/T1Board.cpp b/variants/heltec_t1/T1Board.cpp index 490f8678..1ef5b766 100644 --- a/variants/heltec_t1/T1Board.cpp +++ b/variants/heltec_t1/T1Board.cpp @@ -81,34 +81,6 @@ uint16_t T1Board::getBattMilliVolts() { } void T1Board::variant_shutdown() { - nrf_gpio_cfg_default(PIN_TFT_CS); - nrf_gpio_cfg_default(PIN_TFT_DC); - nrf_gpio_cfg_default(PIN_TFT_SDA); - nrf_gpio_cfg_default(PIN_TFT_SCL); - nrf_gpio_cfg_default(PIN_TFT_RST); - nrf_gpio_cfg_default(PIN_TFT_LEDA_CTL); - nrf_gpio_cfg_default(PIN_TFT_VDD_CTL); - - nrf_gpio_cfg_default(PIN_WIRE_SDA); - nrf_gpio_cfg_default(PIN_WIRE_SCL); - - nrf_gpio_cfg_default(LORA_CS); - nrf_gpio_cfg_default(SX126X_DIO1); - nrf_gpio_cfg_default(SX126X_BUSY); - nrf_gpio_cfg_default(SX126X_RESET); - nrf_gpio_cfg_default(PIN_SPI_MISO); - nrf_gpio_cfg_default(PIN_SPI_MOSI); - nrf_gpio_cfg_default(PIN_SPI_SCK); - - nrf_gpio_cfg_default(PIN_SPI1_MOSI); - nrf_gpio_cfg_default(PIN_SPI1_SCK); - - nrf_gpio_cfg_default(PIN_GPS_RESET); - nrf_gpio_cfg_default(PIN_GPS_EN); - nrf_gpio_cfg_default(PIN_GPS_PPS); - nrf_gpio_cfg_default(PIN_GPS_RX); - nrf_gpio_cfg_default(PIN_GPS_TX); - nrf_gpio_cfg_default(PIN_BUZZER_VOLTAGE_MULTIPLIER_1); nrf_gpio_cfg_default(PIN_BUZZER_VOLTAGE_MULTIPLIER_2); @@ -127,7 +99,7 @@ void T1Board::variant_shutdown() { void T1Board::powerOff() { variant_shutdown(); - sd_power_system_off(); + NRF52Board::powerOff(); } const char* T1Board::getManufacturerName() const { diff --git a/variants/heltec_t114/T114Board.h b/variants/heltec_t114/T114Board.h index f27dc291..bd76d3de 100644 --- a/variants/heltec_t114/T114Board.h +++ b/variants/heltec_t114/T114Board.h @@ -50,10 +50,7 @@ public: #ifdef LED_PIN digitalWrite(LED_PIN, HIGH); #endif -#if ENV_INCLUDE_GPS == 1 - pinMode(GPS_EN, OUTPUT); - digitalWrite(GPS_EN, LOW); -#endif - sd_power_system_off(); + + NRF52Board::powerOff(); } }; diff --git a/variants/lilygo_techo_card/TechoCardBoard.cpp b/variants/lilygo_techo_card/TechoCardBoard.cpp index f0dcef31..8143587d 100644 --- a/variants/lilygo_techo_card/TechoCardBoard.cpp +++ b/variants/lilygo_techo_card/TechoCardBoard.cpp @@ -91,7 +91,7 @@ void TechoCardBoard::powerOff() { nrf_gpio_cfg_sense_input(BUTTON_PIN, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW); turnOffLeds(); digitalWrite(PIN_PWR_EN, LOW); - sd_power_system_off(); + NRF52Board::powerOff(); } #endif diff --git a/variants/sensecap_solar/SenseCapSolarBoard.h b/variants/sensecap_solar/SenseCapSolarBoard.h index 6799a5e9..5a8861c2 100644 --- a/variants/sensecap_solar/SenseCapSolarBoard.h +++ b/variants/sensecap_solar/SenseCapSolarBoard.h @@ -54,7 +54,7 @@ public: #ifdef NRF52_POWER_MANAGEMENT initiateShutdown(SHUTDOWN_REASON_USER); #else - sd_power_system_off(); + NRF52Board::powerOff(); #endif } }; From ddf83877031280c41103655937a76b82e25360ea Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Sun, 21 Jun 2026 21:13:25 +0700 Subject: [PATCH 143/214] Fixed missing rtc_clock for Heltec T114's GPS --- variants/heltec_t114/target.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/heltec_t114/target.cpp b/variants/heltec_t114/target.cpp index cb8f75db..90a902f8 100644 --- a/variants/heltec_t114/target.cpp +++ b/variants/heltec_t114/target.cpp @@ -22,7 +22,7 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock); #if ENV_INCLUDE_GPS #include -MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); +MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); #else EnvironmentSensorManager sensors; From f5d9e185173d03a9b72dce4a59c7051c8ad86c06 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Sun, 21 Jun 2026 23:14:03 +0700 Subject: [PATCH 144/214] Fixed poweroff for NRF52 boards --- variants/gat562_30s_mesh_kit/GAT56230SMeshKitBoard.h | 2 +- variants/gat562_mesh_evb_pro/GAT562EVBProBoard.h | 2 +- variants/gat562_mesh_tracker_pro/GAT562MeshTrackerProBoard.h | 2 +- variants/gat562_mesh_watch13/GAT56MeshWatch13Board.h | 2 +- variants/keepteen_lt1/KeepteenLT1Board.h | 4 ---- variants/lilygo_t_impulse_plus/TImpulsePlusBoard.h | 2 +- variants/lilygo_techo/TechoBoard.h | 2 +- variants/lilygo_techo_lite/TechoBoard.h | 2 +- variants/mesh_pocket/MeshPocket.h | 4 ---- variants/meshtiny/MeshtinyBoard.h | 2 +- variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h | 2 +- variants/nano_g2_ultra/nano-g2.h | 2 +- variants/promicro/PromicroBoard.h | 4 ---- variants/rak_wismesh_tag/RAKWismeshTagBoard.h | 2 +- variants/t1000-e/T1000eBoard.h | 2 +- variants/thinknode_m1/ThinkNodeM1Board.h | 3 +-- variants/thinknode_m3/ThinkNodeM3Board.h | 2 +- variants/thinknode_m6/ThinkNodeM6Board.h | 2 +- variants/wio-tracker-l1/WioTrackerL1Board.h | 2 +- variants/xiao_nrf52/XiaoNrf52Board.h | 2 +- 20 files changed, 17 insertions(+), 30 deletions(-) diff --git a/variants/gat562_30s_mesh_kit/GAT56230SMeshKitBoard.h b/variants/gat562_30s_mesh_kit/GAT56230SMeshKitBoard.h index ab7ecc24..0b0f701c 100644 --- a/variants/gat562_30s_mesh_kit/GAT56230SMeshKitBoard.h +++ b/variants/gat562_30s_mesh_kit/GAT56230SMeshKitBoard.h @@ -47,7 +47,7 @@ public: uint32_t button_pin = PIN_BUTTON1; nrf_gpio_cfg_input(button_pin, NRF_GPIO_PIN_PULLUP); nrf_gpio_cfg_sense_set(button_pin, NRF_GPIO_PIN_SENSE_LOW); - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/gat562_mesh_evb_pro/GAT562EVBProBoard.h b/variants/gat562_mesh_evb_pro/GAT562EVBProBoard.h index 33c3d05f..66c77099 100644 --- a/variants/gat562_mesh_evb_pro/GAT562EVBProBoard.h +++ b/variants/gat562_mesh_evb_pro/GAT562EVBProBoard.h @@ -47,7 +47,7 @@ public: uint32_t button_pin = PIN_BUTTON1; nrf_gpio_cfg_input(button_pin, NRF_GPIO_PIN_PULLUP); nrf_gpio_cfg_sense_set(button_pin, NRF_GPIO_PIN_SENSE_LOW); - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/gat562_mesh_tracker_pro/GAT562MeshTrackerProBoard.h b/variants/gat562_mesh_tracker_pro/GAT562MeshTrackerProBoard.h index aa1772e4..39b55911 100644 --- a/variants/gat562_mesh_tracker_pro/GAT562MeshTrackerProBoard.h +++ b/variants/gat562_mesh_tracker_pro/GAT562MeshTrackerProBoard.h @@ -47,7 +47,7 @@ public: uint32_t button_pin = PIN_BUTTON1; nrf_gpio_cfg_input(button_pin, NRF_GPIO_PIN_PULLUP); nrf_gpio_cfg_sense_set(button_pin, NRF_GPIO_PIN_SENSE_LOW); - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/gat562_mesh_watch13/GAT56MeshWatch13Board.h b/variants/gat562_mesh_watch13/GAT56MeshWatch13Board.h index da792b78..805ca67f 100644 --- a/variants/gat562_mesh_watch13/GAT56MeshWatch13Board.h +++ b/variants/gat562_mesh_watch13/GAT56MeshWatch13Board.h @@ -38,7 +38,7 @@ public: uint32_t button_pin = PIN_BUTTON1; nrf_gpio_cfg_input(button_pin, NRF_GPIO_PIN_PULLUP); nrf_gpio_cfg_sense_set(button_pin, NRF_GPIO_PIN_SENSE_LOW); - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/keepteen_lt1/KeepteenLT1Board.h b/variants/keepteen_lt1/KeepteenLT1Board.h index 752b27e7..25d4d867 100644 --- a/variants/keepteen_lt1/KeepteenLT1Board.h +++ b/variants/keepteen_lt1/KeepteenLT1Board.h @@ -37,8 +37,4 @@ public: digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off } #endif - - void powerOff() override { - sd_power_system_off(); - } }; diff --git a/variants/lilygo_t_impulse_plus/TImpulsePlusBoard.h b/variants/lilygo_t_impulse_plus/TImpulsePlusBoard.h index ea1782cf..6cee1255 100644 --- a/variants/lilygo_t_impulse_plus/TImpulsePlusBoard.h +++ b/variants/lilygo_t_impulse_plus/TImpulsePlusBoard.h @@ -53,7 +53,7 @@ public: digitalWrite(RT9080_EN, LOW); // power off system - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/lilygo_techo/TechoBoard.h b/variants/lilygo_techo/TechoBoard.h index e560cd14..d8cde1fe 100644 --- a/variants/lilygo_techo/TechoBoard.h +++ b/variants/lilygo_techo/TechoBoard.h @@ -39,6 +39,6 @@ public: #ifdef PIN_PWR_EN digitalWrite(PIN_PWR_EN, LOW); #endif - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/lilygo_techo_lite/TechoBoard.h b/variants/lilygo_techo_lite/TechoBoard.h index 7a43fd83..225ed831 100644 --- a/variants/lilygo_techo_lite/TechoBoard.h +++ b/variants/lilygo_techo_lite/TechoBoard.h @@ -38,6 +38,6 @@ public: #ifdef PIN_PWR_EN digitalWrite(PIN_PWR_EN, LOW); #endif - sd_power_system_off(); + NRF52Board::powerOff(); } }; \ No newline at end of file diff --git a/variants/mesh_pocket/MeshPocket.h b/variants/mesh_pocket/MeshPocket.h index 478bd56d..e215eb77 100644 --- a/variants/mesh_pocket/MeshPocket.h +++ b/variants/mesh_pocket/MeshPocket.h @@ -32,8 +32,4 @@ public: const char* getManufacturerName() const override { return "Heltec MeshPocket"; } - - void powerOff() override { - sd_power_system_off(); - } }; diff --git a/variants/meshtiny/MeshtinyBoard.h b/variants/meshtiny/MeshtinyBoard.h index b69c0e41..67a521fe 100644 --- a/variants/meshtiny/MeshtinyBoard.h +++ b/variants/meshtiny/MeshtinyBoard.h @@ -60,7 +60,7 @@ public: nrf_gpio_cfg_sense_input(g_ADigitalPinMap[PIN_USER_BTN], NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW); #endif - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h b/variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h index 4fa5cd41..450a3d13 100644 --- a/variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h +++ b/variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h @@ -65,7 +65,7 @@ public: #ifdef BUTTON_PIN nrf_gpio_cfg_sense_input(digitalPinToInterrupt(BUTTON_PIN), NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW); #endif - sd_power_system_off(); + NRF52Board::powerOff(); } #if defined(P_LORA_TX_LED) diff --git a/variants/nano_g2_ultra/nano-g2.h b/variants/nano_g2_ultra/nano-g2.h index cf771efe..bf9543b0 100644 --- a/variants/nano_g2_ultra/nano-g2.h +++ b/variants/nano_g2_ultra/nano-g2.h @@ -52,6 +52,6 @@ public: nrf_gpio_cfg_sense_input(digitalPinToInterrupt(PIN_USER_BTN), NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_LOW); - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/promicro/PromicroBoard.h b/variants/promicro/PromicroBoard.h index 7b6afb1b..b190c47c 100644 --- a/variants/promicro/PromicroBoard.h +++ b/variants/promicro/PromicroBoard.h @@ -72,8 +72,4 @@ public: #endif return 0; } - - void powerOff() override { - sd_power_system_off(); - } }; diff --git a/variants/rak_wismesh_tag/RAKWismeshTagBoard.h b/variants/rak_wismesh_tag/RAKWismeshTagBoard.h index cc5aa06f..daa90d74 100644 --- a/variants/rak_wismesh_tag/RAKWismeshTagBoard.h +++ b/variants/rak_wismesh_tag/RAKWismeshTagBoard.h @@ -69,6 +69,6 @@ public: nrf_gpio_cfg_sense_input(digitalPinToInterrupt(BUTTON_PIN), NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW); #endif - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/t1000-e/T1000eBoard.h b/variants/t1000-e/T1000eBoard.h index e7653fb2..1111d3c4 100644 --- a/variants/t1000-e/T1000eBoard.h +++ b/variants/t1000-e/T1000eBoard.h @@ -88,6 +88,6 @@ public: nrf_gpio_cfg_sense_input(BUTTON_PIN, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_HIGH); #endif - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/thinknode_m1/ThinkNodeM1Board.h b/variants/thinknode_m1/ThinkNodeM1Board.h index ebc46e6e..94224d9c 100644 --- a/variants/thinknode_m1/ThinkNodeM1Board.h +++ b/variants/thinknode_m1/ThinkNodeM1Board.h @@ -40,7 +40,6 @@ public: #endif // power off board - sd_power_system_off(); - + NRF52Board::powerOff(); } }; diff --git a/variants/thinknode_m3/ThinkNodeM3Board.h b/variants/thinknode_m3/ThinkNodeM3Board.h index 1435d31d..396d80d1 100644 --- a/variants/thinknode_m3/ThinkNodeM3Board.h +++ b/variants/thinknode_m3/ThinkNodeM3Board.h @@ -49,6 +49,6 @@ public: #endif // power off board - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/thinknode_m6/ThinkNodeM6Board.h b/variants/thinknode_m6/ThinkNodeM6Board.h index 32baa2a0..78815e2c 100644 --- a/variants/thinknode_m6/ThinkNodeM6Board.h +++ b/variants/thinknode_m6/ThinkNodeM6Board.h @@ -44,6 +44,6 @@ public: #endif // power off board - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/wio-tracker-l1/WioTrackerL1Board.h b/variants/wio-tracker-l1/WioTrackerL1Board.h index 052238e6..e376e066 100644 --- a/variants/wio-tracker-l1/WioTrackerL1Board.h +++ b/variants/wio-tracker-l1/WioTrackerL1Board.h @@ -35,6 +35,6 @@ public: } void powerOff() override { - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/xiao_nrf52/XiaoNrf52Board.h b/variants/xiao_nrf52/XiaoNrf52Board.h index 2790dbad..b2638a44 100644 --- a/variants/xiao_nrf52/XiaoNrf52Board.h +++ b/variants/xiao_nrf52/XiaoNrf52Board.h @@ -50,7 +50,7 @@ public: nrf_gpio_cfg_sense_input(digitalPinToInterrupt(g_ADigitalPinMap[PIN_USER_BTN]), NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_LOW); #endif - sd_power_system_off(); + NRF52Board::powerOff(); } }; From 62849ef1142369f76c0975d709a23288b5a9b861 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Mon, 22 Jun 2026 09:13:36 +0700 Subject: [PATCH 145/214] Fixed GPS time sync for Heltec T114 --- variants/heltec_t114/target.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/heltec_t114/target.cpp b/variants/heltec_t114/target.cpp index cb8f75db..90a902f8 100644 --- a/variants/heltec_t114/target.cpp +++ b/variants/heltec_t114/target.cpp @@ -22,7 +22,7 @@ AutoDiscoverRTCClock rtc_clock(fallback_clock); #if ENV_INCLUDE_GPS #include -MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); +MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); #else EnvironmentSensorManager sensors; From b38e6e04d461430243b5db7a1b2e09164d21d886 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 22 Jun 2026 14:13:08 -0700 Subject: [PATCH 146/214] build.sh: refactor menu flow --- build.sh | 1425 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 1242 insertions(+), 183 deletions(-) diff --git a/build.sh b/build.sh index 313c4c47..8a6044c7 100755 --- a/build.sh +++ b/build.sh @@ -1,9 +1,50 @@ #!/usr/bin/env bash +ALL_PIO_ENVS=() +SUPPORTED_PIO_ENVS=() +declare -A PIO_ENV_PLATFORM_BY_NAME=() +PIO_CONFIG_JSON="" +MENU_CHOICE="" +SELECTED_TARGET="" +SELECTED_COMMAND_ARGS=() +MESHDEBUG_OVERRIDE="" +PACKET_LOGGING_OVERRIDE="" +RADIO_SETTINGS_API_URL="https://api.meshcore.nz/api/v1/config" +RADIO_SETTING_TITLE="" +RADIO_FREQ_OVERRIDE="" +RADIO_BW_OVERRIDE="" +RADIO_SF_OVERRIDE="" +RADIO_CR_OVERRIDE="" +BATCH_BUILD_MODE=0 +RESOLVED_BUILD_TARGETS=() + +ENV_VARIANT_SUFFIX_PATTERN='companion_radio_serial|companion_radio_wifi|companion_radio_usb|comp_radio_usb|companion_usb|companion_radio_ble|companion_ble|repeater_bridge_rs232_serial1|repeater_bridge_rs232_serial2|repeater_bridge_rs232|repeater_bridge_espnow|terminal_chat|room_server|room_svr|kiss_modem|sensor|repeatr|repeater' +BOARD_MODIFIER_WITHOUT_DISPLAY="_without_display" +BOARD_MODIFIER_LOGGING="_logging" +BOARD_MODIFIER_TFT="_tft" +BOARD_MODIFIER_EINK="_eink" +BOARD_MODIFIER_EINK_SUFFIX="Eink" +BOARD_LABEL_WITHOUT_DISPLAY="without_display" +BOARD_LABEL_LOGGING="logging" +BOARD_LABEL_TFT="tft" +BOARD_LABEL_EINK="eink" +DEFAULT_VARIANT_LABEL="default" +TAG_PREFIX_ROOM_SERVER="room-server" +TAG_PREFIX_COMPANION="companion" +TAG_PREFIX_REPEATER="repeater" +SUPPORTED_PLATFORM_PATTERN='ESP32_PLATFORM|NRF52_PLATFORM|STM32_PLATFORM|RP2040_PLATFORM' +OUTPUT_DIR="out" +FALLBACK_VERSION_PREFIX="dev" +FALLBACK_VERSION_DATE_FORMAT='+%Y-%m-%d-%H-%M' + +# External programs invoked by this script: +# bash, cat, cp, date, git, grep, head, mkdir, pio, python3, rm, sed, sort +# Keep this list in sync when adding or removing non-builtin command usage. + global_usage() { cat - < [target] +bash build.sh [target] Commands: help|usage|-h|--help: Shows this message. @@ -17,21 +58,27 @@ Commands: Examples: Build firmware for the "RAK_4631_repeater" device target -$ sh build.sh build-firmware RAK_4631_repeater +$ bash build.sh build-firmware RAK_4631_repeater + +Run without arguments to choose an interactive build action/target, debug options, radio settings, and firmware version +$ bash build.sh Build all firmwares for device targets containing the string "RAK_4631" -$ sh build.sh build-matching-firmwares +$ bash build.sh build-matching-firmwares Build all companion firmwares -$ sh build.sh build-companion-firmwares +$ bash build.sh build-companion-firmwares Build all repeater firmwares -$ sh build.sh build-repeater-firmwares +$ bash build.sh build-repeater-firmwares Build all chat room server firmwares -$ sh build.sh build-room-server-firmwares +$ bash build.sh build-room-server-firmwares Environment Variables: + FIRMWARE_VERSION=vX.Y.Z: Firmware version to embed in the build output. + If not set, build.sh derives a default from the latest matching git tag and appends "-dev". + In interactive builds, this value is offered as the editable default. DISABLE_DEBUG=1: Disables all debug logging flags (MESH_DEBUG, MESH_PACKET_LOGGING, etc.) If not set, debug flags from variant platformio.ini files are used. @@ -39,61 +86,815 @@ Examples: Build without debug logging: $ export FIRMWARE_VERSION=v1.0.0 $ export DISABLE_DEBUG=1 -$ sh build.sh build-firmware RAK_4631_repeater +$ bash build.sh build-firmware RAK_4631_repeater Build with debug logging (default, uses flags from variant files): $ export FIRMWARE_VERSION=v1.0.0 -$ sh build.sh build-firmware RAK_4631_repeater +$ bash build.sh build-firmware RAK_4631_repeater + +Build with the derived default version from git tags: +$ unset FIRMWARE_VERSION +$ bash build.sh EOF } -# get a list of pio env names that start with "env:" -get_pio_envs() { - pio project config | grep 'env:' | sed 's/env://' +init_project_context() { + if [ ${#ALL_PIO_ENVS[@]} -eq 0 ]; then + mapfile -t ALL_PIO_ENVS < <(pio project config | grep 'env:' | sed 's/env://') + fi + + if [ -z "$PIO_CONFIG_JSON" ]; then + PIO_CONFIG_JSON=$(pio project config --json-output) + fi + + if [ ${#SUPPORTED_PIO_ENVS[@]} -eq 0 ]; then + while IFS=$'\t' read -r env_name env_platform; do + if [ -z "$env_name" ] || [ -z "$env_platform" ]; then + continue + fi + SUPPORTED_PIO_ENVS+=("$env_name") + PIO_ENV_PLATFORM_BY_NAME["$env_name"]=$env_platform + done < <( + python3 -c ' +import json +import re +import sys + +pattern = re.compile(sys.argv[1]) +data = json.load(sys.stdin) +for section, options in data: + if not section.startswith("env:"): + continue + env_name = section[4:] + for key, value in options: + if key != "build_flags": + continue + values = value if isinstance(value, list) else str(value).split() + for flag in values: + match = pattern.search(str(flag)) + if match: + print(f"{env_name}\t{match.group(0)}") + break + else: + continue + break +' "$SUPPORTED_PLATFORM_PATTERN" <<<"$PIO_CONFIG_JSON" + ) + fi } -# Catch cries for help before doing anything else. -case $1 in - help|usage|-h|--help) - global_usage - exit 1 - ;; - list|-l) - get_pio_envs - exit 0 - ;; -esac +get_pio_envs() { + get_supported_pio_envs +} -# cache project config json for use in get_platform_for_env() -PIO_CONFIG_JSON=$(pio project config --json-output) +canonicalize_variant_suffix() { + local variant_suffix=$1 -# $1 should be the string to find (case insensitive) -get_pio_envs_containing_string() { - shopt -s nocasematch - envs=($(get_pio_envs)) - for env in "${envs[@]}"; do - if [[ "$env" == *${1}* ]]; then - echo $env - fi + case "${variant_suffix,,}" in + comp_radio_usb|companion_usb|companion_radio_usb) + echo "companion_radio_usb" + ;; + companion_ble|companion_radio_ble) + echo "companion_radio_ble" + ;; + room_svr|room_server) + echo "room_server" + ;; + repeatr|repeater) + echo "repeater" + ;; + *) + echo "${variant_suffix,,}" + ;; + esac +} + +trim_trailing_underscores() { + local value=$1 + + while [[ "$value" == *_ ]]; do + value=${value%_} + done + + echo "$value" +} + +sort_lines_case_insensitive() { + sort -f +} + +print_numbered_menu() { + local items=("$@") + local i + + for i in "${!items[@]}"; do + printf '%d) %s\n' "$((i + 1))" "${items[$i]}" done } -# $1 should be the string to find (case insensitive) -get_pio_envs_ending_with_string() { - shopt -s nocasematch - envs=($(get_pio_envs)) - for env in "${envs[@]}"; do - if [[ "$env" == *${1} ]]; then - echo $env +prompt_menu_choice() { + local prompt_label=$1 + local max_choice=$2 + local allow_back=${3:-0} + local choice + + while true; do + if [ "$allow_back" -eq 1 ]; then + read -r -p "${prompt_label} [1-${max_choice}, B=Back, Q=Quit]: " choice + else + read -r -p "${prompt_label} [1-${max_choice}, Q=Quit]: " choice + fi + + case "${choice^^}" in + Q) + MENU_CHOICE="QUIT" + return 0 + ;; + B) + if [ "$allow_back" -eq 1 ]; then + MENU_CHOICE="BACK" + return 0 + fi + echo "Invalid selection." + ;; + *) + if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le "$max_choice" ]; then + MENU_CHOICE="$choice" + return 0 + fi + echo "Invalid selection." + ;; + esac + done +} + +prompt_on_off_choice() { + local prompt_label=$1 + local default_choice=$2 + local choice + + while true; do + read -r -p "${prompt_label} [on/off] (default: ${default_choice}): " choice + choice=${choice,,} + if [ -z "$choice" ]; then + choice=$default_choice + fi + + case "$choice" in + on|off) + MENU_CHOICE="$choice" + return 0 + ;; + *) + echo "Invalid selection. Choose 'on' or 'off'." + ;; + esac + done +} + +prompt_for_build_mode() { + local options=( + "Build one firmware target" + "Build all firmwares" + "Build all repeater firmwares" + "Build all companion firmwares" + "Build all chat room server firmwares" + ) + + echo "No command provided. Select a build action:" + while true; do + print_numbered_menu "${options[@]}" + prompt_menu_choice "Build action" "${#options[@]}" + if [ "$MENU_CHOICE" == "QUIT" ]; then + echo "Cancelled." + exit 1 + fi + + case "$MENU_CHOICE" in + 1) + prompt_for_board_target + SELECTED_COMMAND_ARGS=(build-firmware "$SELECTED_TARGET") + return 0 + ;; + 2) + SELECTED_COMMAND_ARGS=(build-firmwares) + return 0 + ;; + 3) + SELECTED_COMMAND_ARGS=(build-repeater-firmwares) + return 0 + ;; + 4) + SELECTED_COMMAND_ARGS=(build-companion-firmwares) + return 0 + ;; + 5) + SELECTED_COMMAND_ARGS=(build-room-server-firmwares) + return 0 + ;; + esac + done +} + +prompt_for_debug_build_settings() { + echo "Set debug build options:" + prompt_on_off_choice "Mesh debug (MESH_DEBUG)" "off" + MESHDEBUG_OVERRIDE="$MENU_CHOICE" + + prompt_on_off_choice "Packet logging (MESH_PACKET_LOGGING)" "off" + PACKET_LOGGING_OVERRIDE="$MENU_CHOICE" + + echo "Using debug options: meshdebug=${MESHDEBUG_OVERRIDE}, packet_logging=${PACKET_LOGGING_OVERRIDE}" +} + +clear_radio_overrides() { + RADIO_SETTING_TITLE="" + RADIO_FREQ_OVERRIDE="" + RADIO_BW_OVERRIDE="" + RADIO_SF_OVERRIDE="" + RADIO_CR_OVERRIDE="" +} + +set_radio_overrides() { + RADIO_SETTING_TITLE=$1 + RADIO_FREQ_OVERRIDE=$2 + RADIO_BW_OVERRIDE=$3 + RADIO_SF_OVERRIDE=$4 + RADIO_CR_OVERRIDE=$5 +} + +fetch_suggested_radio_settings() { + python3 - "$RADIO_SETTINGS_API_URL" <<'PY' +import json +import sys +import urllib.request + +url = sys.argv[1] +request = urllib.request.Request( + url, + headers={ + "Accept": "application/json", + "User-Agent": "MeshCore-build.sh/1.0 (+https://github.com/meshcore-dev/MeshCore)", + }, +) + +try: + with urllib.request.urlopen(request, timeout=8) as response: + payload = json.load(response) +except Exception as exc: + print(f"radio preset fetch failed: {exc}", file=sys.stderr) + raise SystemExit(1) + +entries = ( + payload.get("config", {}) + .get("suggested_radio_settings", {}) + .get("entries", []) +) + +for entry in entries: + title = str(entry.get("title", "")).strip() + description = str(entry.get("description", "")).strip() + freq = str(entry.get("frequency", "")).strip() + sf = str(entry.get("spreading_factor", "")).strip() + bw = str(entry.get("bandwidth", "")).strip() + cr = str(entry.get("coding_rate", "")).strip() + if title and freq and sf and bw and cr: + print("\t".join([title, description, freq, bw, sf, cr])) +PY +} + +is_valid_custom_radio_bandwidth() { + python3 - "$1" <<'PY' +import sys + +allowed = [7.81, 10.42, 15.63, 20.83, 31.25, 41.67, 62.5, 125.0, 250.0, 500.0] +try: + value = float(sys.argv[1]) +except Exception: + raise SystemExit(1) + +raise SystemExit(0 if any(abs(value - option) < 1e-6 for option in allowed) else 1) +PY +} + +prompt_for_custom_radio_setting() { + local freq + local sf + local bw + local cr + + echo + echo "Custom radio settings:" + + while true; do + read -r -p "Center frequency (MHz, e.g. 915.000): " freq + if [[ "$freq" =~ ^[0-9]+([.][0-9]+)?$ ]]; then + break + fi + echo "Please enter a numeric MHz value (e.g. 915.000)." + done + + echo "Spreading factor options: 5, 6, 7, 8, 9, 10, 11, 12" + while true; do + read -r -p "SF (5-12): " sf + if [[ "$sf" =~ ^[0-9]+$ ]] && [ "$sf" -ge 5 ] && [ "$sf" -le 12 ]; then + break + fi + echo "Please enter 5, 6, 7, 8, 9, 10, 11, or 12." + done + + echo "Bandwidth options (kHz): 7.81 10.42 15.63 20.83 31.25 41.67 62.5 125 250 500" + while true; do + read -r -p "BW (kHz): " bw + if [[ "$bw" =~ ^[0-9]+([.][0-9]+)?$ ]] && is_valid_custom_radio_bandwidth "$bw"; then + break + fi + echo "Please enter one of: 7.81 10.42 15.63 20.83 31.25 41.67 62.5 125 250 500." + done + + echo "Coding rate options: CR5, CR6, CR7, CR8" + while true; do + read -r -p "CR (5-8): " cr + if [[ "$cr" =~ ^[0-9]+$ ]] && [ "$cr" -ge 5 ] && [ "$cr" -le 8 ]; then + break + fi + echo "Please enter 5, 6, 7, or 8." + done + + set_radio_overrides "Custom" "$freq" "$bw" "$sf" "$cr" +} + +prompt_for_radio_build_settings() { + local -a preset_rows=() + local -a options=("Keep target defaults (no radio override)") + local row + local title + local description + local freq + local bw + local sf + local cr + local preset_index + local choice_index + local custom_index + local preset_output + + clear_radio_overrides + + if preset_output=$(fetch_suggested_radio_settings); then + if [ -n "$preset_output" ]; then + mapfile -t preset_rows <<< "$preset_output" + fi + for row in "${preset_rows[@]}"; do + if [ -z "$row" ]; then + continue + fi + IFS=$'\t' read -r title description freq bw sf cr <<< "$row" + options+=("${title}: ${description}") + done + else + echo "Could not fetch radio presets from ${RADIO_SETTINGS_API_URL}." + preset_rows=() + fi + + options+=("Custom") + custom_index=${#options[@]} + + echo "Set radio build options:" + while true; do + print_numbered_menu "${options[@]}" + prompt_menu_choice "Radio setting" "${#options[@]}" + if [ "$MENU_CHOICE" == "QUIT" ]; then + echo "Cancelled." + exit 1 + fi + + choice_index=$MENU_CHOICE + if [ "$choice_index" -eq 1 ]; then + echo "Using target default radio settings." + return 0 + fi + + if [ "$choice_index" -eq "$custom_index" ]; then + prompt_for_custom_radio_setting + echo "Using radio setting: ${RADIO_SETTING_TITLE} (${RADIO_FREQ_OVERRIDE}MHz / SF${RADIO_SF_OVERRIDE} / BW${RADIO_BW_OVERRIDE} / CR${RADIO_CR_OVERRIDE})" + return 0 + fi + + preset_index=$((choice_index - 2)) + if [ "$preset_index" -ge 0 ] && [ "$preset_index" -lt "${#preset_rows[@]}" ]; then + IFS=$'\t' read -r title description freq bw sf cr <<< "${preset_rows[$preset_index]}" + set_radio_overrides "$title" "$freq" "$bw" "$sf" "$cr" + echo "Using radio setting: ${RADIO_SETTING_TITLE} (${RADIO_FREQ_OVERRIDE}MHz / SF${RADIO_SF_OVERRIDE} / BW${RADIO_BW_OVERRIDE} / CR${RADIO_CR_OVERRIDE})" + return 0 fi done } -# get platform flag for a given environment -# $1 should be the environment name +get_env_metadata() { + local env_name=$1 + local trimmed_env_name + local board_part + local variant_part + local board_family + local board_modifier + local variant_label + local tag_prefix + + trimmed_env_name=$(trim_trailing_underscores "$env_name") + board_part=$trimmed_env_name + variant_part="" + + shopt -s nocasematch + # Split a raw env name into board and variant pieces using the normalized + # suffix vocabulary defined near the top of the file. + if [[ "$trimmed_env_name" =~ ^(.+)[_-](${ENV_VARIANT_SUFFIX_PATTERN})$ ]]; then + board_part=${BASH_REMATCH[1]} + variant_part=$(canonicalize_variant_suffix "${BASH_REMATCH[2]}") + fi + + # Fold display and form-factor suffixes into the variant label so related + # boards share one first-level menu entry. + case "$board_part" in + *"$BOARD_MODIFIER_WITHOUT_DISPLAY") + board_family=${board_part%"$BOARD_MODIFIER_WITHOUT_DISPLAY"} + board_modifier="$BOARD_LABEL_WITHOUT_DISPLAY" + ;; + *"$BOARD_MODIFIER_LOGGING") + board_family=${board_part%"$BOARD_MODIFIER_LOGGING"} + board_modifier="$BOARD_LABEL_LOGGING" + ;; + *"$BOARD_MODIFIER_TFT") + board_family=${board_part%"$BOARD_MODIFIER_TFT"} + board_modifier="$BOARD_LABEL_TFT" + ;; + *"$BOARD_MODIFIER_EINK") + board_family=${board_part%"$BOARD_MODIFIER_EINK"} + board_modifier="$BOARD_LABEL_EINK" + ;; + *"$BOARD_MODIFIER_EINK_SUFFIX") + board_family=${board_part%"$BOARD_MODIFIER_EINK_SUFFIX"} + board_modifier="$BOARD_LABEL_EINK" + ;; + *) + board_family=$board_part + board_modifier="" + ;; + esac + shopt -u nocasematch + + variant_label="$variant_part" + if [ -n "$board_modifier" ]; then + if [ -n "$variant_label" ]; then + variant_label="${board_modifier}_${variant_label}" + else + variant_label="$board_modifier" + fi + fi + + if [ -z "$variant_label" ]; then + variant_label="$DEFAULT_VARIANT_LABEL" + fi + + case "$variant_part" in + room_server) + tag_prefix="$TAG_PREFIX_ROOM_SERVER" + ;; + companion_radio_*) + tag_prefix="$TAG_PREFIX_COMPANION" + ;; + repeater*) + tag_prefix="$TAG_PREFIX_REPEATER" + ;; + *) + tag_prefix="" + ;; + esac + + printf '%s\t%s\t%s\n' "$board_family" "$variant_label" "$tag_prefix" +} + +get_metadata_field() { + local env_name=$1 + local field_index=$2 + local metadata + + metadata=$(get_env_metadata "$env_name") + case "$field_index" in + 1) + echo "${metadata%%$'\t'*}" + ;; + 2) + metadata=${metadata#*$'\t'} + echo "${metadata%%$'\t'*}" + ;; + 3) + echo "${metadata##*$'\t'}" + ;; + esac +} + +get_board_family_for_env() { + get_metadata_field "$1" 1 +} + +get_variant_name_for_env() { + get_metadata_field "$1" 2 +} + +get_release_tag_prefix_for_env() { + get_metadata_field "$1" 3 +} + +get_variants_for_board() { + local board_family=$1 + local env + + for env in "${ALL_PIO_ENVS[@]}"; do + if ! is_supported_build_env "$env"; then + continue + fi + + if [ "$(get_board_family_for_env "$env")" == "$board_family" ]; then + echo "$env" + fi + done | sort_lines_case_insensitive +} + +prompt_for_variant_for_board() { + local board=$1 + local -A seen_variant_labels=() + local variants + local variant_labels + local i + local j + + mapfile -t variants < <(get_variants_for_board "$board") + if [ ${#variants[@]} -eq 0 ]; then + echo "No firmware variants were found for ${board}." + return 1 + fi + + if [ ${#variants[@]} -eq 1 ]; then + SELECTED_TARGET="${variants[0]}" + return 0 + fi + + variant_labels=() + for i in "${!variants[@]}"; do + variant_labels[i]=$(get_variant_name_for_env "${variants[$i]}") + seen_variant_labels["${variant_labels[$i]}"]=$(( ${seen_variant_labels["${variant_labels[$i]}"]:-0} + 1 )) + done + + # Stop early if normalization would present the user with ambiguous labels. + for i in "${!variant_labels[@]}"; do + if [ "${seen_variant_labels["${variant_labels[$i]}"]}" -gt 1 ]; then + echo "Ambiguous firmware variants detected for ${board}: ${variant_labels[$i]}" + echo "The normalized menu labels are not unique for this board family." + for j in "${!variants[@]}"; do + echo " ${variants[$j]}" + done + exit 1 + fi + done + + echo "Select a firmware variant for ${board}:" + while true; do + print_numbered_menu "${variant_labels[@]}" + prompt_menu_choice "Variant selection" "${#variant_labels[@]}" 1 + if [ "$MENU_CHOICE" == "BACK" ]; then + return 1 + fi + if [ "$MENU_CHOICE" == "QUIT" ]; then + echo "Cancelled." + exit 1 + fi + + SELECTED_TARGET="${variants[$((MENU_CHOICE - 1))]}" + return 0 + done +} + +prompt_for_board_target() { + local -A seen_boards=() + local boards=() + local board + local env + + if ! [ -t 0 ]; then + echo "No command provided and no interactive terminal is available." + global_usage + exit 1 + fi + + if [ ${#ALL_PIO_ENVS[@]} -eq 0 ]; then + echo "No PlatformIO environments were found." + exit 1 + fi + + for env in "${ALL_PIO_ENVS[@]}"; do + if ! is_supported_build_env "$env"; then + continue + fi + + board=$(get_board_family_for_env "$env") + if [ -z "${seen_boards[$board]}" ]; then + seen_boards["$board"]=1 + boards+=("$board") + fi + done + + mapfile -t boards < <(printf '%s\n' "${boards[@]}" | sort_lines_case_insensitive) + + echo "Select a board family:" + while true; do + print_numbered_menu "${boards[@]}" + prompt_menu_choice "Board selection" "${#boards[@]}" + if [ "$MENU_CHOICE" == "QUIT" ]; then + echo "Cancelled." + exit 1 + fi + + board=${boards[$((MENU_CHOICE - 1))]} + if prompt_for_variant_for_board "$board"; then + echo "Building firmware for ${SELECTED_TARGET}" + return 0 + fi + done +} + +get_latest_version_from_tags() { + local env_name=$1 + local tag_prefix + local latest_tag + local fallback_version + + fallback_version="${FALLBACK_VERSION_PREFIX}-$(date "${FALLBACK_VERSION_DATE_FORMAT}")" + tag_prefix=$(get_release_tag_prefix_for_env "$env_name") + if [ -z "$tag_prefix" ]; then + echo "$fallback_version" + return 0 + fi + + latest_tag=$(git tag --list "${tag_prefix}-v*" --sort=-version:refname | head -n 1) + if [ -z "$latest_tag" ]; then + echo "$fallback_version" + return 0 + fi + + echo "${latest_tag#"${tag_prefix}"-}" +} + +derive_default_firmware_version() { + local env_name=$1 + local base_version + + base_version=$(get_latest_version_from_tags "$env_name") + case "$base_version" in + *-dev|dev-*) + echo "$base_version" + ;; + *) + echo "${base_version}-dev" + ;; + esac +} + +derive_default_firmware_version_for_targets() { + local target + local tag_prefix + local candidate_version + local fallback_version + local -a candidate_versions=() + local -a sorted_versions=() + local -A seen_tag_prefixes=() + + fallback_version="${FALLBACK_VERSION_PREFIX}-$(date "${FALLBACK_VERSION_DATE_FORMAT}")" + + for target in "$@"; do + tag_prefix=$(get_release_tag_prefix_for_env "$target") + if [ -n "$tag_prefix" ]; then + if [ -n "${seen_tag_prefixes[$tag_prefix]+x}" ]; then + continue + fi + seen_tag_prefixes["$tag_prefix"]=1 + fi + + candidate_version=$(derive_default_firmware_version "$target") + candidate_versions+=("$candidate_version") + done + + if [ ${#candidate_versions[@]} -eq 0 ]; then + echo "$fallback_version" + return 0 + fi + + mapfile -t sorted_versions < <(printf '%s\n' "${candidate_versions[@]}" | sort -u -V) + echo "${sorted_versions[$((${#sorted_versions[@]} - 1))]}" +} + +prompt_for_firmware_version() { + local prompt_label=$1 + local result_var=$2 + local suggested_version=${3:-} + local entered_version + + if [ -z "$suggested_version" ]; then + suggested_version=$(derive_default_firmware_version "$prompt_label") + fi + + if ! [ -t 0 ]; then + printf -v "$result_var" '%s' "$suggested_version" + return 0 + fi + + echo "Suggested firmware version for ${prompt_label}: ${suggested_version}" + read -r -e -i "${suggested_version}" -p "Firmware version: " entered_version + printf -v "$result_var" '%s' "${entered_version:-$suggested_version}" +} + +prompt_for_resolved_firmware_version() { + local prompt_label + local selected_version=${FIRMWARE_VERSION:-} + + if [ ${#RESOLVED_BUILD_TARGETS[@]} -eq 0 ]; then + return 0 + fi + + if ! [ -t 0 ]; then + return 0 + fi + + if [ -z "$selected_version" ]; then + selected_version=$(derive_default_firmware_version_for_targets "${RESOLVED_BUILD_TARGETS[@]}") + fi + + if [ ${#RESOLVED_BUILD_TARGETS[@]} -eq 1 ]; then + prompt_label="${RESOLVED_BUILD_TARGETS[0]}" + else + prompt_label="${#RESOLVED_BUILD_TARGETS[@]} build targets" + fi + + prompt_for_firmware_version "$prompt_label" selected_version "$selected_version" + FIRMWARE_VERSION=$selected_version + export FIRMWARE_VERSION +} + +get_pio_envs_containing_string() { + local env + + shopt -s nocasematch + for env in "${ALL_PIO_ENVS[@]}"; do + if [[ "$env" == *${1}* ]] && is_supported_build_env "$env"; then + echo "$env" + fi + done + shopt -u nocasematch +} + +get_supported_pio_envs() { + if [ ${#SUPPORTED_PIO_ENVS[@]} -gt 0 ]; then + printf '%s\n' "${SUPPORTED_PIO_ENVS[@]}" + fi +} + +get_pio_envs_for_variant_role() { + local role=$1 + local env + local variant_name + + for env in "${ALL_PIO_ENVS[@]}"; do + if ! is_supported_build_env "$env"; then + continue + fi + + variant_name=$(get_variant_name_for_env "$env") + case "$role:$variant_name" in + companion:companion_radio_*) + echo "$env" + ;; + repeater:repeater*) + echo "$env" + ;; + room_server:room_server) + echo "$env" + ;; + esac + done +} + get_platform_for_env() { local env_name=$1 - echo "$PIO_CONFIG_JSON" | python3 -c " + + if [ -n "${PIO_ENV_PLATFORM_BY_NAME[$env_name]+x}" ]; then + echo "${PIO_ENV_PLATFORM_BY_NAME[$env_name]}" + return 0 + fi + + # PlatformIO exposes project config as JSON; scan the selected env's + # build_flags to recover the platform token used for artifact collection. + # Feed the cached JSON via stdin to avoid shell echo quirks and argv/env size limits. + python3 -c " import sys, json, re data = json.load(sys.stdin) for section, options in data: @@ -101,178 +902,436 @@ for section, options in data: for key, value in options: if key == 'build_flags': for flag in value: - match = re.search(r'(ESP32_PLATFORM|NRF52_PLATFORM|STM32_PLATFORM|RP2040_PLATFORM)', flag) + match = re.search(r'($SUPPORTED_PLATFORM_PATTERN)', flag) if match: print(match.group(1)) sys.exit(0) -" +" <<<"$PIO_CONFIG_JSON" +} + +is_supported_platform() { + local env_platform=$1 + + [[ "$env_platform" =~ ^(${SUPPORTED_PLATFORM_PATTERN})$ ]] +} + +is_known_pio_env() { + local env_name=$1 + local env + + for env in "${ALL_PIO_ENVS[@]}"; do + if [ "$env" == "$env_name" ]; then + return 0 + fi + done + + return 1 +} + +is_supported_build_env() { + local env_name=$1 + + [ -n "${PIO_ENV_PLATFORM_BY_NAME[$env_name]+x}" ] } -# disable all debug logging flags if DISABLE_DEBUG=1 is set disable_debug_flags() { if [ "$DISABLE_DEBUG" == "1" ]; then export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -UMESH_DEBUG -UBLE_DEBUG_LOGGING -UWIFI_DEBUG_LOGGING -UBRIDGE_DEBUG -UGPS_NMEA_DEBUG -UCORE_DEBUG_LEVEL -UESPNOW_DEBUG_LOGGING -UDEBUG_RP2040_WIRE -UDEBUG_RP2040_SPI -UDEBUG_RP2040_CORE -UDEBUG_RP2040_PORT -URADIOLIB_DEBUG_SPI -UCFG_DEBUG -URADIOLIB_DEBUG_BASIC -URADIOLIB_DEBUG_PROTOCOL" fi } -# build firmware for the provided pio env in $1 +apply_debug_overrides() { + case "${MESHDEBUG_OVERRIDE,,}" in + on) + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DMESH_DEBUG=1" + ;; + off) + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -UMESH_DEBUG" + ;; + esac + + case "${PACKET_LOGGING_OVERRIDE,,}" in + on) + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DMESH_PACKET_LOGGING=1" + ;; + off) + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -UMESH_PACKET_LOGGING" + ;; + esac +} + +apply_radio_overrides() { + if [ -n "$RADIO_FREQ_OVERRIDE" ] && [ -n "$RADIO_BW_OVERRIDE" ] && [ -n "$RADIO_SF_OVERRIDE" ] && [ -n "$RADIO_CR_OVERRIDE" ]; then + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DLORA_FREQ=${RADIO_FREQ_OVERRIDE} -DLORA_BW=${RADIO_BW_OVERRIDE} -DLORA_SF=${RADIO_SF_OVERRIDE} -DLORA_CR=${RADIO_CR_OVERRIDE}" + fi +} + +copy_build_output() { + local source_path=$1 + local output_path=$2 + + if ! [ -f "$source_path" ]; then + echo "Expected build output missing: $source_path" + return 1 + fi + + cp -- "$source_path" "$output_path" +} + +collect_esp32_artifacts() { + local env_name=$1 + local firmware_filename=$2 + + pio run -t mergebin -e "$env_name" || return $? + copy_build_output ".pio/build/${env_name}/firmware.bin" "out/${firmware_filename}.bin" || return $? + copy_build_output ".pio/build/${env_name}/firmware-merged.bin" "out/${firmware_filename}-merged.bin" || return $? +} + +collect_nrf52_artifacts() { + local env_name=$1 + local firmware_filename=$2 + + python3 bin/uf2conv/uf2conv.py ".pio/build/${env_name}/firmware.hex" -c -o ".pio/build/${env_name}/firmware.uf2" -f 0xADA52840 || return $? + copy_build_output ".pio/build/${env_name}/firmware.uf2" "out/${firmware_filename}.uf2" || return $? + copy_build_output ".pio/build/${env_name}/firmware.zip" "out/${firmware_filename}.zip" || return $? +} + +collect_stm32_artifacts() { + local env_name=$1 + local firmware_filename=$2 + + copy_build_output ".pio/build/${env_name}/firmware.bin" "out/${firmware_filename}.bin" || return $? + copy_build_output ".pio/build/${env_name}/firmware.hex" "out/${firmware_filename}.hex" || return $? +} + +collect_rp2040_artifacts() { + local env_name=$1 + local firmware_filename=$2 + + copy_build_output ".pio/build/${env_name}/firmware.bin" "out/${firmware_filename}.bin" || return $? + copy_build_output ".pio/build/${env_name}/firmware.uf2" "out/${firmware_filename}.uf2" || return $? +} + +collect_build_artifacts() { + local env_name=$1 + local env_platform=$2 + local firmware_filename=$3 + + # Post-build outputs differ by platform, so dispatch to the matching + # collector after the main firmware build succeeds. + case "$env_platform" in + ESP32_PLATFORM) + collect_esp32_artifacts "$env_name" "$firmware_filename" + ;; + NRF52_PLATFORM) + collect_nrf52_artifacts "$env_name" "$firmware_filename" + ;; + STM32_PLATFORM) + collect_stm32_artifacts "$env_name" "$firmware_filename" + ;; + RP2040_PLATFORM) + collect_rp2040_artifacts "$env_name" "$firmware_filename" + ;; + *) + echo "Unsupported or unknown platform for env: $env_name" + return 1 + ;; + esac +} + +restore_platformio_build_flags() { + local had_platformio_build_flags=$1 + local original_platformio_build_flags=${2:-} + + if [ "$had_platformio_build_flags" -eq 1 ]; then + export PLATFORMIO_BUILD_FLAGS="$original_platformio_build_flags" + else + unset PLATFORMIO_BUILD_FLAGS + fi +} + build_firmware() { - # get env platform for post build actions - ENV_PLATFORM=($(get_platform_for_env $1)) + local env_name=$1 + local env_platform + local commit_hash + local firmware_build_date + local firmware_version + local firmware_version_string + local firmware_filename + local original_platformio_build_flags + local had_platformio_build_flags=0 + local build_status - # get git commit sha - COMMIT_HASH=$(git rev-parse --short HEAD) - - # set firmware build date - FIRMWARE_BUILD_DATE=$(date '+%d-%b-%Y') - - # get FIRMWARE_VERSION, which should be provided by the environment - if [ -z "$FIRMWARE_VERSION" ]; then - echo "FIRMWARE_VERSION must be set in environment" - exit 1 + env_platform=$(get_platform_for_env "$env_name") + if ! is_supported_platform "$env_platform"; then + echo "Unsupported or unknown platform for env: $env_name" + return 1 fi - # set firmware version string - # e.g: v1.0.0-abcdef - FIRMWARE_VERSION_STRING="${FIRMWARE_VERSION}-${COMMIT_HASH}" + commit_hash=$(git rev-parse --short HEAD) + firmware_build_date=$(date '+%d-%b-%Y') + firmware_version=${FIRMWARE_VERSION:-} - # craft filename - # e.g: RAK_4631_Repeater-v1.0.0-SHA - FIRMWARE_FILENAME="$1-${FIRMWARE_VERSION_STRING}" + if [ -z "$firmware_version" ]; then + if [ "$BATCH_BUILD_MODE" -eq 1 ]; then + firmware_version=$(derive_default_firmware_version "$env_name") + else + prompt_for_firmware_version "$env_name" firmware_version + fi + echo "FIRMWARE_VERSION not set, using derived default for ${env_name}: ${firmware_version}" + fi - # add firmware version info to end of existing platformio build flags in environment vars - export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DFIRMWARE_BUILD_DATE='\"${FIRMWARE_BUILD_DATE}\"' -DFIRMWARE_VERSION='\"${FIRMWARE_VERSION_STRING}\"'" + firmware_version_string="${firmware_version}-${commit_hash}" + firmware_filename="${env_name}-${firmware_version_string}" - # disable debug flags if requested + if [ "${PLATFORMIO_BUILD_FLAGS+x}" ]; then + had_platformio_build_flags=1 + original_platformio_build_flags=$PLATFORMIO_BUILD_FLAGS + else + original_platformio_build_flags="" + fi + + export PLATFORMIO_BUILD_FLAGS="${original_platformio_build_flags} -DFIRMWARE_BUILD_DATE='\"${firmware_build_date}\"' -DFIRMWARE_VERSION='\"${firmware_version_string}\"'" disable_debug_flags + apply_debug_overrides + apply_radio_overrides - # build firmware target - pio run -e $1 - - # build merge-bin for esp32 fresh install, copy .bins to out folder (e.g: Heltec_v3_room_server-v1.0.0-SHA.bin) - if [ "$ENV_PLATFORM" == "ESP32_PLATFORM" ]; then - pio run -t mergebin -e $1 - cp .pio/build/$1/firmware.bin out/${FIRMWARE_FILENAME}.bin 2>/dev/null || true - cp .pio/build/$1/firmware-merged.bin out/${FIRMWARE_FILENAME}-merged.bin 2>/dev/null || true + pio run -e "$env_name" + build_status=$? + if [ "$build_status" -eq 0 ]; then + collect_build_artifacts "$env_name" "$env_platform" "$firmware_filename" + build_status=$? fi - # build .uf2 for nrf52 boards, copy .uf2 and .zip to out folder (e.g: RAK_4631_Repeater-v1.0.0-SHA.uf2) - if [ "$ENV_PLATFORM" == "NRF52_PLATFORM" ]; then - python3 bin/uf2conv/uf2conv.py .pio/build/$1/firmware.hex -c -o .pio/build/$1/firmware.uf2 -f 0xADA52840 - cp .pio/build/$1/firmware.uf2 out/${FIRMWARE_FILENAME}.uf2 2>/dev/null || true - cp .pio/build/$1/firmware.zip out/${FIRMWARE_FILENAME}.zip 2>/dev/null || true + restore_platformio_build_flags "$had_platformio_build_flags" "$original_platformio_build_flags" + return "$build_status" +} + +resolve_matching_firmwares() { + local envs + + mapfile -t envs < <(get_pio_envs_containing_string "$1") + if [ ${#envs[@]} -gt 0 ]; then + printf '%s\n' "${envs[@]}" + fi +} + +resolve_all_firmwares() { + get_supported_pio_envs +} + +resolve_companion_firmwares() { + get_pio_envs_for_variant_role companion +} + +resolve_repeater_firmwares() { + get_pio_envs_for_variant_role repeater +} + +resolve_room_server_firmwares() { + get_pio_envs_for_variant_role room_server +} + +# Keep bulk build command names mapped to their target resolvers in one place. +get_bulk_build_resolver_name() { + case "$1" in + build-firmwares) + echo "resolve_all_firmwares" + ;; + build-companion-firmwares) + echo "resolve_companion_firmwares" + ;; + build-repeater-firmwares) + echo "resolve_repeater_firmwares" + ;; + build-room-server-firmwares) + echo "resolve_room_server_firmwares" + ;; + *) + return 1 + ;; + esac +} + +is_bulk_build_command() { + get_bulk_build_resolver_name "$1" >/dev/null +} + +is_build_command() { + case "$1" in + build-firmware|build-matching-firmwares) + return 0 + ;; + *) + is_bulk_build_command "$1" + ;; + esac +} + +resolve_bulk_command_targets() { + local resolver_name + + resolver_name=$(get_bulk_build_resolver_name "$1") || return $? + mapfile -t RESOLVED_BUILD_TARGETS < <("$resolver_name") +} + +validate_build_target() { + local env_name=$1 + local env_platform + + if ! is_known_pio_env "$env_name"; then + echo "Unknown build target: $env_name" + return 1 fi - # for stm32, copy .bin and .hex to out folder - if [ "$ENV_PLATFORM" == "STM32_PLATFORM" ]; then - cp .pio/build/$1/firmware.bin out/${FIRMWARE_FILENAME}.bin 2>/dev/null || true - cp .pio/build/$1/firmware.hex out/${FIRMWARE_FILENAME}.hex 2>/dev/null || true + env_platform=$(get_platform_for_env "$env_name") + if ! is_supported_platform "$env_platform"; then + echo "Unsupported build target: $env_name" + return 1 fi +} - # for rp2040, copy .bin and .uf2 to out folder - if [ "$ENV_PLATFORM" == "RP2040_PLATFORM" ]; then - cp .pio/build/$1/firmware.bin out/${FIRMWARE_FILENAME}.bin 2>/dev/null || true - cp .pio/build/$1/firmware.uf2 out/${FIRMWARE_FILENAME}.uf2 2>/dev/null || true +resolve_command_targets() { + local target + + RESOLVED_BUILD_TARGETS=() + case "$1" in + build-firmware) + for target in "${@:2}"; do + validate_build_target "$target" || return $? + RESOLVED_BUILD_TARGETS+=("$target") + done + ;; + build-matching-firmwares) + mapfile -t RESOLVED_BUILD_TARGETS < <(resolve_matching_firmwares "$2") + ;; + *) + # Bulk command target resolution is centralized so the build-family + # command list is not repeated in every command handling case. + resolve_bulk_command_targets "$1" || return $? + ;; + esac + + if [ ${#RESOLVED_BUILD_TARGETS[@]} -eq 0 ]; then + echo "No supported build targets matched: ${*:2}" + return 1 fi - } -# firmwares containing $1 will be built -build_all_firmwares_matching() { - envs=($(get_pio_envs_containing_string "$1")) - for env in "${envs[@]}"; do - build_firmware $env - done -} +prepare_output_dir() { + local output_dir="$OUTPUT_DIR" -# firmwares ending with $1 will be built -build_all_firmwares_by_suffix() { - envs=($(get_pio_envs_ending_with_string "$1")) - for env in "${envs[@]}"; do - build_firmware $env - done -} - -build_repeater_firmwares() { - -# # build specific repeater firmwares -# build_firmware "Heltec_v2_repeater" -# build_firmware "Heltec_v3_repeater" -# build_firmware "Xiao_C3_Repeater_sx1262" -# build_firmware "Xiao_S3_WIO_Repeater" -# build_firmware "LilyGo_T3S3_sx1262_Repeater" -# build_firmware "RAK_4631_Repeater" - - # build all repeater firmwares - build_all_firmwares_by_suffix "_repeater" - -} - -build_companion_firmwares() { - -# # build specific companion firmwares -# build_firmware "Heltec_v2_companion_radio_usb" -# build_firmware "Heltec_v2_companion_radio_ble" -# build_firmware "Heltec_v3_companion_radio_usb" -# build_firmware "Heltec_v3_companion_radio_ble" -# build_firmware "Xiao_S3_WIO_companion_radio_ble" -# build_firmware "LilyGo_T3S3_sx1262_companion_radio_usb" -# build_firmware "LilyGo_T3S3_sx1262_companion_radio_ble" -# build_firmware "RAK_4631_companion_radio_usb" -# build_firmware "RAK_4631_companion_radio_ble" -# build_firmware "t1000e_companion_radio_ble" - - # build all companion firmwares - build_all_firmwares_by_suffix "_companion_radio_usb" - build_all_firmwares_by_suffix "_companion_radio_ble" - -} - -build_room_server_firmwares() { - -# # build specific room server firmwares -# build_firmware "Heltec_v3_room_server" -# build_firmware "RAK_4631_room_server" - - # build all room server firmwares - build_all_firmwares_by_suffix "_room_server" - -} - -build_firmwares() { - build_companion_firmwares - build_repeater_firmwares - build_room_server_firmwares -} - -# clean build dir -rm -rf out -mkdir -p out - -# handle script args -if [[ $1 == "build-firmware" ]]; then - TARGETS=${@:2} - if [ "$TARGETS" ]; then - for env in $TARGETS; do - build_firmware $env - done - else - echo "usage: $0 build-firmware " + if [ -z "$output_dir" ] || [ "$output_dir" == "/" ] || [ "$output_dir" == "." ]; then + echo "Refusing to clean unsafe output directory: $output_dir" exit 1 fi -elif [[ $1 == "build-matching-firmwares" ]]; then - if [ "$2" ]; then - build_all_firmwares_matching $2 - else - echo "usage: $0 build-matching-firmwares " + + rm -rf -- "$output_dir" + mkdir -p -- "$output_dir" +} + +run_resolved_build_targets() { + local targets=("$@") + local env + local previous_batch_build_mode=$BATCH_BUILD_MODE + local build_status=0 + + if [ ${#targets[@]} -eq 0 ]; then + echo "No build targets resolved." + return 1 + fi + + if [ ${#targets[@]} -gt 1 ]; then + BATCH_BUILD_MODE=1 + fi + for env in "${targets[@]}"; do + build_firmware "$env" + build_status=$? + if [ "$build_status" -ne 0 ]; then + break + fi + done + BATCH_BUILD_MODE=$previous_batch_build_mode + + return "$build_status" +} + +validate_command() { + case "$1" in + build-firmware) + if [ "$#" -lt 2 ]; then + echo "usage: $0 build-firmware " + exit 1 + fi + ;; + build-matching-firmwares) + if [ -z "${2:-}" ]; then + echo "usage: $0 build-matching-firmwares " + exit 1 + fi + ;; + *) + # Bulk commands have no required positional arguments; the helper keeps + # that command set in sync with target resolution. + if ! is_bulk_build_command "$1"; then + global_usage + exit 1 + fi + ;; + esac +} + +run_command() { + # All build commands share execution after validation resolves their target list. + if is_build_command "$1"; then + run_resolved_build_targets "${RESOLVED_BUILD_TARGETS[@]}" + return $? + fi + + global_usage + exit 1 +} + +main() { + case "${1:-}" in + help|usage|-h|--help) + global_usage + exit 0 + ;; + list|-l) + init_project_context + get_pio_envs + exit 0 + ;; + esac + + if [ $# -gt 0 ]; then + validate_command "$@" + fi + + init_project_context + + if [ $# -eq 0 ]; then + if ! [ -t 0 ]; then + echo "No command provided and no interactive terminal is available." + global_usage + exit 1 + fi + + prompt_for_build_mode + prompt_for_debug_build_settings + prompt_for_radio_build_settings + set -- "${SELECTED_COMMAND_ARGS[@]}" + validate_command "$@" + fi + + if ! resolve_command_targets "$@"; then exit 1 fi -elif [[ $1 == "build-firmwares" ]]; then - build_firmwares -elif [[ $1 == "build-companion-firmwares" ]]; then - build_companion_firmwares -elif [[ $1 == "build-repeater-firmwares" ]]; then - build_repeater_firmwares -elif [[ $1 == "build-room-server-firmwares" ]]; then - build_room_server_firmwares -fi + + prompt_for_resolved_firmware_version + prepare_output_dir + run_command "$@" +} + +main "$@" From 4b6810d380184074624f62a89ed4d4936ee9dec8 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 22 Jun 2026 18:18:14 -0700 Subject: [PATCH 147/214] Restore uf2reset CLI after PR merges --- docs/cli_commands.md | 23 +++++++++++++++++++++++ src/helpers/CommonCLI.cpp | 32 +++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index ee4a4216..ba92a2b1 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -28,12 +28,35 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Usage:** - `reboot` +**Note:** No reply is sent. + +--- + +### Power-off the node +**Usage:** +- `poweroff`, or +- `shutdown` + +**Note:** No reply is sent. + +--- + +### Enter the UF2 bootloader (nRF52 only) +**Usage:** +- `uf2reset` + +**Serial Only:** Yes + +**Note:** Reboots directly into the UF2 bootloader on supported nRF52 boards. + --- ### Reset the clock and reboot **Usage:** - `clkreboot` +**Note:** No reply is sent. + --- ### Sync the clock with the remote device diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 6ee5ec93..3937dd50 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -4,6 +4,30 @@ #include "AdvertDataHelpers.h" #include "TxtDataHelpers.h" #include + +#if defined(NRF52_PLATFORM) +#include +#include + +#ifndef DFU_MAGIC_UF2_RESET +#define DFU_MAGIC_UF2_RESET 0x57 +#endif + +static void resetToUf2Bootloader() { + uint8_t sd_enabled = 0; + sd_softdevice_is_enabled(&sd_enabled); + + if (sd_enabled) { + sd_power_gpregret_clr(0, 0xFF); + sd_power_gpregret_set(0, DFU_MAGIC_UF2_RESET); + } else { + NRF_POWER->GPREGRET = DFU_MAGIC_UF2_RESET; + } + + NVIC_SystemReset(); +} +#endif + #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) @@ -414,11 +438,17 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re _board->powerOff(); // doesn't return } else if (memcmp(command, "reboot", 6) == 0) { _board->reboot(); // doesn't return + } else if (sender_timestamp == 0 && memcmp(command, "uf2reset", 8) == 0 && (command[8] == 0 || command[8] == ' ')) { +#if defined(NRF52_PLATFORM) + resetToUf2Bootloader(); // doesn't return +#else + strcpy(reply, "ERR: unsupported"); +#endif } else if (memcmp(command, "clkreboot", 9) == 0) { // Reset clock getRTCClock()->setCurrentTime(1715770351); // 15 May 2024, 8:50pm _board->reboot(); // doesn't return - } else if (memcmp(command, "advert.zerohop", 14) == 0 && (command[14] == 0 || command[14] == ' ')) { + } else if (memcmp(command, "advert.zerohop", 14) == 0 && (command[14] == 0 || command[14] == ' ')) { // send zerohop advert _callbacks->sendSelfAdvertisement(1500, false); // longer delay, give CLI response time to be sent first strcpy(reply, "OK - zerohop advert sent"); From 2fd968540407cf5db9e5a3aa33aae07683a541a4 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Tue, 23 Jun 2026 09:40:55 +0700 Subject: [PATCH 148/214] Added missing PERSISTENT_RAM in memory --- boards/nrf52840_s140_v7_extrafs.ld | 3 +++ 1 file changed, 3 insertions(+) diff --git a/boards/nrf52840_s140_v7_extrafs.ld b/boards/nrf52840_s140_v7_extrafs.ld index 48348188..ef6ebba1 100644 --- a/boards/nrf52840_s140_v7_extrafs.ld +++ b/boards/nrf52840_s140_v7_extrafs.ld @@ -7,6 +7,9 @@ MEMORY { FLASH (rx) : ORIGIN = 0x27000, LENGTH = 0xD4000 - 0x27000 + /* To keep data in RAM across resets */ + PERSISTENT_RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 8 + /* SRAM required by Softdevice depend on * - Attribute Table Size (Number of Services and Characteristics) * - Vendor UUID count From 1d1cba676c346cacbcc51821a60756b27ac07caf Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Wed, 24 Jun 2026 21:49:53 +0700 Subject: [PATCH 149/214] Skip agc reset if in STATE_TX_WAIT --- src/helpers/radiolib/RadioLibWrappers.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helpers/radiolib/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp index b6519aef..b01e9440 100644 --- a/src/helpers/radiolib/RadioLibWrappers.cpp +++ b/src/helpers/radiolib/RadioLibWrappers.cpp @@ -68,8 +68,8 @@ void RadioLibWrapper::doResetAGC() { } void RadioLibWrapper::resetAGC() { - // make sure we're not mid-receive of packet! - if ((state & STATE_INT_READY) != 0 || isReceivingPacket()) return; + // make sure we're not mid-receiving and mid-sending of packet! + if ((state & STATE_INT_READY) != 0 || isReceivingPacket() || (state == STATE_TX_WAIT)) return; doResetAGC(); state = STATE_IDLE; // trigger a startReceive() From 1976494d7bb9da4433da3b2595e485172e7384ec Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Wed, 24 Jun 2026 22:03:35 +0700 Subject: [PATCH 150/214] PR 1727: Added get/set cad. Added # channel busy. --- docs/cli_commands.md | 14 ++++++++++++ examples/companion_radio/MyMesh.cpp | 3 +++ examples/companion_radio/MyMesh.h | 1 + examples/simple_repeater/MyMesh.cpp | 1 + examples/simple_repeater/MyMesh.h | 3 +++ examples/simple_room_server/MyMesh.cpp | 1 + examples/simple_room_server/MyMesh.h | 3 +++ examples/simple_sensor/SensorMesh.cpp | 4 ++++ examples/simple_sensor/SensorMesh.h | 1 + src/Dispatcher.cpp | 1 + src/Dispatcher.h | 3 +++ src/MeshCore.h | 2 ++ src/helpers/CommonCLI.cpp | 14 +++++++++--- src/helpers/CommonCLI.h | 1 + src/helpers/radiolib/RadioLibWrappers.cpp | 26 ++++++++++++++++++++--- src/helpers/radiolib/RadioLibWrappers.h | 5 ++++- 16 files changed, 76 insertions(+), 7 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 4d3270b2..06d119a2 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -604,6 +604,20 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### Enable or disable hardware Channel Activity Detection (CAD) +**Usage:** +- `get cad` +- `set cad ` + +**Description:** When enabled, the radio performs a hardware Channel Activity Detection scan before transmitting and defers if the channel is busy. Runs independently of `int.thresh` — either, both, or none may be active. + +**Parameters:** +- `on|off`: Enable or disable hardware CAD + +**Default:** `off` + +--- + #### View or change the AGC Reset Interval **Usage:** - `get agc.reset.interval` diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 1dd5162b..10578511 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -263,6 +263,9 @@ float MyMesh::getAirtimeBudgetFactor() const { int MyMesh::getInterferenceThreshold() const { return 0; // disabled for now, until currentRSSI() problem is resolved } +bool MyMesh::getCADEnabled() const { + return true; // hardware CAD before TX (no CLI toggle on companion; enabled by default) +} int MyMesh::calcRxDelay(float score, uint32_t air_time) const { if (_prefs.rx_delay_base <= 0.0f) return 0; diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 43d3950b..f4190f30 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -105,6 +105,7 @@ public: protected: float getAirtimeBudgetFactor() const override; int getInterferenceThreshold() const override; + bool getCADEnabled() const override; int calcRxDelay(float score, uint32_t air_time) const override; uint32_t getRetransmitDelay(const mesh::Packet *packet) override; uint32_t getDirectRetransmitDelay(const mesh::Packet *packet) override; diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 1b0ca191..68cc3685 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -895,6 +895,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_max_unscoped = 64; _prefs.flood_max_advert = 8; _prefs.interference_threshold = 0; // disabled + _prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on') // bridge defaults _prefs.bridge_enabled = 1; // enabled diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index fbc756f4..c2d662a2 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -150,6 +150,9 @@ protected: int getInterferenceThreshold() const override { return _prefs.interference_threshold; } + bool getCADEnabled() const override { + return _prefs.cad_enabled; + } int getAGCResetInterval() const override { return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds } diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index bbea97f5..c311c941 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -650,6 +650,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_max_unscoped = 64; _prefs.flood_max_advert = 8; _prefs.interference_threshold = 0; // disabled + _prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on') #ifdef ROOM_PASSWORD StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password)); #endif diff --git a/examples/simple_room_server/MyMesh.h b/examples/simple_room_server/MyMesh.h index 24c26418..380e54da 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -144,6 +144,9 @@ protected: int getInterferenceThreshold() const override { return _prefs.interference_threshold; } + bool getCADEnabled() const override { + return _prefs.cad_enabled; + } int getAGCResetInterval() const override { return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds } diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index 879fcbf0..055c579d 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -323,6 +323,9 @@ uint32_t SensorMesh::getDirectRetransmitDelay(const mesh::Packet* packet) { int SensorMesh::getInterferenceThreshold() const { return _prefs.interference_threshold; } +bool SensorMesh::getCADEnabled() const { + return _prefs.cad_enabled; +} int SensorMesh::getAGCResetInterval() const { return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds } @@ -726,6 +729,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise _prefs.disable_fwd = true; _prefs.flood_max = 64; _prefs.interference_threshold = 0; // disabled + _prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on') // GPS defaults _prefs.gps_enabled = 0; diff --git a/examples/simple_sensor/SensorMesh.h b/examples/simple_sensor/SensorMesh.h index c9f135f6..1d65b877 100644 --- a/examples/simple_sensor/SensorMesh.h +++ b/examples/simple_sensor/SensorMesh.h @@ -120,6 +120,7 @@ protected: uint32_t getRetransmitDelay(const mesh::Packet* packet) override; uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override; int getInterferenceThreshold() const override; + bool getCADEnabled() const override; int getAGCResetInterval() const override; void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override; int searchPeersByHash(const uint8_t* hash) override; diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index 9d7a1113..c0610b7f 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -66,6 +66,7 @@ uint32_t Dispatcher::getCADFailMaxDuration() const { void Dispatcher::loop() { if (millisHasNowPassed(next_floor_calib_time)) { _radio->triggerNoiseFloorCalibrate(getInterferenceThreshold()); + _radio->setCADEnabled(getCADEnabled()); next_floor_calib_time = futureMillis(NOISE_FLOOR_CALIB_INTERVAL); } _radio->loop(); diff --git a/src/Dispatcher.h b/src/Dispatcher.h index dd032f13..aad6cba3 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -65,6 +65,8 @@ public: virtual void triggerNoiseFloorCalibrate(int threshold) { } + virtual void setCADEnabled(bool enable) { } + virtual void resetAGC() { } virtual bool isInRecvMode() const = 0; @@ -166,6 +168,7 @@ protected: virtual uint32_t getCADFailRetryDelay() const; virtual uint32_t getCADFailMaxDuration() const; virtual int getInterferenceThreshold() const { return 0; } // disabled by default + virtual bool getCADEnabled() const { return false; } // hardware CAD disabled by default virtual int getAGCResetInterval() const { return 0; } // disabled by default virtual unsigned long getDutyCycleWindowMs() const { return 3600000; } diff --git a/src/MeshCore.h b/src/MeshCore.h index 89e60b1f..4c23b415 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -75,6 +75,8 @@ public: virtual const char* getResetReasonString(uint32_t reason) { return "Not available"; } virtual uint8_t getShutdownReason() const { return 0; } virtual const char* getShutdownReasonString(uint8_t reason) { return "Not available"; } + + inline static uint32_t n_cad_busy = 0; }; /** diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 115865ec..23efe7fa 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -95,7 +95,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->flood_max_unscoped, sizeof(_prefs->flood_max_unscoped)); // 291 file.read((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292 file.read((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 293 - // next: 294 + file.read((uint8_t *)&_prefs->cad_enabled, sizeof(_prefs->cad_enabled)); // 294 + // next: 295 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -127,6 +128,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { // sanitise settings _prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean _prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean + _prefs->cad_enabled = constrain(_prefs->cad_enabled, 0, 1); // boolean file.close(); } @@ -191,8 +193,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290 file.write((uint8_t *)&_prefs->flood_max_unscoped, sizeof(_prefs->flood_max_unscoped)); // 291 file.write((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292 - // next: 293 - file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 294 + file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 293 + file.write((uint8_t *)&_prefs->cad_enabled, sizeof(_prefs->cad_enabled)); // 294 // next: 295 file.close(); @@ -539,6 +541,10 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep _prefs->interference_threshold = atoi(&config[11]); savePrefs(); strcpy(reply, "OK"); + } else if (memcmp(config, "cad ", 4) == 0) { + _prefs->cad_enabled = memcmp(&config[4], "on", 2) == 0; + savePrefs(); + strcpy(reply, "OK"); } else if (memcmp(config, "agc.reset.interval ", 19) == 0) { _prefs->agc_reset_interval = atoi(&config[19]) / 4; savePrefs(); @@ -850,6 +856,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "> %s", StrHelper::ftoa(_prefs->airtime_factor)); } else if (memcmp(config, "int.thresh", 10) == 0) { sprintf(reply, "> %d", (uint32_t) _prefs->interference_threshold); + } else if (memcmp(config, "cad", 3) == 0) { + sprintf(reply, "> %s. # channel busy: %u", _prefs->cad_enabled ? "on" : "off", _board->n_cad_busy); } else if (memcmp(config, "agc.reset.interval", 18) == 0) { sprintf(reply, "> %d", ((uint32_t) _prefs->agc_reset_interval) * 4); } else if (memcmp(config, "multi.acks", 10) == 0) { diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 095b6407..695d2053 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -65,6 +65,7 @@ struct NodePrefs { // persisted to file uint8_t radio_fem_rxgain; // LoRa FEM RX gain setting uint8_t path_hash_mode; // which path mode to use when sending uint8_t loop_detect; + uint8_t cad_enabled; // hardware Channel Activity Detection before TX (boolean) }; class CommonCLICallbacks { diff --git a/src/helpers/radiolib/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp index b01e9440..66606362 100644 --- a/src/helpers/radiolib/RadioLibWrappers.cpp +++ b/src/helpers/radiolib/RadioLibWrappers.cpp @@ -36,6 +36,7 @@ void RadioLibWrapper::begin() { _noise_floor = 0; _threshold = 0; + _cad_enabled = false; // start average out some samples _num_floor_samples = 0; @@ -178,10 +179,29 @@ void RadioLibWrapper::onSendFinished() { state = STATE_IDLE; } +int16_t RadioLibWrapper::performChannelScan() { + return _radio->scanChannel(); +} + bool RadioLibWrapper::isChannelActive() { - return _threshold == 0 - ? false // interference check is disabled - : getCurrentRSSI() > _noise_floor + _threshold; + // int.thresh: RSSI-based interference detection (relative to noise floor) + if (_threshold != 0 && getCurrentRSSI() > _noise_floor + _threshold) return true; + + // cad: hardware channel activity detection + if (_cad_enabled) { + int16_t result = performChannelScan(); + // scanChannel() triggers DIO interrupt (CAD done) which sets STATE_INT_READY + // via setFlag() ISR. Clear it before restarting RX so recvRaw() doesn't + // try to read a non-existent packet and count a spurious recv error. + state = STATE_IDLE; + startRecv(); + if (result != RADIOLIB_CHANNEL_FREE) { + _board->n_cad_busy++; + return true; + } + } + + return false; } float RadioLibWrapper::getLastRSSI() const { diff --git a/src/helpers/radiolib/RadioLibWrappers.h b/src/helpers/radiolib/RadioLibWrappers.h index efd3e179..9943bcab 100644 --- a/src/helpers/radiolib/RadioLibWrappers.h +++ b/src/helpers/radiolib/RadioLibWrappers.h @@ -9,6 +9,7 @@ protected: mesh::MainBoard* _board; uint32_t n_recv, n_sent, n_recv_errors; int16_t _noise_floor, _threshold; + bool _cad_enabled; uint16_t _num_floor_samples; int32_t _floor_sample_sum; uint8_t _preamble_sf; @@ -32,7 +33,7 @@ public: bool isInRecvMode() const override; bool isChannelActive(); - bool isReceiving() override { + bool isReceiving() override { if (isReceivingPacket()) return true; return isChannelActive(); @@ -46,9 +47,11 @@ public: virtual uint8_t getSpreadingFactor() const { return LORA_SF; } static uint16_t preambleLengthForSF(uint8_t sf) { return sf <= 8 ? 32 : 16; } void updatePreamble(uint8_t sf) { _preamble_sf = sf; _radio->setPreambleLength(preambleLengthForSF(sf)); } + virtual int16_t performChannelScan(); int getNoiseFloor() const override { return _noise_floor; } void triggerNoiseFloorCalibrate(int threshold) override; + void setCADEnabled(bool enable) override { _cad_enabled = enable; } void resetAGC() override; void loop() override; From 7473719b923830f7d9086dc13b18ff2c834deba2 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Wed, 24 Jun 2026 13:46:16 -0700 Subject: [PATCH 151/214] build.sh: print build flags before build --- build.sh | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/build.sh b/build.sh index 8a6044c7..742c40ef 100755 --- a/build.sh +++ b/build.sh @@ -966,6 +966,47 @@ apply_radio_overrides() { fi } +print_build_flags() { + local env_name=$1 + + echo "Build flags for ${env_name}:" + python3 -c ' +import json +import os +import shlex +import sys + +env_name = sys.argv[1] +data = json.load(sys.stdin) +config_flags = [] + +for section, options in data: + if section != f"env:{env_name}": + continue + for key, value in options: + if key != "build_flags": + continue + if isinstance(value, list): + config_flags.extend(str(flag) for flag in value) + elif value: + config_flags.extend(shlex.split(str(value))) + break + +env_flags = shlex.split(os.environ.get("PLATFORMIO_BUILD_FLAGS", "")) + +def print_flags(title, flags): + print(f" {title}:") + if not flags: + print(" (none)") + return + for flag in flags: + print(f" {flag}") + +print_flags("platformio.ini build_flags", config_flags) +print_flags("PLATFORMIO_BUILD_FLAGS", env_flags) +' "$env_name" <<<"$PIO_CONFIG_JSON" +} + copy_build_output() { local source_path=$1 local output_path=$2 @@ -1096,6 +1137,7 @@ build_firmware() { apply_debug_overrides apply_radio_overrides + print_build_flags "$env_name" pio run -e "$env_name" build_status=$? if [ "$build_status" -eq 0 ]; then From 48ac725d7687c20e93dc4e478c40ba35bfece57d Mon Sep 17 00:00:00 2001 From: mikecarper Date: Wed, 24 Jun 2026 17:06:49 -0700 Subject: [PATCH 152/214] Tune keymind repeater behavior --- boards/nrf52840_s140_v7_extrafs.ld | 3 + build.sh | 75 +- docs/cli_commands.md | 264 +++- docs/halo_keymind_settings.md | 26 +- examples/simple_repeater/MyMesh.cpp | 1654 +++++++++++++++++++- examples/simple_repeater/MyMesh.h | 92 +- examples/simple_repeater/main.cpp | 11 +- examples/simple_room_server/MyMesh.cpp | 40 +- examples/simple_room_server/MyMesh.h | 5 + examples/simple_room_server/main.cpp | 11 +- src/Mesh.cpp | 109 +- src/Mesh.h | 5 +- src/MeshCore.h | 2 + src/helpers/CommonCLI.cpp | 495 +++++- src/helpers/CommonCLI.h | 74 +- src/helpers/ESP32Board.cpp | 38 +- src/helpers/ESP32Board.h | 12 + src/helpers/NRF52Board.cpp | 82 +- src/helpers/NRF52Board.h | 4 +- src/helpers/SimpleMeshTables.h | 31 +- src/helpers/radiolib/CustomLR1110Wrapper.h | 4 + variants/minewsemi_me25ls01/platformio.ini | 1 + variants/thinknode_m3/platformio.ini | 1 + variants/wio_wm1110/platformio.ini | 1 + 24 files changed, 2885 insertions(+), 155 deletions(-) diff --git a/boards/nrf52840_s140_v7_extrafs.ld b/boards/nrf52840_s140_v7_extrafs.ld index 48348188..ef6ebba1 100644 --- a/boards/nrf52840_s140_v7_extrafs.ld +++ b/boards/nrf52840_s140_v7_extrafs.ld @@ -7,6 +7,9 @@ MEMORY { FLASH (rx) : ORIGIN = 0x27000, LENGTH = 0xD4000 - 0x27000 + /* To keep data in RAM across resets */ + PERSISTENT_RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 8 + /* SRAM required by Softdevice depend on * - Attribute Table Size (Number of Services and Characteristics) * - Vendor UUID count diff --git a/build.sh b/build.sh index 742c40ef..6a01595d 100755 --- a/build.sh +++ b/build.sh @@ -15,6 +15,7 @@ RADIO_FREQ_OVERRIDE="" RADIO_BW_OVERRIDE="" RADIO_SF_OVERRIDE="" RADIO_CR_OVERRIDE="" +FIRMWARE_PROFILE_OVERRIDE="${FIRMWARE_PROFILE_OVERRIDE:-}" BATCH_BUILD_MODE=0 RESOLVED_BUILD_TARGETS=() @@ -60,7 +61,7 @@ Examples: Build firmware for the "RAK_4631_repeater" device target $ bash build.sh build-firmware RAK_4631_repeater -Run without arguments to choose an interactive build action/target, debug options, radio settings, and firmware version +Run without arguments to choose an interactive build action/target, debug options, radio settings, firmware profile, and firmware version $ bash build.sh Build all firmwares for device targets containing the string "RAK_4631" @@ -315,6 +316,10 @@ clear_radio_overrides() { RADIO_CR_OVERRIDE="" } +clear_firmware_profile_overrides() { + FIRMWARE_PROFILE_OVERRIDE="" +} + set_radio_overrides() { RADIO_SETTING_TITLE=$1 RADIO_FREQ_OVERRIDE=$2 @@ -323,6 +328,10 @@ set_radio_overrides() { RADIO_CR_OVERRIDE=$5 } +set_firmware_profile_override() { + FIRMWARE_PROFILE_OVERRIDE=$1 +} + fetch_suggested_radio_settings() { python3 - "$RADIO_SETTINGS_API_URL" <<'PY' import json @@ -367,7 +376,7 @@ is_valid_custom_radio_bandwidth() { python3 - "$1" <<'PY' import sys -allowed = [7.81, 10.42, 15.63, 20.83, 31.25, 41.67, 62.5, 125.0, 250.0, 500.0] +allowed = [7.8, 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125.0, 250.0, 500.0] try: value = float(sys.argv[1]) except Exception: @@ -403,13 +412,13 @@ prompt_for_custom_radio_setting() { echo "Please enter 5, 6, 7, 8, 9, 10, 11, or 12." done - echo "Bandwidth options (kHz): 7.81 10.42 15.63 20.83 31.25 41.67 62.5 125 250 500" + echo "Bandwidth options (kHz): 7.8 10.4 15.6 20.8 31.25 41.7 62.5 125 250 500" while true; do read -r -p "BW (kHz): " bw if [[ "$bw" =~ ^[0-9]+([.][0-9]+)?$ ]] && is_valid_custom_radio_bandwidth "$bw"; then break fi - echo "Please enter one of: 7.81 10.42 15.63 20.83 31.25 41.67 62.5 125 250 500." + echo "Please enter one of: 7.8 10.4 15.6 20.8 31.25 41.7 62.5 125 250 500." done echo "Coding rate options: CR5, CR6, CR7, CR8" @@ -426,6 +435,7 @@ prompt_for_custom_radio_setting() { prompt_for_radio_build_settings() { local -a preset_rows=() + local -a fetched_preset_rows=() local -a options=("Keep target defaults (no radio override)") local row local title @@ -443,20 +453,26 @@ prompt_for_radio_build_settings() { if preset_output=$(fetch_suggested_radio_settings); then if [ -n "$preset_output" ]; then - mapfile -t preset_rows <<< "$preset_output" + mapfile -t fetched_preset_rows <<< "$preset_output" fi - for row in "${preset_rows[@]}"; do + for row in "${fetched_preset_rows[@]}"; do if [ -z "$row" ]; then continue fi - IFS=$'\t' read -r title description freq bw sf cr <<< "$row" - options+=("${title}: ${description}") + preset_rows+=("$row") done else echo "Could not fetch radio presets from ${RADIO_SETTINGS_API_URL}." - preset_rows=() fi + for row in "${preset_rows[@]}"; do + if [ -z "$row" ]; then + continue + fi + IFS=$'\t' read -r title description freq bw sf cr <<< "$row" + options+=("${title}: ${description}") + done + options+=("Custom") custom_index=${#options[@]} @@ -491,6 +507,37 @@ prompt_for_radio_build_settings() { done } +prompt_for_firmware_profile_settings() { + local -a options=( + "Keep target defaults" + "Cascade: path.hash.mode=2 / loop.detect=minimal / rxdelay=2 / agc.reset.interval=8 / advert.interval=0 / flood.advert.interval=83 / multi.acks=1" + ) + + clear_firmware_profile_overrides + + echo "Set firmware profile options:" + while true; do + print_numbered_menu "${options[@]}" + prompt_menu_choice "Firmware profile" "${#options[@]}" + if [ "$MENU_CHOICE" == "QUIT" ]; then + echo "Cancelled." + exit 1 + fi + + case "$MENU_CHOICE" in + 1) + echo "Using target default firmware profile settings." + return 0 + ;; + 2) + set_firmware_profile_override "cascade" + echo "Using firmware profile: Cascade" + return 0 + ;; + esac + done +} + get_env_metadata() { local env_name=$1 local trimmed_env_name @@ -966,6 +1013,14 @@ apply_radio_overrides() { fi } +apply_firmware_profile_overrides() { + case "${FIRMWARE_PROFILE_OVERRIDE,,}" in + cascade) + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DCASCADE_PROFILE=1 -DDEFAULT_PATH_HASH_MODE=2 -DDEFAULT_LOOP_DETECT=1 -DDEFAULT_RX_DELAY_BASE=2.0f -DDEFAULT_AGC_RESET_INTERVAL_SECONDS=8 -DDEFAULT_ADVERT_INTERVAL_MINUTES=0 -DDEFAULT_FLOOD_ADVERT_INTERVAL_HOURS=83 -DDEFAULT_MULTI_ACKS=1" + ;; + esac +} + print_build_flags() { local env_name=$1 @@ -1136,6 +1191,7 @@ build_firmware() { disable_debug_flags apply_debug_overrides apply_radio_overrides + apply_firmware_profile_overrides print_build_flags "$env_name" pio run -e "$env_name" @@ -1363,6 +1419,7 @@ main() { prompt_for_build_mode prompt_for_debug_build_settings prompt_for_radio_build_settings + prompt_for_firmware_profile_settings set -- "${SELECTED_COMMAND_ARGS[@]}" validate_command "$@" fi diff --git a/docs/cli_commands.md b/docs/cli_commands.md index ba92a2b1..fe3464c3 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -92,9 +92,10 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- -### Start an Over-The-Air (OTA) firmware update +### Start or stop an Over-The-Air (OTA) firmware update **Usage:** - `start ota` +- `stop ota` --- @@ -214,7 +215,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Parameters:** - `freq`: Frequency in MHz -- `bw`: Bandwidth in kHz +- `bw`: Bandwidth in kHz. Most targets allow `7.8`, `10.4`, `15.6`, `20.8`, `31.25`, `41.7`, `62.5`, `125`, `250`, `500`. LR1110 targets allow `62.5`, `125`, `250`, `500`. - `sf`: Spreading factor (5-12) - `cr`: Coding rate (5-8) @@ -247,8 +248,8 @@ This document provides an overview of CLI commands that can be sent to MeshCore - `tempradio ,,,,` **Parameters:** -- `freq`: Frequency in MHz (300-2500) -- `bw`: Bandwidth in kHz (7.8-500) +- `freq`: Frequency in MHz (150-2500) +- `bw`: Bandwidth in kHz (same allowed values as `set radio`) - `sf`: Spreading factor (5-12) - `cr`: Coding rate (5-8) - `timeout_mins`: Duration in minutes (must be > 0) @@ -257,6 +258,32 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### Schedule radio parameter changes +**Usage:** +- `set radioat ,,,,` +- `get radioat [n|all]` +- `del radioat [n|all]` +- `set tempradioat ,,,,,` +- `get tempradioat [n|all]` +- `del tempradioat [n|all]` + +**Parameters:** +- `freq`: Frequency in MHz (150-2500) +- `bw`: Bandwidth in kHz (same allowed values as `set radio`) +- `sf`: Spreading factor (5-12) +- `cr`: Coding rate (5-8) +- `start_time`: Unix epoch time when the setting starts +- `end_time`: Unix epoch time when a temporary setting reverts +- `n`: Scheduled entry number from `get radioat` or `get tempradioat` + +**Notes:** +- `get radioat` and `get tempradioat` list all entries when `n` is omitted. +- `del radioat` and `del tempradioat` delete all entries when `n` is omitted. +- Each queue supports 3 entries. Scheduled entries are not saved across reboot. +- `radioat` saves the new radio preferences when it fires. `tempradioat` applies temporarily, then reverts to the saved radio preferences. + +--- + #### View or change this node's frequency **Usage:** - `get freq` @@ -423,6 +450,46 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### Send a repeater flood text +**Usage:** +- `send text.flood ` + +**Parameters:** +- `message`: Text to send to the shared `#repeaters` flood channel, prefixed with this node's name. + +**Example:** +``` +send text.flood checking ridge link +``` + +--- + +#### View or change battery alert state +**Usage:** +- `get battery.alert` +- `set battery.alert ` + +**Default:** `off` + +**Note:** When enabled, the repeater checks battery level once per minute and sends low-battery warnings to the `#repeaters` flood channel. + +--- + +#### View or change battery alert thresholds +**Usage:** +- `get battery.alert.low` +- `set battery.alert.low <1-100>` +- `get battery.alert.critical` +- `set battery.alert.critical <0-99>` + +**Defaults:** +- `battery.alert.low`: `20` +- `battery.alert.critical`: `10` + +**Note:** The low threshold must be greater than the critical threshold. + +--- + #### View this node's public key **Usage:** `get public.key` @@ -450,7 +517,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Default:** `off` -**Note:** When enabled, device enters sleep mode between radio transmissions +**Note:** When enabled, device enters sleep mode between radio transmissions. Enabling is refused from the local serial console or while an active USB serial data connection is detected; USB power alone does not block power saving. --- @@ -1011,7 +1078,30 @@ set direct.retry off --- -#### View or apply a direct retry preset +#### View or change direct retry heard-table gate +**Usage:** +- `get direct.retry.heard` +- `set direct.retry.heard ` + +**Parameters:** +- `state`: `on`|`off` + +**Default:** `on` + +**Note:** When enabled, the recent repeater table is the direct retry eligibility +gate. Prefixes missing from the table are assumed reachable; prefixes in the +table below the active SNR gate are blocked. + +**Examples:** +``` +get direct.retry.heard +set direct.retry.heard on +set direct.retry.heard off +``` + +--- + +#### View or apply a retry preset **Usage:** - `get retry.preset` - `set retry.preset ` @@ -1020,10 +1110,11 @@ set direct.retry off - `preset`: `infra`|`rooftop`|`mobile` **Notes:** +- Applies shared direct retry and flood retry defaults. - `infra`: fewer, slower retries for stable fixed infrastructure. - `rooftop`: default long retry window for weak rooftop links. -- `mobile`: long retry count with shorter spacing for moving or changing links. -- Changing `direct.retry.count`, `direct.retry.base`, `direct.retry.step`, or `direct.retry.margin` makes the preset report as `custom`. +- `mobile`: long retry count with shorter spacing for moving or changing links; flood retry count is `15`. +- Changing `direct.retry.count`, `direct.retry.base`, `direct.retry.step`, `direct.retry.margin`, `flood.retry.count`, or `flood.retry.path` makes the preset report as `custom`. **Examples:** ``` @@ -1035,6 +1126,155 @@ set retry.preset mobile --- +### Flood Retry + +Flood retry resends flood-routed packets when the same packet is not heard from +another qualifying repeater. + +#### View or change flood retry count +**Usage:** +- `get flood.retry.count` +- `set flood.retry.count ` + +**Parameters:** +- `count`: Base retry attempts after the original send, from `0` to `15`. `0` disables flood retry. + +**Note:** Actual attempts are capped at `15`. Hop 1 flood retries use `count * 2`; hop 2 flood retries use `count * 1.5`, rounded up. + +**Defaults:** +- `infra`: `1` +- `rooftop`: `3` +- `mobile`: `15` + +**Examples:** +``` +get flood.retry.count +set flood.retry.count 0 +set flood.retry.count 15 +``` + +--- + +#### View or change flood retry path gate +**Usage:** +- `get flood.retry.path` +- `set flood.retry.path ` + +**Parameters:** +- `count`: Maximum flood path hash count eligible for retry, from `0` to `63`. +- `off`: Disable the path-length gate. + +**Defaults:** +- `infra`: `1` +- `rooftop`: `2` +- `mobile`: `1` + +**Examples:** +``` +get flood.retry.path +set flood.retry.path 1 +set flood.retry.path off +``` + +--- + +#### View or change flood retry advert handling +**Usage:** +- `get flood.retry.advert` +- `set flood.retry.advert ` + +**Parameters:** +- `on`: Retry node advert floods. +- `off`: Do not retry node advert floods. + +**Default:** `off` + +**Examples:** +``` +get flood.retry.advert +set flood.retry.advert off +``` + +--- + +#### View or change flood retry target prefixes +**Usage:** +- `get flood.retry.prefixes` +- `set flood.retry.prefixes ` + +**Parameters:** +- `prefixes`: Comma-separated 3-byte path hash prefixes, up to 8 entries. +- `none` or `off`: Clear the list. + +**Note:** When set, non-bridge flood retry only accepts same-packet echoes whose +last hop matches one of these prefixes. When unset, any non-ignored last hop can +cancel the retry. + +**Examples:** +``` +get flood.retry.prefixes +set flood.retry.prefixes A58296,860CCA,425E5C +set flood.retry.prefixes none +``` + +--- + +#### View or change flood retry ignored prefixes +**Usage:** +- `get flood.retry.ignore` +- `set flood.retry.ignore ` + +**Parameters:** +- `prefixes`: Comma-separated 3-byte path hash prefixes, up to 8 entries. +- `none` or `off`: Clear the list. + +**Note:** Non-bridge flood retry does not cancel on same-packet echoes whose +last hop matches this list. Bridge mode also excludes these prefixes from bucket +and `other` hits. + +**Examples:** +``` +get flood.retry.ignore +set flood.retry.ignore 71CE82,C7618C +set flood.retry.ignore none +``` + +--- + +#### View or change flood retry bridge mode +**Usage:** +- `get flood.retry.bridge` +- `set flood.retry.bridge ` + +**Note:** Bridge mode retries until each configured fresh bucket, plus the non-source `other` bucket, has been heard or the retry count is exhausted. + +**Examples:** +``` +get flood.retry.bridge +set flood.retry.bridge on +``` + +--- + +#### View or change flood retry bridge buckets +**Usage:** +- `get flood.retry.bucket.` +- `set flood.retry.bucket ` + +**Parameters:** +- `n`: Bucket number from `1` to `6`. +- `prefixes`: Comma-separated 3-byte path hash prefixes, up to 17 entries per bucket. +- `none` or `off`: Clear the bucket. + +**Examples:** +``` +get flood.retry.bucket.1 +set flood.retry.bucket 1 71CE82,C7618C +set flood.retry.bucket 2 none +``` + +--- + #### View or change direct retry count **Usage:** - `get direct.retry.count` @@ -1067,6 +1307,9 @@ set direct.retry.count 15 **Explanation:** - The first retry waits `base` milliseconds after the failed echo window. +- For non-TRACE direct paths shorter than 6 remaining hops, the effective wait is scaled by `hops / 6`. +- Non-TRACE direct paths with 6 or more remaining hops use the configured value unchanged. +- TRACE retries shorter than 16 remaining hops use `hops / 16`; 16 or more remaining hops use the configured value unchanged. - Larger values reduce channel pressure and give slow repeaters more time. - Smaller values recover faster but create tighter retry bursts. @@ -1092,7 +1335,10 @@ set direct.retry.base 500 **Explanation:** - Retry delay is `base + attempt_index * step`. -- With `base=175` and `step=100`, retries wait about `175`, `275`, `375`, `475` ms, and so on. +- For non-TRACE direct paths shorter than 6 remaining hops, that computed delay is scaled by `hops / 6`. +- Non-TRACE direct paths with 6 or more remaining hops use the computed delay unchanged. +- TRACE retries shorter than 16 remaining hops use `hops / 16`; 16 or more remaining hops use the computed delay unchanged. +- With `base=175` and `step=100`, non-TRACE paths with 6 or more remaining hops wait about `175`, `275`, `375`, `475` ms, and so on. - `step=0` keeps every retry at the same delay. - Larger steps spread retries over time and are safer on busy channels. diff --git a/docs/halo_keymind_settings.md b/docs/halo_keymind_settings.md index ca2c0803..62bc4779 100644 --- a/docs/halo_keymind_settings.md +++ b/docs/halo_keymind_settings.md @@ -78,7 +78,6 @@ set flood.retry.ignore none | `battery.alert.critical` | Critical threshold percentage. Critical warnings repeat more often. | `get battery.alert.critical`, `set battery.alert.critical <0-99>` | `set battery.alert.critical 10` | | `recent.repeater` | Shows, seeds, or clears the recent repeater prefix/SNR table used by direct retry and bridge freshness checks. | `get recent.repeater`, `get recent.repeater `, `set recent.repeater `, `clear recent.repeater` | `set recent.repeater A1B2C3 -8.5` | | `outpath` | Overrides the primary direct route used for replies to the current remote client. | `get outpath`, `set outpath `, `set outpath direct`, `set outpath clear`, `set outpath flood` | `set outpath A1B2C3,D4E5F6` | -| `altpath` | Optional second direct route used for duplicate response attempts to the current remote client. | `get altpath`, `set altpath `, `set altpath clear` | `set altpath A1B2C3,D4E5F6` | ## Other Keymind Commands @@ -146,8 +145,8 @@ Serial CLI pages contain up to `128` rows. Remote LoRa CLI pages contain up to ## Direct Path Overrides -`outpath` and `altpath` apply to the current remote client ACL entry. They need -remote client context, so they are not useful from the local serial CLI. +`outpath` applies to the current remote client ACL entry. It needs remote +client context, so it is not useful from the local serial CLI. Set paths with comma-separated hop hashes. Each hop must be `2`, `4`, or `6` hex characters, and all hops in one path must use the same width. @@ -158,17 +157,12 @@ set outpath A1B2C3,D4E5F6 set outpath direct set outpath clear set outpath flood - -get altpath -set altpath A1B2C3,D4E5F6 -set altpath clear ``` `set outpath direct` sets a zero-hop direct route for a client reachable without repeaters. `set outpath clear` forgets the override and lets normal path discovery fill it again. `set outpath flood` forces replies to use flood packets -until the client logs in again. `altpath` sends a duplicate reply over a second -direct route; clearing it returns replies to a single route. +until the client logs in again. ## Direct Retry Settings @@ -180,8 +174,8 @@ Direct retry applies to direct-routed packets. A queued resend is canceled when | `direct.retry.heard` | Uses the recent repeater table as the direct retry eligibility gate. | `get direct.retry.heard`, `set direct.retry.heard on/off` | `set direct.retry.heard on` | | `direct.retry.margin` | SNR margin in dB above the SF-specific receive floor. | `get direct.retry.margin`, `set direct.retry.margin <0-40>` | `set direct.retry.margin 5` | | `direct.retry.count` | Maximum direct retry attempts after initial TX. | `get direct.retry.count`, `set direct.retry.count <1-15>` | `set direct.retry.count 15` | -| `direct.retry.base` | Base wait in milliseconds before retry. | `get direct.retry.base`, `set direct.retry.base <10-5000>` | `set direct.retry.base 175` | -| `direct.retry.step` | Milliseconds added per retry attempt. | `get direct.retry.step`, `set direct.retry.step <0-5000>` | `set direct.retry.step 100` | +| `direct.retry.base` | Base wait in milliseconds before retry; non-TRACE paths under 6 remaining hops scale by `hops / 6`, TRACE paths under 16 by `hops / 16`. | `get direct.retry.base`, `set direct.retry.base <10-5000>` | `set direct.retry.base 175` | +| `direct.retry.step` | Milliseconds added per retry attempt before the same short-path scaling. | `get direct.retry.step`, `set direct.retry.step <0-5000>` | `set direct.retry.step 100` | | `direct.retry.cr` | Adaptive coding-rate thresholds for direct retry packets. Uses `CR4`, `CR5`, `CR7`, or `CR8`; `CR6` is never selected. | `get direct.retry.cr`, `set direct.retry.cr ,,,`, `set direct.retry.cr off` | `set direct.retry.cr 10.0,7.5,2.5,0` | The default adaptive coding-rate profile is `10.0,7.5,2.5,2.5`. @@ -217,14 +211,16 @@ set direct.retry.margin 0 ## Flood And Advert Settings -Flood retry applies to flood-routed packets. A queued retry is canceled when a qualifying downstream echo is heard. +Flood retry applies to flood-routed packets. A queued retry is canceled when the +same packet is heard from a qualifying, non-ignored repeater. Bridge mode uses +the bucket rules below instead. | Setting | What it does | How to use | Example | | --- | --- | --- | --- | -| `flood.retry.count` | Maximum flood retry attempts after initial TX. `0` disables flood retry. | `get flood.retry.count`, `set flood.retry.count <0-15>` | `set flood.retry.count 7` | +| `flood.retry.count` | Base flood retry attempts after initial TX. Hop 1 doubles it, hop 2 uses 1.5x rounded up, and actual attempts cap at `15`; `0` disables flood retry. | `get flood.retry.count`, `set flood.retry.count <0-15>` | `set flood.retry.count 7` | | `flood.retry.path` | Maximum path hash count eligible for flood retry, or `off` to disable the gate. | `get flood.retry.path`, `set flood.retry.path <0-63/off>` | `set flood.retry.path 1` | | `flood.retry.advert` | Allows or blocks retry for node advert packets (`type=4`). Default is `off`. | `get flood.retry.advert`, `set flood.retry.advert on/off` | `set flood.retry.advert off` | -| `flood.retry.prefixes` | Target prefixes. If set, only matching downstream echoes cancel a retry. | `get flood.retry.prefixes`, `set flood.retry.prefixes ` | `set flood.retry.prefixes BEEBB0,425E5C` | +| `flood.retry.prefixes` | Target prefixes. If set, only same-packet echoes from matching last-hop prefixes cancel a retry. | `get flood.retry.prefixes`, `set flood.retry.prefixes ` | `set flood.retry.prefixes BEEBB0,425E5C` | | `flood.retry.ignore` | Ignored prefixes. In non-bridge retry, ignored last-hop echoes do not cancel retry. | `get flood.retry.ignore`, `set flood.retry.ignore ` | `set flood.retry.ignore 71CE82,C7618C` | | `flood.retry.bridge` | Enables bucket-based bridge retry logic. | `get flood.retry.bridge`, `set flood.retry.bridge on/off` | `set flood.retry.bridge on` | | `flood.retry.bucket.` | Shows one bridge bucket. Buckets are numbered `1`-`6`. | `get flood.retry.bucket.` | `get flood.retry.bucket.1` | @@ -236,7 +232,7 @@ The shared retry preset sets these flood defaults: | --- | ---: | ---: | | `infra` | `1` | `1` | | `rooftop` | `3` | `2` | -| `mobile` | `3` | `1` | +| `mobile` | `15` | `1` | Example for path-gated retry: diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 2641b20d..8f301412 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -19,6 +19,28 @@ #define LORA_TX_POWER 20 #endif +#ifndef DEFAULT_ADVERT_INTERVAL_MINUTES + #define DEFAULT_ADVERT_INTERVAL_MINUTES 2 +#endif +#ifndef DEFAULT_FLOOD_ADVERT_INTERVAL_HOURS + #define DEFAULT_FLOOD_ADVERT_INTERVAL_HOURS 47 +#endif +#ifndef DEFAULT_AGC_RESET_INTERVAL_SECONDS + #define DEFAULT_AGC_RESET_INTERVAL_SECONDS 0 +#endif +#ifndef DEFAULT_RX_DELAY_BASE + #define DEFAULT_RX_DELAY_BASE 0.0f +#endif +#ifndef DEFAULT_MULTI_ACKS + #define DEFAULT_MULTI_ACKS 0 +#endif +#ifndef DEFAULT_PATH_HASH_MODE + #define DEFAULT_PATH_HASH_MODE 0 +#endif +#ifndef DEFAULT_LOOP_DETECT + #define DEFAULT_LOOP_DETECT LOOP_DETECT_OFF +#endif + #ifndef ADVERT_NAME #define ADVERT_NAME "repeater" #endif @@ -60,6 +82,165 @@ #define LAZY_CONTACTS_WRITE_DELAY 5000 +#ifndef REPEATERS_CHANNEL_KEY_HEX + #define REPEATERS_CHANNEL_KEY_HEX "89db441e2814dccf0dbd2e8cc5f501a3" +#endif +#ifndef BATT_MIN_MILLIVOLTS + #define BATT_MIN_MILLIVOLTS 3000 +#endif +#ifndef BATT_MAX_MILLIVOLTS + #define BATT_MAX_MILLIVOLTS 4200 +#endif + +#define LOW_BATTERY_MIN_VALID_MV 1000 +#define LOW_BATTERY_CHECK_INTERVAL (60UL * 1000UL) +#define LOW_BATTERY_WARN_INTERVAL (24UL * 60UL * 60UL * 1000UL) +#define LOW_BATTERY_CRITICAL_INTERVAL (12UL * 60UL * 60UL * 1000UL) + +#ifndef DIRECT_RETRY_TRACE_SCALE_HOPS + #define DIRECT_RETRY_TRACE_SCALE_HOPS 32U +#endif + +static const char* skipLocalSpaces(const char* text) { + while (text != NULL && *text == ' ') text++; + return text; +} + +static bool selectorIsEmpty(const char* text) { + text = skipLocalSpaces(text); + return text == NULL || *text == 0; +} + +static bool selectorIsAll(const char* text) { + text = skipLocalSpaces(text); + if (text == NULL || memcmp(text, "all", 3) != 0) { + return false; + } + text += 3; + while (*text == ' ') text++; + return *text == 0; +} + +static bool parsePositiveSelector(const char* text, int& value) { + text = skipLocalSpaces(text); + if (text == NULL || *text == 0) { + return false; + } + + uint32_t n = 0; + bool saw_digit = false; + while (*text >= '0' && *text <= '9') { + saw_digit = true; + n = (n * 10) + (uint32_t)(*text - '0'); + if (n > 32767) { + return false; + } + text++; + } + while (*text == ' ') text++; + if (!saw_digit || n == 0 || *text != 0) { + return false; + } + value = (int)n; + return true; +} + +static bool bwMatches(float bw, float allowed) { + float diff = bw - allowed; + if (diff < 0.0f) diff = -diff; + return diff <= 0.001f; +} + +static bool isValidLoRaBandwidth(float bw) { +#if defined(USE_LR1110) + return bwMatches(bw, 62.5f) + || bwMatches(bw, 125.0f) + || bwMatches(bw, 250.0f) + || bwMatches(bw, 500.0f); +#elif defined(USE_LLCC68) || defined(USE_SX1272) + return bwMatches(bw, 125.0f) + || bwMatches(bw, 250.0f) + || bwMatches(bw, 500.0f); +#else + return bwMatches(bw, 7.8f) + || bwMatches(bw, 10.4f) + || bwMatches(bw, 15.6f) + || bwMatches(bw, 20.8f) + || bwMatches(bw, 31.25f) + || bwMatches(bw, 41.7f) + || bwMatches(bw, 62.5f) + || bwMatches(bw, 125.0f) + || bwMatches(bw, 250.0f) + || bwMatches(bw, 500.0f); +#endif +} + +static bool isValidScheduledRadioParams(float freq, float bw, uint8_t sf, uint8_t cr) { + return freq >= 150.0f && freq <= 2500.0f + && isValidLoRaBandwidth(bw) + && sf >= 5 && sf <= 12 + && cr >= 5 && cr <= 8; +} + +static bool buildRepeatersChannel(mesh::GroupChannel& channel) { + const char* hex = REPEATERS_CHANNEL_KEY_HEX; + size_t hex_len = strlen(hex); + if (!(hex_len == 32 || hex_len == 64)) return false; + for (size_t i = 0; i < hex_len; i++) { + if (!mesh::Utils::isHexChar(hex[i])) return false; + } + + memset(channel.secret, 0, sizeof(channel.secret)); + size_t key_len = hex_len / 2; + if (!mesh::Utils::fromHex(channel.secret, key_len, hex)) return false; + + mesh::Utils::sha256(channel.hash, sizeof(channel.hash), channel.secret, key_len); + return true; +} + +static uint8_t batteryPercentFromMilliVolts(uint16_t batt_mv) { + const int min_mv = BATT_MIN_MILLIVOLTS; + const int max_mv = BATT_MAX_MILLIVOLTS; + if (max_mv <= min_mv) return 100; + + int pct = (((int)batt_mv - min_mv) * 100) / (max_mv - min_mv); + if (pct < 0) return 0; + if (pct > 100) return 100; + return (uint8_t)pct; +} + +static bool parseBatteryAlertPercent(const char* value, uint8_t min_value, uint8_t max_value, uint8_t& result) { + if (value == NULL || *value == 0) { + return false; + } + + uint16_t parsed = 0; + while (*value) { + if (*value < '0' || *value > '9') { + return false; + } + parsed = (uint16_t)(parsed * 10 + (*value - '0')); + if (parsed > max_value) { + return false; + } + value++; + } + if (parsed < min_value) { + return false; + } + + result = (uint8_t)parsed; + return true; +} + +static void formatFixed3(char* dest, size_t dest_len, float value) { + long scaled = (long)(value * 1000.0f + (value >= 0.0f ? 0.5f : -0.5f)); + long whole = scaled / 1000; + long decimals = scaled % 1000; + if (decimals < 0) decimals = -decimals; + snprintf(dest, dest_len, "%ld.%03ld", whole, decimals); +} + void MyMesh::putNeighbour(const mesh::Identity &id, uint32_t timestamp, float snr) { #if MAX_NEIGHBOURS // check if neighbours enabled // find existing neighbour, else use least recently updated @@ -558,6 +739,66 @@ bool MyMesh::extractDirectRetryPrefix(const mesh::Packet* packet, uint8_t* prefi return true; } +static bool isDirectShortcutPayload(const mesh::Packet* packet) { + if (packet == NULL || !packet->isRouteDirect()) { + return false; + } + + switch (packet->getPayloadType()) { + case PAYLOAD_TYPE_PATH: + case PAYLOAD_TYPE_REQ: + case PAYLOAD_TYPE_RESPONSE: + case PAYLOAD_TYPE_TXT_MSG: + case PAYLOAD_TYPE_ANON_REQ: + return true; + default: + return false; + } +} + +bool MyMesh::maybeShortCircuitDirect(mesh::Packet* packet) { + if (!isDirectShortcutPayload(packet)) { + return false; + } + + uint8_t hash_size = packet->getPathHashSize(); + uint8_t hash_count = packet->getPathHashCount(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES || hash_count < 3) { + return false; + } + + int self_idx = -1; + for (uint8_t i = 1; i + 1 < hash_count; i++) { + if (self_id.isHashMatch(&packet->path[i * hash_size], hash_size)) { + self_idx = i; + break; + } + } + if (self_idx < 1) { + return false; + } + + const SimpleMeshTables* tables = static_cast(getTables()); + if (tables == NULL) { + return false; + } + + const uint8_t* previous_hop = &packet->path[(self_idx - 1) * hash_size]; + const uint8_t* next_hop = &packet->path[(self_idx + 1) * hash_size]; + if (tables->findRecentRepeaterByHash(previous_hop, hash_size) == NULL + || tables->findRecentRepeaterByHash(next_hop, hash_size) == NULL) { + return false; + } + + uint8_t remaining_count = hash_count - (uint8_t)self_idx; + memmove(packet->path, &packet->path[self_idx * hash_size], remaining_count * hash_size); + packet->setPathHashCount(remaining_count); + MESH_DEBUG_PRINTLN("direct shortcut: skipped %u planned hop(s), remaining=%u", + (uint32_t)self_idx, + (uint32_t)remaining_count); + return true; +} + int8_t MyMesh::getDirectRetryMinSNRX4() const { switch (active_sf) { case 7: return -30; @@ -587,11 +828,73 @@ uint32_t MyMesh::getDirectRetryAttemptStepMillis() const { return _prefs.direct_retry_step_ms; } +static uint8_t decodeRetryTraceHashSize(uint8_t flags, uint8_t route_bytes) { + uint8_t code = flags & 0x03; + uint8_t size_pow2 = (uint8_t)(1U << code); + uint8_t size_linear = (uint8_t)(code + 1U); + + bool pow2_ok = size_pow2 > 0 && (route_bytes % size_pow2) == 0; + bool linear_ok = size_linear > 0 && (route_bytes % size_linear) == 0; + + if (pow2_ok && !linear_ok) return size_pow2; + if (linear_ok && !pow2_ok) return size_linear; + if (pow2_ok) return size_pow2; + return size_linear; +} + +static uint8_t getDirectRetryRemainingHops(const mesh::Packet* packet) { + if (packet == NULL) { + return 0; + } + if (packet->getPayloadType() != PAYLOAD_TYPE_TRACE) { + return packet->getPathHashCount(); + } + if (packet->payload_len < 9) { + return 0; + } + + uint8_t route_bytes = packet->payload_len - 9; + uint8_t hash_size = decodeRetryTraceHashSize(packet->payload[8], route_bytes); + if (hash_size == 0) { + return 0; + } + + uint8_t route_hops = route_bytes / hash_size; + if (packet->path_len >= route_hops) { + return 0; + } + return route_hops - packet->path_len; +} + +static uint32_t scaleDirectRetryDelayForPath(const mesh::Packet* packet, uint32_t delay_ms) { + uint8_t hops = getDirectRetryRemainingHops(packet); + if (hops == 0) { + return delay_ms; + } + + if (packet != NULL && packet->getPayloadType() == PAYLOAD_TYPE_TRACE) { + if (hops >= DIRECT_RETRY_TRACE_SCALE_HOPS) { + return delay_ms; + } + uint32_t scaled = ((delay_ms * hops) + (DIRECT_RETRY_TRACE_SCALE_HOPS - 1U)) / DIRECT_RETRY_TRACE_SCALE_HOPS; + return scaled > 0 ? scaled : 1; + } + + if (hops >= 6) { + return delay_ms; + } + uint32_t scaled = ((delay_ms * hops) + 5U) / 6U; + return scaled > 0 ? scaled : 1; +} + bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const { (void)packet; if (!_prefs.direct_retry_enabled) { return false; } + if (!_prefs.direct_retry_recent_enabled) { + return true; + } if (next_hop_hash == NULL || next_hop_hash_len == 0) { return true; } @@ -638,29 +941,63 @@ uint8_t MyMesh::getDirectRetryMaxAttempts(const mesh::Packet* packet) const { } uint32_t MyMesh::getDirectRetryAttemptDelay(const mesh::Packet* packet, uint8_t attempt_idx) { - (void)packet; - return _prefs.direct_retry_base_ms + ((uint32_t)attempt_idx * getDirectRetryAttemptStepMillis()); + uint32_t delay_ms = _prefs.direct_retry_base_ms + ((uint32_t)attempt_idx * getDirectRetryAttemptStepMillis()); + return scaleDirectRetryDelayForPath(packet, delay_ms); } -void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) { +static void formatDirectRetryTarget(char* dest, size_t dest_len, const uint8_t* target_hash, uint8_t target_hash_len) { + if (dest == NULL || dest_len == 0) { + return; + } + if (target_hash == NULL || target_hash_len == 0 || target_hash_len > MAX_HASH_SIZE) { + StrHelper::strncpy(dest, "-", dest_len); + return; + } + + size_t hex_len = (size_t)target_hash_len * 2; + if (dest_len <= hex_len) { + StrHelper::strncpy(dest, "-", dest_len); + return; + } + + mesh::Utils::toHex(dest, target_hash, target_hash_len); + dest[hex_len] = 0; +} + +void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt, + const uint8_t* target_hash, uint8_t target_hash_len, int16_t payload_type) { + char type_label[8]; + char target_label[(MAX_HASH_SIZE * 2) + 1]; + const char* route_label = packet != NULL ? (packet->isRouteDirect() ? "D" : "F") : "D"; + if (packet != NULL) { + snprintf(type_label, sizeof(type_label), "%u", (uint32_t)packet->getPayloadType()); + } else if (payload_type >= 0) { + snprintf(type_label, sizeof(type_label), "%u", (uint32_t)payload_type); + } else { + strcpy(type_label, "?"); + } + formatDirectRetryTarget(target_label, sizeof(target_label), target_hash, target_hash_len); + #if MESH_DEBUG - MESH_DEBUG_PRINTLN("direct retry %s attempt=%u delay=%lu type=%u route=%s", + MESH_DEBUG_PRINTLN("direct retry %s attempt=%u delay=%lu type=%s route=%s target=%s", event ? event : "?", (uint32_t)retry_attempt, (unsigned long)delay_millis, - packet ? (uint32_t)packet->getPayloadType() : 0, - packet && packet->isRouteDirect() ? "D" : "F"); + type_label, + route_label, + target_label); #endif if (_logging) { File f = openAppend(PACKET_LOG_FILE); if (f) { f.print(getLogDateTime()); - f.printf(": direct retry %s attempt=%u delay=%lu type=%u route=%s\n", + f.printf(": direct retry %s attempt=%u delay=%lu type=%s route=%s target=%s\n", event ? event : "?", (uint32_t)retry_attempt, (unsigned long)delay_millis, - packet ? (uint32_t)packet->getPayloadType() : 0, - packet && packet->isRouteDirect() ? "D" : "F"); + type_label, + route_label, + target_label); f.close(); } } @@ -690,6 +1027,582 @@ void MyMesh::onDirectRetrySucceeded(const uint8_t* next_hop_hash, uint8_t next_h } } +bool MyMesh::hasFloodRetryPrefixes() const { + for (int i = 0; i < FLOOD_RETRY_PREFIX_SLOTS; i++) { + const uint8_t* configured = _prefs.flood_retry_prefixes[i]; + if (configured[0] != 0 || configured[1] != 0 || configured[2] != 0) { + return true; + } + } + return false; +} + +bool MyMesh::floodRetryLastHopMatches(const mesh::Packet* packet) const { + if (packet == NULL || packet->getPathHashCount() == 0) { + return false; + } + + uint8_t hash_size = packet->getPathHashSize(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + return false; + } + + const uint8_t* heard_prefix = &packet->path[(packet->getPathHashCount() - 1) * hash_size]; + for (int i = 0; i < FLOOD_RETRY_PREFIX_SLOTS; i++) { + const uint8_t* configured = _prefs.flood_retry_prefixes[i]; + if ((configured[0] != 0 || configured[1] != 0 || configured[2] != 0) + && memcmp(configured, heard_prefix, hash_size) == 0) { + return true; + } + } + + return false; +} + +bool MyMesh::floodRetryPrefixMatches(const mesh::Packet* packet) const { + if (packet == NULL || packet->getPathHashCount() == 0) { + return false; + } + + uint8_t hash_size = packet->getPathHashSize(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + return false; + } + + const uint8_t* path = packet->path; + for (int hop = 0; hop < packet->getPathHashCount(); hop++) { + for (int i = 0; i < FLOOD_RETRY_PREFIX_SLOTS; i++) { + const uint8_t* configured = _prefs.flood_retry_prefixes[i]; + if ((configured[0] != 0 || configured[1] != 0 || configured[2] != 0) + && memcmp(configured, path, hash_size) == 0) { + return true; + } + } + path += hash_size; + } + + return false; +} + +bool MyMesh::floodRetryPrefixIgnored(const uint8_t* prefix, uint8_t prefix_len) const { + if (prefix == NULL || prefix_len == 0 || prefix_len > MAX_ROUTE_HASH_BYTES) { + return false; + } + for (int i = 0; i < FLOOD_RETRY_IGNORE_PREFIXES; i++) { + const uint8_t* ignored = _prefs.flood_retry_ignore_prefixes[i]; + if ((ignored[0] != 0 || ignored[1] != 0 || ignored[2] != 0) + && memcmp(ignored, prefix, prefix_len) == 0) { + return true; + } + } + return false; +} + +uint8_t MyMesh::floodRetryEffectivePathLength(const mesh::Packet* packet, uint8_t max_hops) const { + if (packet == NULL || !packet->isRouteFlood() || packet->getPathHashCount() == 0) { + return 0; + } + + uint8_t hash_size = packet->getPathHashSize(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + return packet->getPathHashCount(); + } + + uint8_t hop_count = packet->getPathHashCount(); + if (max_hops < hop_count) { + hop_count = max_hops; + } + + uint8_t effective_len = 0; + const uint8_t* path = packet->path; + for (uint8_t hop = 0; hop < hop_count; hop++) { + if (!floodRetryPrefixIgnored(path, hash_size)) { + effective_len++; + } + path += hash_size; + } + return effective_len; +} + +bool MyMesh::floodRetryPrefixFresh(const uint8_t* prefix, uint8_t prefix_len) const { + const SimpleMeshTables* tables = static_cast(getTables()); + if (tables == NULL) { + return false; + } + const auto* recent = tables->findRecentRepeaterByHash(prefix, prefix_len); + if (recent == NULL || recent->last_heard_millis == 0) { + return false; + } + return (uint32_t)(millis() - recent->last_heard_millis) <= 3600000UL; +} + +static const uint8_t FLOOD_RETRY_BRIDGE_OTHER_BUCKET = FLOOD_RETRY_BRIDGE_BUCKETS; + +static uint8_t floodRetryBucketMask(uint8_t bucket) { + if (bucket >= 8) { + return 0; + } + return (uint8_t)(1U << bucket); +} + +int MyMesh::floodRetryBucketForPrefix(const uint8_t* prefix, uint8_t prefix_len, bool require_fresh, + bool include_other) const { + if (prefix == NULL || prefix_len == 0 || prefix_len > MAX_ROUTE_HASH_BYTES) { + return -1; + } + if (floodRetryPrefixIgnored(prefix, prefix_len)) { + return -1; + } + if (require_fresh && !floodRetryPrefixFresh(prefix, prefix_len)) { + return -1; + } + for (int bucket = 0; bucket < FLOOD_RETRY_BRIDGE_BUCKETS; bucket++) { + for (int i = 0; i < FLOOD_RETRY_BUCKET_PREFIXES; i++) { + const uint8_t* configured = _prefs.flood_retry_bridge_buckets[bucket][i]; + if ((configured[0] != 0 || configured[1] != 0 || configured[2] != 0) + && memcmp(configured, prefix, prefix_len) == 0) { + return bucket; + } + } + } + if (include_other) { + return FLOOD_RETRY_BRIDGE_OTHER_BUCKET; + } + return -1; +} + +int MyMesh::floodRetryBucketForPathHop(const uint8_t* prefix, uint8_t prefix_len, uint8_t hop, + uint8_t progress_marker) const { + return floodRetryBucketForPrefix(prefix, prefix_len, hop < progress_marker, true); +} + +int MyMesh::floodRetrySourceBucket(const mesh::Packet* packet) const { + if (packet == NULL) { + return -1; + } + uint8_t hash_size = packet->getPathHashSize(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + return -1; + } + if (packet->getPathHashCount() < 2) { + return FLOOD_RETRY_BRIDGE_OTHER_BUCKET; + } + const uint8_t* source_prefix = &packet->path[(packet->getPathHashCount() - 2) * hash_size]; + return floodRetryBucketForPrefix(source_prefix, hash_size, true, true); +} + +uint8_t MyMesh::floodRetryBridgeTargetMask(uint8_t source_bucket) const { + uint8_t mask = 0; + for (int bucket = 0; bucket < FLOOD_RETRY_BRIDGE_BUCKETS; bucket++) { + if (bucket == source_bucket) { + continue; + } + for (int i = 0; i < FLOOD_RETRY_BUCKET_PREFIXES; i++) { + const uint8_t* configured = _prefs.flood_retry_bridge_buckets[bucket][i]; + if ((configured[0] != 0 || configured[1] != 0 || configured[2] != 0) + && !floodRetryPrefixIgnored(configured, FLOOD_RETRY_PREFIX_LEN) + && floodRetryPrefixFresh(configured, FLOOD_RETRY_PREFIX_LEN)) { + mask |= floodRetryBucketMask((uint8_t)bucket); + break; + } + } + } + if (source_bucket != FLOOD_RETRY_BRIDGE_OTHER_BUCKET) { + mask |= floodRetryBucketMask(FLOOD_RETRY_BRIDGE_OTHER_BUCKET); + } + return mask; +} + +uint8_t MyMesh::floodRetryBridgeHeardMask(const mesh::Packet* packet, uint8_t source_bucket, + uint8_t progress_marker) const { + if (packet == NULL || packet->getPathHashCount() == 0) { + return 0; + } + uint8_t hash_size = packet->getPathHashSize(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + return 0; + } + + uint8_t mask = 0; + const uint8_t* path = packet->path; + for (int hop = 0; hop < packet->getPathHashCount(); hop++) { + if (progress_marker > 0 && hop == progress_marker - 1) { + path += hash_size; + continue; + } + int bucket = floodRetryBucketForPathHop(path, hash_size, (uint8_t)hop, progress_marker); + if (bucket >= 0 && bucket != source_bucket) { + mask |= floodRetryBucketMask((uint8_t)bucket); + } + path += hash_size; + } + return mask; +} + +MyMesh::FloodRetryBridgeState* MyMesh::floodRetryBridgeStateFor(const mesh::Packet* packet, bool create) const { + if (packet == NULL) { + return NULL; + } + + uint8_t key[MAX_HASH_SIZE]; + packet->calculatePacketHash(key); + FloodRetryBridgeState* free_slot = NULL; + for (int i = 0; i < MAX_FLOOD_RETRY_SLOTS; i++) { + if (flood_retry_bridge_states[i].active + && memcmp(flood_retry_bridge_states[i].key, key, MAX_HASH_SIZE) == 0) { + return &flood_retry_bridge_states[i]; + } + if (!flood_retry_bridge_states[i].active && free_slot == NULL) { + free_slot = &flood_retry_bridge_states[i]; + } + } + if (!create || free_slot == NULL) { + return NULL; + } + + int source_bucket = floodRetrySourceBucket(packet); + if (source_bucket < 0) { + return NULL; + } + + uint8_t target_mask = floodRetryBridgeTargetMask((uint8_t)source_bucket); + if (target_mask == 0) { + return NULL; + } + + uint8_t progress_marker = packet->getPathHashCount(); + uint8_t heard_mask = floodRetryBridgeHeardMask(packet, (uint8_t)source_bucket, progress_marker) & target_mask; + if ((heard_mask & target_mask) == target_mask) { + return NULL; + } + + memset(free_slot, 0, sizeof(*free_slot)); + memcpy(free_slot->key, key, sizeof(free_slot->key)); + free_slot->source_bucket = (uint8_t)source_bucket; + free_slot->target_mask = target_mask; + free_slot->heard_mask = heard_mask; + free_slot->progress_marker = progress_marker; + free_slot->active = true; + return free_slot; +} + +bool MyMesh::allowFloodRetry(const mesh::Packet* packet) const { + if (_prefs.disable_fwd || constrain(_prefs.flood_retry_attempts, 0, 15) == 0) { + return false; + } + if (packet != NULL && packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && !_prefs.flood_retry_advert_enabled) { + return false; + } + if (!_prefs.flood_retry_bridge_enabled) { + return true; + } + FloodRetryBridgeState* state = floodRetryBridgeStateFor(packet, true); + if (state == NULL) { + return false; + } + if ((state->heard_mask & state->target_mask) == state->target_mask) { + state->active = false; + return false; + } + return true; +} + +void MyMesh::clearFloodRetryBridgeState(const mesh::Packet* packet) { + FloodRetryBridgeState* state = floodRetryBridgeStateFor(packet, false); + if (state != NULL) { + state->active = false; + } +} + +void MyMesh::refreshFloodRetryHeardRecent(const mesh::Packet* packet) { + if (packet == NULL || !packet->isRouteFlood() || packet->getPathHashCount() == 0) { + return; + } + + uint8_t hash_size = packet->getPathHashSize(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + return; + } + + SimpleMeshTables* tables = static_cast(getTables()); + if (tables == NULL) { + return; + } + const uint8_t* path = packet->path; + if (_prefs.flood_retry_bridge_enabled) { + FloodRetryBridgeState* state = floodRetryBridgeStateFor(packet, false); + if (state != NULL) { + for (int hop = 0; hop < packet->getPathHashCount(); hop++) { + if (state->progress_marker > 0 && hop == state->progress_marker - 1) { + path += hash_size; + continue; + } + int bucket = floodRetryBucketForPathHop(path, hash_size, (uint8_t)hop, state->progress_marker); + uint8_t bucket_mask = bucket >= 0 ? floodRetryBucketMask((uint8_t)bucket) : 0; + if (bucket >= 0 && bucket != state->source_bucket && (state->target_mask & bucket_mask)) { + tables->setRecentRepeater(path, hash_size, packet->_snr, false, true); + } + path += hash_size; + } + return; + } + } + + const uint8_t* heard_prefix = &packet->path[(packet->getPathHashCount() - 1) * hash_size]; + tables->setRecentRepeater(heard_prefix, hash_size, packet->_snr, false, true); +} + +void MyMesh::formatFloodRetryPath(char* dest, size_t dest_len, const mesh::Packet* packet) const { + if (dest == NULL || dest_len == 0) { + return; + } + dest[0] = 0; + + if (packet == NULL || packet->getPathHashCount() == 0) { + StrHelper::strncpy(dest, "-", dest_len); + return; + } + + uint8_t hash_size = packet->getPathHashSize(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + StrHelper::strncpy(dest, "invalid", dest_len); + return; + } + + char* out = dest; + size_t remaining = dest_len; + const uint8_t* path = packet->path; + for (int hop = 0; hop < packet->getPathHashCount(); hop++) { + size_t needed = (hop > 0 ? 1 : 0) + ((size_t)hash_size * 2) + 1; + if (remaining < needed) { + if (remaining > 4) { + strcpy(out, "..."); + } + return; + } + if (hop > 0) { + *out++ = '>'; + remaining--; + } + mesh::Utils::toHex(out, path, hash_size); + out += (size_t)hash_size * 2; + remaining -= (size_t)hash_size * 2; + path += hash_size; + } +} + +bool MyMesh::formatFloodRetryHeard(char* dest, size_t dest_len, const mesh::Packet* packet) const { + if (dest == NULL || dest_len == 0 || packet == NULL || packet->getPathHashCount() == 0) { + return false; + } + dest[0] = 0; + + uint8_t hash_size = packet->getPathHashSize(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + return false; + } + + char* out = dest; + size_t remaining = dest_len; + bool first = true; + + if (_prefs.flood_retry_bridge_enabled) { + FloodRetryBridgeState* state = floodRetryBridgeStateFor(packet, false); + if (state == NULL) { + return false; + } + const uint8_t* path = packet->path; + for (int hop = 0; hop < packet->getPathHashCount(); hop++) { + if (state->progress_marker > 0 && hop == state->progress_marker - 1) { + path += hash_size; + continue; + } + int bucket = floodRetryBucketForPathHop(path, hash_size, (uint8_t)hop, state->progress_marker); + uint8_t bucket_mask = bucket >= 0 ? floodRetryBucketMask((uint8_t)bucket) : 0; + if (bucket >= 0 && bucket != state->source_bucket && (state->target_mask & bucket_mask)) { + char bucket_label[8]; + if ((uint8_t)bucket == FLOOD_RETRY_BRIDGE_OTHER_BUCKET) { + strcpy(bucket_label, "other"); + } else { + snprintf(bucket_label, sizeof(bucket_label), "b%d", bucket + 1); + } + size_t needed = (first ? 0 : 1) + strlen(bucket_label) + 1 + ((size_t)hash_size * 2) + 1; + if (remaining < needed) { + if (remaining > 4) { + strcpy(out, "..."); + } + return dest[0] != 0; + } + if (!first) { + *out++ = ','; + remaining--; + } + int n = snprintf(out, remaining, "%s:", bucket_label); + if (n < 0 || (size_t)n >= remaining) { + return dest[0] != 0; + } + out += n; + remaining -= n; + mesh::Utils::toHex(out, path, hash_size); + out += (size_t)hash_size * 2; + remaining -= (size_t)hash_size * 2; + first = false; + } + path += hash_size; + } + return dest[0] != 0; + } + + const uint8_t* heard_prefix = &packet->path[(packet->getPathHashCount() - 1) * hash_size]; + if (remaining < ((size_t)hash_size * 2) + 1) { + return false; + } + mesh::Utils::toHex(out, heard_prefix, hash_size); + return true; +} + +void MyMesh::onFloodRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) { + if (event == NULL || packet == NULL) { + return; + } + + bool clear_bridge_state = _prefs.flood_retry_bridge_enabled + && (strcmp(event, "good") == 0 || strcmp(event, "failure") == 0 || strcmp(event, "failed_all_tries") == 0 + || strncmp(event, "dropped_", 8) == 0); + + if (clear_bridge_state && strcmp(event, "failure") == 0) { + clearFloodRetryBridgeState(packet); + } + + if (strcmp(event, "failure") == 0) { + return; + } + + const char* time_label = "time_ms"; + if (strcmp(event, "queued") == 0 || strcmp(event, "dropped_queue_full") == 0) { + time_label = "wait_ms"; + } else if (strcmp(event, "resent") == 0 || strcmp(event, "failed_all_tries") == 0 + || strcmp(event, "failure") == 0 || strncmp(event, "dropped_", 8) == 0) { + time_label = "elapsed_ms"; + } else if (strcmp(event, "good") == 0) { + time_label = "echo_ms"; + } + + char path_log[208]; + char heard_log[96]; + char heard_suffix[112]; + formatFloodRetryPath(path_log, sizeof(path_log), packet); + heard_suffix[0] = 0; + if (strcmp(event, "good") == 0 && formatFloodRetryHeard(heard_log, sizeof(heard_log), packet)) { + refreshFloodRetryHeardRecent(packet); + snprintf(heard_suffix, sizeof(heard_suffix), ", heard=%s", heard_log); + } + + MESH_DEBUG_PRINTLN("%s flood retry %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%u, path=%s%s, %s=%lu)", + getLogDateTime(), + event, + (unsigned int)retry_attempt, + (uint32_t)packet->getPayloadType(), + packet->isRouteDirect() ? "D" : "F", + (uint32_t)packet->payload_len, + (unsigned int)packet->getPathHashCount(), + path_log, + heard_suffix, + time_label, + (unsigned long)delay_millis); + + if (_logging) { + File f = openAppend(PACKET_LOG_FILE); + if (f) { + f.print(getLogDateTime()); + f.printf(": FLOOD RETRY %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%u, path=%s%s, %s=%lu)\n", + event, + (unsigned int)retry_attempt, + (uint32_t)packet->getPayloadType(), + packet->isRouteDirect() ? "D" : "F", + (uint32_t)packet->payload_len, + (unsigned int)packet->getPathHashCount(), + path_log, + heard_suffix, + time_label, + (unsigned long)delay_millis); + f.close(); + } + } + + if (clear_bridge_state) { + clearFloodRetryBridgeState(packet); + } +} + +bool MyMesh::hasFloodRetryTargetPrefix(const mesh::Packet* packet) const { + if (_prefs.flood_retry_bridge_enabled) { + return false; + } + return floodRetryPrefixMatches(packet); +} + +uint8_t MyMesh::getFloodRetryMaxPathLength(const mesh::Packet* packet) const { + uint8_t gate = _prefs.flood_retry_max_path; + if (gate == FLOOD_RETRY_PATH_GATE_DISABLED) { + return FLOOD_RETRY_PATH_GATE_DISABLED; + } + if (gate > 63) { + gate = FLOOD_RETRY_ROOFTOP_MAX_PATH; + } + + uint8_t raw_hops = packet != NULL ? packet->getPathHashCount() : 0; + uint8_t effective_hops = floodRetryEffectivePathLength(packet); + uint8_t ignored_hops = raw_hops > effective_hops ? raw_hops - effective_hops : 0; + uint16_t adjusted_gate = (uint16_t)gate + ignored_hops; + return adjusted_gate > 63 ? 63 : (uint8_t)adjusted_gate; +} + +uint8_t MyMesh::getFloodRetryMaxAttempts(const mesh::Packet* packet) const { + if (_prefs.disable_fwd) { + return 0; + } + + uint8_t attempts = constrain(_prefs.flood_retry_attempts, 0, 15); + uint16_t scaled_attempts = attempts; + uint8_t hops = packet != NULL ? packet->getPathHashCount() : 0; + if (hops == 1) { + scaled_attempts = (uint16_t)attempts * 2U; + } else if (hops == 2) { + scaled_attempts = (((uint16_t)attempts * 3U) + 1U) / 2U; + } + return scaled_attempts > 15 ? 15 : (uint8_t)scaled_attempts; +} + +bool MyMesh::isFloodRetryEchoTarget(const mesh::Packet* packet, uint8_t progress_marker) const { + if (packet == NULL || !packet->isRouteFlood()) { + return false; + } + if (_prefs.flood_retry_bridge_enabled) { + FloodRetryBridgeState* state = floodRetryBridgeStateFor(packet, false); + if (state == NULL) { + return false; + } + state->heard_mask |= floodRetryBridgeHeardMask(packet, state->source_bucket, state->progress_marker) & state->target_mask; + return (state->heard_mask & state->target_mask) == state->target_mask; + } + if (packet->getPathHashCount() == 0) { + return false; + } + uint8_t hash_size = packet->getPathHashSize(); + if (hash_size == 0 || hash_size > MAX_ROUTE_HASH_BYTES) { + return false; + } + const uint8_t* heard_prefix = &packet->path[(packet->getPathHashCount() - 1) * hash_size]; + if (floodRetryPrefixIgnored(heard_prefix, hash_size)) { + return false; + } + if (hasFloodRetryPrefixes()) { + return floodRetryLastHopMatches(packet); + } + return true; +} + static void formatLocalSnrX4(char* dest, size_t dest_len, int16_t snr_x4) { int16_t v = snr_x4; const char* sign = ""; @@ -698,6 +1611,10 @@ static void formatLocalSnrX4(char* dest, size_t dest_len, int16_t snr_x4) { v = -v; } snprintf(dest, dest_len, "%s%d.%02d", sign, v / 4, (v % 4) * 25); + size_t len = strlen(dest); + if (len > 3 && dest[len - 1] == '0') { + dest[len - 1] = 0; + } } void MyMesh::formatRecentRepeatersReply(char *reply, int page) { @@ -712,12 +1629,12 @@ void MyMesh::formatRecentRepeatersReply(char *reply, int page) { return; } - const int page_size = 4; + const int page_size = 10; int pages = (count + page_size - 1) / page_size; if (page < 1) page = 1; if (page > pages) page = pages; - int len = snprintf(reply, 160, "> %d/%d ", page, pages); + int len = snprintf(reply, 160, "> %d/%d", page, pages); int start = (page - 1) * page_size; for (int i = 0; i < page_size && len < 150; i++) { const SimpleMeshTables::RecentRepeaterInfo* info = tables->getRecentRepeaterBySortedIdx(start + i); @@ -727,13 +1644,39 @@ void MyMesh::formatRecentRepeatersReply(char *reply, int page) { mesh::Utils::toHex(prefix, info->prefix, info->prefix_len); prefix[info->prefix_len * 2] = 0; formatLocalSnrX4(snr, sizeof(snr), info->snr_x4); - len += snprintf(&reply[len], 160 - len, "%s%s,%s", - i == 0 ? "" : " ", + len += snprintf(&reply[len], 160 - len, "\n%s,%s%s", prefix, + snr[0] == '-' ? "" : " ", snr); } } +void MyMesh::printRecentRepeatersSerial() { + const SimpleMeshTables* tables = static_cast(getTables()); + if (tables == NULL) { + Serial.println("Error: unsupported"); + return; + } + + int count = tables->getRecentRepeaterCount(); + Serial.printf("Recent repeaters (%d):\n", count); + if (count <= 0) { + Serial.println("-none-"); + return; + } + + for (int i = 0; i < count; i++) { + const SimpleMeshTables::RecentRepeaterInfo* info = tables->getRecentRepeaterBySortedIdx(i); + if (info == NULL) break; + char prefix[MAX_ROUTE_HASH_BYTES * 2 + 1]; + char snr[12]; + mesh::Utils::toHex(prefix, info->prefix, info->prefix_len); + prefix[info->prefix_len * 2] = 0; + formatLocalSnrX4(snr, sizeof(snr), info->snr_x4); + Serial.printf("%s,%s%s\n", prefix, snr[0] == '-' ? "" : " ", snr); + } +} + bool MyMesh::setRecentRepeater(const uint8_t* prefix, uint8_t prefix_len, int8_t snr_x4) { SimpleMeshTables* tables = static_cast(getTables()); return tables != NULL && tables->setRecentRepeater(prefix, prefix_len, snr_x4); @@ -1062,12 +2005,16 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc last_millis = 0; uptime_millis = 0; next_local_advert = next_flood_advert = 0; + next_battery_alert_check = 0; + last_battery_alert_sent = 0; + battery_alert_sent = false; dirty_contacts_expiry = 0; - set_radio_at = revert_radio_at = 0; active_sf = 0; active_cr = 0; + memset(scheduled_radio_settings, 0, sizeof(scheduled_radio_settings)); _logging = false; region_load_active = false; + memset(flood_retry_bridge_states, 0, sizeof(flood_retry_bridge_states)); #if MAX_NEIGHBOURS memset(neighbours, 0, sizeof(neighbours)); @@ -1076,7 +2023,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc // defaults memset(&_prefs, 0, sizeof(_prefs)); _prefs.airtime_factor = 1.0; - _prefs.rx_delay_base = 0.0f; // turn off by default, was 10.0; + _prefs.rx_delay_base = DEFAULT_RX_DELAY_BASE; _prefs.tx_delay_factor = 0.5f; // was 0.25f _prefs.direct_tx_delay_factor = 0.3f; // was 0.2 StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); @@ -1088,13 +2035,17 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.bw = LORA_BW; _prefs.cr = LORA_CR; _prefs.tx_power_dbm = LORA_TX_POWER; - _prefs.advert_interval = 1; // default to 2 minutes for NEW installs - _prefs.flood_advert_interval = 47; // 47 hours + _prefs.advert_interval = DEFAULT_ADVERT_INTERVAL_MINUTES / 2; + _prefs.flood_advert_interval = DEFAULT_FLOOD_ADVERT_INTERVAL_HOURS; _prefs.flood_max = 64; _prefs.flood_max_unscoped = 64; _prefs.flood_max_advert = 8; _prefs.interference_threshold = 0; // disabled _prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on') + _prefs.agc_reset_interval = DEFAULT_AGC_RESET_INTERVAL_SECONDS / 4; + _prefs.multi_acks = DEFAULT_MULTI_ACKS; + _prefs.path_hash_mode = DEFAULT_PATH_HASH_MODE; + _prefs.loop_detect = DEFAULT_LOOP_DETECT; _prefs.retry_preset = RETRY_PRESET_ROOFTOP; _prefs.direct_retry_attempts = DIRECT_RETRY_ROOFTOP_COUNT; _prefs.direct_retry_base_ms = DIRECT_RETRY_ROOFTOP_BASE_MS; @@ -1108,6 +2059,14 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.direct_retry_cr_enabled = 1; _prefs.direct_retry_prefs_magic[0] = DIRECT_RETRY_PREFS_MAGIC_0; _prefs.direct_retry_prefs_magic[1] = DIRECT_RETRY_PREFS_MAGIC_1; + _prefs.direct_retry_recent_enabled = DIRECT_RETRY_RECENT_DEFAULT; + _prefs.flood_retry_attempts = FLOOD_RETRY_ROOFTOP_COUNT; + _prefs.flood_retry_max_path = FLOOD_RETRY_ROOFTOP_MAX_PATH; + _prefs.flood_retry_bridge_enabled = 0; + _prefs.flood_retry_advert_enabled = FLOOD_RETRY_ADVERT_DEFAULT; + _prefs.battery_alert_enabled = 0; + _prefs.battery_alert_low_percent = BATTERY_ALERT_LOW_PERCENT_DEFAULT; + _prefs.battery_alert_critical_percent = BATTERY_ALERT_CRITICAL_PERCENT_DEFAULT; // bridge defaults _prefs.bridge_enabled = 1; // enabled @@ -1175,9 +2134,7 @@ void MyMesh::begin(FILESYSTEM *fs) { } #endif - radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); - active_sf = _prefs.sf; - active_cr = _prefs.cr; + applySavedRadioParams(); radio_driver.setTxPower(_prefs.tx_power_dbm); radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); @@ -1206,14 +2163,574 @@ void MyMesh::sendFloodScoped(const TransportKey& scope, mesh::Packet* pkt, uint3 } } -void MyMesh::applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) { - set_radio_at = futureMillis(2000); // give CLI reply some time to be sent back, before applying temp radio params - pending_freq = freq; - pending_bw = bw; - pending_sf = sf; - pending_cr = cr; +bool MyMesh::sendRepeatersFloodText(const char* text) { + if (text == NULL || *text == 0) return false; - revert_radio_at = futureMillis(2000 + timeout_mins * 60 * 1000); // schedule when to revert radio params + mesh::GroupChannel channel; + if (!buildRepeatersChannel(channel)) { + return false; + } + + uint8_t temp[MAX_PACKET_PAYLOAD]; + uint32_t timestamp = getRTCClock()->getCurrentTimeUnique(); + memcpy(temp, ×tamp, 4); + temp[4] = (TXT_TYPE_PLAIN << 2); + + const size_t max_data_len = MAX_PACKET_PAYLOAD - CIPHER_BLOCK_SIZE; + const size_t prefix_cap = max_data_len > 5 ? max_data_len - 5 + 1 : 0; + int prefix_written = prefix_cap > 0 + ? snprintf((char*)&temp[5], prefix_cap, "%s: ", _prefs.node_name) + : -1; + if (prefix_written < 0) { + return false; + } + + size_t prefix_len = (size_t)prefix_written; + if (prefix_len >= prefix_cap) { + prefix_len = prefix_cap - 1; + } + + size_t text_len = strlen(text); + size_t max_text_len = max_data_len - 5 - prefix_len; + if (text_len > max_text_len) { + text_len = max_text_len; + } + memcpy(&temp[5 + prefix_len], text, text_len); + + auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_TXT, channel, temp, 5 + prefix_len + text_len); + if (pkt == NULL) { + return false; + } + + sendFloodScoped(default_scope, pkt, 0, _prefs.path_hash_mode + 1); + return true; +} + +void MyMesh::checkBatteryAlert() { + if (!_prefs.battery_alert_enabled) { + battery_alert_sent = false; + return; + } + + if (next_battery_alert_check && !millisHasNowPassed(next_battery_alert_check)) { + return; + } + next_battery_alert_check = futureMillis(LOW_BATTERY_CHECK_INTERVAL); + + uint16_t batt_mv = board.getBattMilliVolts(); + uint8_t batt_pct = batteryPercentFromMilliVolts(batt_mv); + if (batt_mv <= LOW_BATTERY_MIN_VALID_MV || batt_pct >= _prefs.battery_alert_low_percent) { + battery_alert_sent = false; + return; + } + + unsigned long interval = batt_pct <= _prefs.battery_alert_critical_percent + ? LOW_BATTERY_CRITICAL_INTERVAL + : LOW_BATTERY_WARN_INTERVAL; + if (battery_alert_sent && !millisHasNowPassed(last_battery_alert_sent + interval)) { + return; + } + + char text[96]; + snprintf(text, sizeof(text), "LOW BATTERY %u%% (%u mV)", (uint32_t)batt_pct, (uint32_t)batt_mv); + if (sendRepeatersFloodText(text)) { + battery_alert_sent = true; + last_battery_alert_sent = millis(); + } +} + +void MyMesh::applyRadioParams(float freq, float bw, uint8_t sf, uint8_t cr) { + radio_driver.setParams(freq, bw, sf, cr); + active_sf = sf; + active_cr = cr; +} + +void MyMesh::applySavedRadioParams() { + applyRadioParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); +} + +bool MyMesh::hasStartedScheduledTempRadio() const { + for (int i = 0; i < MAX_SCHEDULED_RADIO_SETTINGS; i++) { + const ScheduledRadioSetting& setting = scheduled_radio_settings[i]; + if (setting.active && setting.temporary && setting.started) { + return true; + } + } + return false; +} + +int MyMesh::findFreeScheduledRadioSlot() const { + for (int i = 0; i < MAX_SCHEDULED_RADIO_SETTINGS; i++) { + if (!scheduled_radio_settings[i].active) { + return i; + } + } + return -1; +} + +int MyMesh::countScheduledRadioSettings(bool temporary) const { + int count = 0; + for (int i = 0; i < MAX_SCHEDULED_RADIO_SETTINGS; i++) { + const ScheduledRadioSetting& setting = scheduled_radio_settings[i]; + if (setting.active && setting.temporary == temporary) { + count++; + } + } + return count; +} + +int MyMesh::findScheduledRadioSettingByIndex(bool temporary, int wanted) const { + bool used[MAX_SCHEDULED_RADIO_SETTINGS] = {}; + for (int rank = 1; rank <= wanted; rank++) { + int best = -1; + for (int i = 0; i < MAX_SCHEDULED_RADIO_SETTINGS; i++) { + const ScheduledRadioSetting& setting = scheduled_radio_settings[i]; + if (!setting.active || setting.temporary != temporary || used[i]) { + continue; + } + if (best < 0 || setting.start_time < scheduled_radio_settings[best].start_time + || (setting.start_time == scheduled_radio_settings[best].start_time && i < best)) { + best = i; + } + } + if (best < 0) { + return -1; + } + used[best] = true; + if (rank == wanted) { + return best; + } + } + return -1; +} + +int MyMesh::getScheduledRadioSettingIndex(bool temporary, int slot_idx) const { + int count = countScheduledRadioSettings(temporary); + for (int i = 1; i <= count; i++) { + if (findScheduledRadioSettingByIndex(temporary, i) == slot_idx) { + return i; + } + } + return -1; +} + +bool MyMesh::scheduledRadioConflicts(bool temporary, uint32_t start_time, uint32_t end_time) const { + for (int i = 0; i < MAX_SCHEDULED_RADIO_SETTINGS; i++) { + const ScheduledRadioSetting& setting = scheduled_radio_settings[i]; + if (!setting.active) { + continue; + } + if (temporary) { + if (setting.temporary && start_time < setting.end_time && end_time > setting.start_time) { + return true; + } + if (!setting.temporary && setting.start_time >= start_time && setting.start_time < end_time) { + return true; + } + } else { + if (!setting.temporary && setting.start_time == start_time) { + return true; + } + if (setting.temporary && start_time >= setting.start_time && start_time < setting.end_time) { + return true; + } + } + } + return false; +} + +void MyMesh::clearScheduledRadioSetting(int idx, bool restore_if_started) { + if (idx < 0 || idx >= MAX_SCHEDULED_RADIO_SETTINGS) { + return; + } + bool restore_radio = restore_if_started + && scheduled_radio_settings[idx].active + && scheduled_radio_settings[idx].temporary + && scheduled_radio_settings[idx].started; + scheduled_radio_settings[idx].active = false; + scheduled_radio_settings[idx].started = false; + if (restore_radio && !hasStartedScheduledTempRadio()) { + applySavedRadioParams(); + } +} + +void MyMesh::formatScheduledRadioDuration(char* dest, size_t dest_len, uint32_t target_time) const { + uint32_t now = getRTCClock()->getCurrentTime(); + if (target_time <= now) { + StrHelper::strncpy(dest, "now", dest_len); + return; + } + + uint32_t seconds = target_time - now; + uint32_t days = seconds / 86400; + seconds %= 86400; + uint32_t hours = seconds / 3600; + seconds %= 3600; + uint32_t minutes = seconds / 60; + seconds %= 60; + + if (days > 0) { + snprintf(dest, dest_len, "%lud%luh", (unsigned long)days, (unsigned long)hours); + } else if (hours > 0) { + snprintf(dest, dest_len, "%luh%lum", (unsigned long)hours, (unsigned long)minutes); + } else if (minutes > 0) { + snprintf(dest, dest_len, "%lum%lus", (unsigned long)minutes, (unsigned long)seconds); + } else { + snprintf(dest, dest_len, "%lus", (unsigned long)seconds); + } +} + +void MyMesh::formatRadioParamTuple(char* dest, size_t dest_len, const ScheduledRadioSetting& setting) const { + char freq[16]; + char bw[16]; + formatFixed3(freq, sizeof(freq), setting.freq); + StrHelper::strncpy(bw, StrHelper::ftoa3(setting.bw), sizeof(bw)); + snprintf(dest, dest_len, "%s,%s,%u,%u", freq, bw, (uint32_t)setting.sf, (uint32_t)setting.cr); +} + +void MyMesh::formatScheduledRadioSetting(char* reply, int setting_idx, int display_idx) const { + const ScheduledRadioSetting& setting = scheduled_radio_settings[setting_idx]; + char params[40]; + char delay[16]; + formatRadioParamTuple(params, sizeof(params), setting); + + if (setting.temporary) { + if (setting.started) { + formatScheduledRadioDuration(delay, sizeof(delay), setting.end_time); + snprintf(reply, 160, "> %d:%s@%lu-%lu active ends in %s", + display_idx, + params, + (unsigned long)setting.start_time, + (unsigned long)setting.end_time, + delay); + } else { + formatScheduledRadioDuration(delay, sizeof(delay), setting.start_time); + snprintf(reply, 160, "> %d:%s@%lu-%lu starts in %s", + display_idx, + params, + (unsigned long)setting.start_time, + (unsigned long)setting.end_time, + delay); + } + } else { + formatScheduledRadioDuration(delay, sizeof(delay), setting.start_time); + snprintf(reply, 160, "> %d:%s@%lu in %s", + display_idx, + params, + (unsigned long)setting.start_time, + delay); + } +} + +void MyMesh::addScheduledRadioParams(bool temporary, float freq, float bw, uint8_t sf, uint8_t cr, + uint32_t start_time, uint32_t end_time, char* reply) { + uint32_t now = getRTCClock()->getCurrentTime(); + if (!isValidScheduledRadioParams(freq, bw, sf, cr)) { + strcpy(reply, "Error, invalid radio params"); + return; + } + if (start_time <= now) { + strcpy(reply, "Error: start is in the past"); + return; + } + if (temporary && end_time <= now) { + strcpy(reply, "Error: end is in the past"); + return; + } + if (temporary && end_time <= start_time) { + strcpy(reply, "Error: end must be after start"); + return; + } + if (countScheduledRadioSettings(temporary) >= MAX_SCHEDULED_RADIO_SETTINGS_PER_TYPE) { + snprintf(reply, 160, "Error: max %d queued", MAX_SCHEDULED_RADIO_SETTINGS_PER_TYPE); + return; + } + if (scheduledRadioConflicts(temporary, start_time, end_time)) { + strcpy(reply, "Error: schedule conflict"); + return; + } + + int slot = findFreeScheduledRadioSlot(); + if (slot < 0) { + strcpy(reply, "Error: queue full"); + return; + } + + scheduled_radio_settings[slot].active = true; + scheduled_radio_settings[slot].temporary = temporary; + scheduled_radio_settings[slot].started = false; + scheduled_radio_settings[slot].freq = freq; + scheduled_radio_settings[slot].bw = bw; + scheduled_radio_settings[slot].sf = sf; + scheduled_radio_settings[slot].cr = cr; + scheduled_radio_settings[slot].start_time = start_time; + scheduled_radio_settings[slot].end_time = temporary ? end_time : 0; + + char delay[16]; + formatScheduledRadioDuration(delay, sizeof(delay), start_time); + snprintf(reply, 160, "OK - %s %d in %s", + temporary ? "tempradioat" : "radioat", + getScheduledRadioSettingIndex(temporary, slot), + delay); +} + +void MyMesh::formatScheduledRadioParams(bool temporary, const char* selector, char* reply) { + if (selectorIsEmpty(selector) || selectorIsAll(selector)) { + int count = countScheduledRadioSettings(temporary); + if (count == 0) { + strcpy(reply, "> -none-"); + return; + } + + int len = snprintf(reply, 160, "> "); + for (int display_idx = 1; display_idx <= count && len < 159; display_idx++) { + int idx = findScheduledRadioSettingByIndex(temporary, display_idx); + if (idx < 0) { + break; + } + char params[40]; + formatRadioParamTuple(params, sizeof(params), scheduled_radio_settings[idx]); + int written; + if (temporary) { + written = snprintf(&reply[len], 160 - len, "%s%d:%s@%lu-%lu", + display_idx == 1 ? "" : " ", + display_idx, + params, + (unsigned long)scheduled_radio_settings[idx].start_time, + (unsigned long)scheduled_radio_settings[idx].end_time); + } else { + written = snprintf(&reply[len], 160 - len, "%s%d:%s@%lu", + display_idx == 1 ? "" : " ", + display_idx, + params, + (unsigned long)scheduled_radio_settings[idx].start_time); + } + if (written < 0 || written >= 160 - len) { + reply[159] = 0; + break; + } + len += written; + } + return; + } + + int wanted = 0; + if (!parsePositiveSelector(selector, wanted)) { + strcpy(reply, temporary ? "Error, use: get tempradioat [n]" : "Error, use: get radioat [n]"); + return; + } + + int idx = findScheduledRadioSettingByIndex(temporary, wanted); + if (idx < 0) { + strcpy(reply, "Error: not found"); + return; + } + formatScheduledRadioSetting(reply, idx, wanted); +} + +void MyMesh::deleteScheduledRadioParams(bool temporary, const char* selector, char* reply) { + if (selectorIsEmpty(selector) || selectorIsAll(selector)) { + int deleted = 0; + bool restore_radio = false; + for (int i = 0; i < MAX_SCHEDULED_RADIO_SETTINGS; i++) { + ScheduledRadioSetting& setting = scheduled_radio_settings[i]; + if (setting.active && setting.temporary == temporary) { + restore_radio = restore_radio || (setting.temporary && setting.started); + setting.active = false; + setting.started = false; + deleted++; + } + } + if (restore_radio && !hasStartedScheduledTempRadio()) { + applySavedRadioParams(); + } + snprintf(reply, 160, "OK - deleted %d", deleted); + return; + } + + int wanted = 0; + if (!parsePositiveSelector(selector, wanted)) { + strcpy(reply, temporary ? "Error, use: del tempradioat [n]" : "Error, use: del radioat [n]"); + return; + } + + int idx = findScheduledRadioSettingByIndex(temporary, wanted); + if (idx < 0) { + strcpy(reply, "Error: not found"); + return; + } + clearScheduledRadioSetting(idx, true); + strcpy(reply, "OK"); +} + +void MyMesh::processScheduledRadioSettings() { + uint32_t now = getRTCClock()->getCurrentTime(); + bool saved_params_changed = false; + bool temp_ended = false; + + while (true) { + int due_idx = -1; + for (int i = 0; i < MAX_SCHEDULED_RADIO_SETTINGS; i++) { + const ScheduledRadioSetting& setting = scheduled_radio_settings[i]; + if (!setting.active || setting.temporary || now < setting.start_time) { + continue; + } + if (due_idx < 0 || setting.start_time < scheduled_radio_settings[due_idx].start_time + || (setting.start_time == scheduled_radio_settings[due_idx].start_time && i < due_idx)) { + due_idx = i; + } + } + if (due_idx < 0) { + break; + } + + ScheduledRadioSetting& setting = scheduled_radio_settings[due_idx]; + _prefs.freq = setting.freq; + _prefs.bw = setting.bw; + _prefs.sf = setting.sf; + _prefs.cr = setting.cr; + savePrefs(); + setting.active = false; + setting.started = false; + saved_params_changed = true; + } + + if (saved_params_changed && !hasStartedScheduledTempRadio()) { + applySavedRadioParams(); + } + + for (int i = 0; i < MAX_SCHEDULED_RADIO_SETTINGS; i++) { + ScheduledRadioSetting& setting = scheduled_radio_settings[i]; + if (setting.active && setting.temporary && setting.started && now >= setting.end_time) { + setting.active = false; + setting.started = false; + temp_ended = true; + } + } + + if (temp_ended && !hasStartedScheduledTempRadio()) { + applySavedRadioParams(); + } + + for (int i = 0; i < MAX_SCHEDULED_RADIO_SETTINGS; i++) { + ScheduledRadioSetting& setting = scheduled_radio_settings[i]; + if (setting.active && setting.temporary && !setting.started && now >= setting.start_time) { + if (now >= setting.end_time) { + setting.active = false; + } else { + applyRadioParams(setting.freq, setting.bw, setting.sf, setting.cr); + setting.started = true; + } + } + } +} + +bool MyMesh::isMillisTimerDue(unsigned long timestamp) const { + return timestamp && millisHasNowPassed(timestamp); +} + +bool MyMesh::hasScheduledRadioWorkDue() const { + uint32_t now = getRTCClock()->getCurrentTime(); + for (int i = 0; i < MAX_SCHEDULED_RADIO_SETTINGS; i++) { + const ScheduledRadioSetting& setting = scheduled_radio_settings[i]; + if (!setting.active) { + continue; + } + if (!setting.temporary && now >= setting.start_time) { + return true; + } + if (setting.temporary) { + if (!setting.started && now >= setting.start_time) { + return true; + } + if (setting.started && now >= setting.end_time) { + return true; + } + } + } + return false; +} + +uint32_t MyMesh::limitSleepToMillisTimer(unsigned long timestamp, uint32_t sleep_secs) const { + if (!timestamp || sleep_secs == 0) { + return sleep_secs; + } + unsigned long now = millis(); + if ((long)(now - timestamp) >= 0) { + return 0; + } + unsigned long remaining_ms = timestamp - now; + uint32_t remaining_secs = (remaining_ms + 999UL) / 1000UL; + return remaining_secs < sleep_secs ? remaining_secs : sleep_secs; +} + +uint32_t MyMesh::limitSleepToRtcTime(uint32_t timestamp, uint32_t sleep_secs) const { + if (!timestamp || sleep_secs == 0) { + return sleep_secs; + } + uint32_t now = getRTCClock()->getCurrentTime(); + if (now >= timestamp) { + return 0; + } + uint32_t remaining_secs = timestamp - now; + return remaining_secs < sleep_secs ? remaining_secs : sleep_secs; +} + +uint32_t MyMesh::limitSleepToScheduledRadioWork(uint32_t sleep_secs) const { + for (int i = 0; i < MAX_SCHEDULED_RADIO_SETTINGS; i++) { + const ScheduledRadioSetting& setting = scheduled_radio_settings[i]; + if (!setting.active) { + continue; + } + if (!setting.temporary || !setting.started) { + sleep_secs = limitSleepToRtcTime(setting.start_time, sleep_secs); + } + if (setting.temporary && setting.started) { + sleep_secs = limitSleepToRtcTime(setting.end_time, sleep_secs); + } + } + return sleep_secs; +} + +uint32_t MyMesh::getPowerSaveSleepSeconds(uint32_t max_secs) const { + if (max_secs == 0 || hasPendingWork()) { + return 0; + } + + uint32_t sleep_secs = max_secs; + sleep_secs = limitSleepToMillisTimer(next_flood_advert, sleep_secs); + sleep_secs = limitSleepToMillisTimer(next_local_advert, sleep_secs); + sleep_secs = limitSleepToMillisTimer(dirty_contacts_expiry, sleep_secs); + if (_prefs.battery_alert_enabled) { + sleep_secs = limitSleepToMillisTimer(next_battery_alert_check, sleep_secs); + } + sleep_secs = limitSleepToScheduledRadioWork(sleep_secs); + return sleep_secs; +} + +void MyMesh::applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) { + for (int i = 0; i < MAX_SCHEDULED_RADIO_SETTINGS; i++) { + if (scheduled_radio_settings[i].active && scheduled_radio_settings[i].temporary) { + scheduled_radio_settings[i].active = false; + scheduled_radio_settings[i].started = false; + } + } + + int slot = findFreeScheduledRadioSlot(); + if (slot < 0) { + return; + } + + uint32_t start_time = getRTCClock()->getCurrentTime() + 2; // give CLI reply time to be sent first + scheduled_radio_settings[slot].active = true; + scheduled_radio_settings[slot].temporary = true; + scheduled_radio_settings[slot].started = false; + scheduled_radio_settings[slot].freq = freq; + scheduled_radio_settings[slot].bw = bw; + scheduled_radio_settings[slot].sf = sf; + scheduled_radio_settings[slot].cr = cr; + scheduled_radio_settings[slot].start_time = start_time; + scheduled_radio_settings[slot].end_time = start_time + ((uint32_t)timeout_mins * 60); } bool MyMesh::formatFileSystem() { @@ -1492,6 +3009,7 @@ static void formatPathReply(const uint8_t* path, uint8_t path_len, char* out, si } void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *command, char *reply) { + char* reply_start = reply; if (region_load_active) { if (StrHelper::isBlank(command)) { // empty/blank line, signal to terminate 'load' operation region_map = temp_map; // copy over the temp instance as new current map @@ -1557,6 +3075,11 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char * strcpy(reply, "Err - bad pubkey"); } } + } else if (sender_timestamp == 0 && sender == NULL + && memcmp(command, "get recent.repeater", 19) == 0 + && (command[19] == 0 || command[19] == ' ')) { + printRecentRepeatersSerial(); + reply_start[0] = 0; } else if (sender_timestamp == 0 && strcmp(command, "get acl") == 0) { Serial.println("ACL:"); for (int i = 0; i < acl.getNumClients(); i++) { @@ -1596,6 +3119,60 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char * formatPathReply(sender->out_path, sender->out_path_len, reply, 160); } } + } else if (strncmp(command, "send text.flood ", 16) == 0) { + char* text = trimSpaces(command + 16); + if (*text == 0) { + strcpy(reply, "Err - usage: send text.flood "); + } else if (sendRepeatersFloodText(text)) { + strcpy(reply, "OK"); + } else { + strcpy(reply, "Err - unable to create packet"); + } + } else if (strcmp(command, "get battery.alert") == 0) { + sprintf(reply, "> %s", _prefs.battery_alert_enabled ? "on" : "off"); + } else if (strcmp(command, "get battery.alert.low") == 0) { + sprintf(reply, "> %u", (uint32_t)_prefs.battery_alert_low_percent); + } else if (strcmp(command, "get battery.alert.critical") == 0) { + sprintf(reply, "> %u", (uint32_t)_prefs.battery_alert_critical_percent); + } else if (strncmp(command, "set battery.alert ", 18) == 0) { + const char* value = command + 18; + if (strcmp(value, "on") == 0) { + _prefs.battery_alert_enabled = 1; + next_battery_alert_check = 0; + savePrefs(); + strcpy(reply, "OK"); + } else if (strcmp(value, "off") == 0) { + _prefs.battery_alert_enabled = 0; + battery_alert_sent = false; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Err - usage: set battery.alert "); + } + } else if (strncmp(command, "set battery.alert.low ", 22) == 0) { + uint8_t percent; + if (!parseBatteryAlertPercent(command + 22, 1, 100, percent)) { + strcpy(reply, "Err - usage: set battery.alert.low <1-100>"); + } else if (percent <= _prefs.battery_alert_critical_percent) { + strcpy(reply, "Err - low must be greater than critical"); + } else { + _prefs.battery_alert_low_percent = percent; + next_battery_alert_check = 0; + savePrefs(); + strcpy(reply, "OK"); + } + } else if (strncmp(command, "set battery.alert.critical ", 27) == 0) { + uint8_t percent; + if (!parseBatteryAlertPercent(command + 27, 0, 99, percent)) { + strcpy(reply, "Err - usage: set battery.alert.critical <0-99>"); + } else if (percent >= _prefs.battery_alert_low_percent) { + strcpy(reply, "Err - critical must be less than low"); + } else { + _prefs.battery_alert_critical_percent = percent; + next_battery_alert_check = 0; + savePrefs(); + strcpy(reply, "OK"); + } } else if (memcmp(command, "discover.neighbors", 18) == 0) { const char* sub = command + 18; while (*sub == ' ') sub++; @@ -1616,6 +3193,7 @@ void MyMesh::loop() { #endif mesh::Mesh::loop(); + checkBatteryAlert(); if (next_flood_advert && millisHasNowPassed(next_flood_advert)) { mesh::Packet *pkt = createSelfAdvert(); @@ -1631,21 +3209,7 @@ void MyMesh::loop() { updateAdvertTimer(); // schedule next local advert } - if (set_radio_at && millisHasNowPassed(set_radio_at)) { // apply pending (temporary) radio params - set_radio_at = 0; // clear timer - radio_driver.setParams(pending_freq, pending_bw, pending_sf, pending_cr); - active_sf = pending_sf; - active_cr = pending_cr; - MESH_DEBUG_PRINTLN("Temp radio params"); - } - - if (revert_radio_at && millisHasNowPassed(revert_radio_at)) { // revert radio params to orig - revert_radio_at = 0; // clear timer - radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); - active_sf = _prefs.sf; - active_cr = _prefs.cr; - MESH_DEBUG_PRINTLN("Radio params restored"); - } + processScheduledRadioSettings(); // is pending dirty contacts write needed? if (dirty_contacts_expiry && millisHasNowPassed(dirty_contacts_expiry)) { @@ -1664,5 +3228,9 @@ bool MyMesh::hasPendingWork() const { #if defined(WITH_BRIDGE) if (bridge.isRunning()) return true; // bridge needs WiFi radio, can't sleep #endif - return _mgr->getOutboundTotal() > 0; + if (_mgr->getOutboundTotal() > 0) return true; + if (isMillisTimerDue(next_flood_advert) || isMillisTimerDue(next_local_advert)) return true; + if (isMillisTimerDue(dirty_contacts_expiry)) return true; + if (_prefs.battery_alert_enabled && isMillisTimerDue(next_battery_alert_check)) return true; + return hasScheduledRadioWorkDue(); } diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 467a4186..dc0580d5 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -80,11 +80,32 @@ struct NeighbourInfo { #define PACKET_LOG_FILE "/packet_log" +#ifndef MAX_SCHEDULED_RADIO_SETTINGS_PER_TYPE + #define MAX_SCHEDULED_RADIO_SETTINGS_PER_TYPE 3 +#endif + +#define MAX_SCHEDULED_RADIO_SETTINGS (MAX_SCHEDULED_RADIO_SETTINGS_PER_TYPE * 2) + class MyMesh : public mesh::Mesh, public CommonCLICallbacks { + struct ScheduledRadioSetting { + bool active; + bool temporary; + bool started; + float freq; + float bw; + uint8_t sf; + uint8_t cr; + uint32_t start_time; + uint32_t end_time; + }; + FILESYSTEM* _fs; uint32_t last_millis; uint64_t uptime_millis; unsigned long next_local_advert, next_flood_advert; + unsigned long next_battery_alert_check; + unsigned long last_battery_alert_sent; + bool battery_alert_sent; bool _logging; NodePrefs _prefs; ClientACL acl; @@ -99,6 +120,15 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { RegionEntry* recv_pkt_region; TransportKey default_scope; RateLimiter discover_limiter, anon_limiter; + struct FloodRetryBridgeState { + uint8_t key[MAX_HASH_SIZE]; + uint8_t source_bucket; + uint8_t target_mask; + uint8_t heard_mask; + uint8_t progress_marker; + bool active; + }; + mutable FloodRetryBridgeState flood_retry_bridge_states[MAX_FLOOD_RETRY_SLOTS]; uint32_t pending_discover_tag; unsigned long pending_discover_until; bool region_load_active; @@ -107,13 +137,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { NeighbourInfo neighbours[MAX_NEIGHBOURS]; #endif CayenneLPP telemetry; - unsigned long set_radio_at, revert_radio_at; - float pending_freq; - float pending_bw; - uint8_t pending_sf; uint8_t active_sf; // live SF, including temporary radio overrides - uint8_t pending_cr; uint8_t active_cr; // live CR, including temporary radio overrides + ScheduledRadioSetting scheduled_radio_settings[MAX_SCHEDULED_RADIO_SETTINGS]; int matching_peer_indexes[MAX_CLIENTS]; #if defined(WITH_RS232_BRIDGE) RS232Bridge bridge; @@ -126,6 +152,25 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { uint8_t getDirectRetryCodingRateForSNR(int8_t snr_x4) const; uint8_t getDirectRetryConfiguredMaxAttempts() const; uint32_t getDirectRetryAttemptStepMillis() const; + bool hasFloodRetryPrefixes() const; + bool floodRetryPrefixMatches(const mesh::Packet* packet) const; + bool floodRetryLastHopMatches(const mesh::Packet* packet) const; + bool floodRetryPrefixIgnored(const uint8_t* prefix, uint8_t prefix_len) const; + uint8_t floodRetryEffectivePathLength(const mesh::Packet* packet, uint8_t max_hops = 0xFF) const; + bool floodRetryPrefixFresh(const uint8_t* prefix, uint8_t prefix_len) const; + int floodRetryBucketForPrefix(const uint8_t* prefix, uint8_t prefix_len, bool require_fresh, + bool include_other) const; + int floodRetryBucketForPathHop(const uint8_t* prefix, uint8_t prefix_len, uint8_t hop, + uint8_t progress_marker) const; + int floodRetrySourceBucket(const mesh::Packet* packet) const; + uint8_t floodRetryBridgeTargetMask(uint8_t source_bucket) const; + uint8_t floodRetryBridgeHeardMask(const mesh::Packet* packet, uint8_t source_bucket, + uint8_t progress_marker) const; + FloodRetryBridgeState* floodRetryBridgeStateFor(const mesh::Packet* packet, bool create) const; + void clearFloodRetryBridgeState(const mesh::Packet* packet); + void refreshFloodRetryHeardRecent(const mesh::Packet* packet); + void formatFloodRetryPath(char* dest, size_t dest_len, const mesh::Packet* packet) const; + bool formatFloodRetryHeard(char* dest, size_t dest_len, const mesh::Packet* packet) const; void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr); uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood); uint8_t handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data); @@ -133,9 +178,30 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { uint8_t handleAnonClockReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data); int handleRequest(ClientInfo* sender, uint32_t sender_timestamp, uint8_t* payload, size_t payload_len); mesh::Packet* createSelfAdvert(); + bool sendRepeatersFloodText(const char* text); + void checkBatteryAlert(); + void printRecentRepeatersSerial(); File openAppend(const char* fname); bool isLooped(const mesh::Packet* packet, const uint8_t max_counters[]); + void applyRadioParams(float freq, float bw, uint8_t sf, uint8_t cr); + void applySavedRadioParams(); + void processScheduledRadioSettings(); + bool isMillisTimerDue(unsigned long timestamp) const; + bool hasScheduledRadioWorkDue() const; + uint32_t limitSleepToMillisTimer(unsigned long timestamp, uint32_t sleep_secs) const; + uint32_t limitSleepToRtcTime(uint32_t timestamp, uint32_t sleep_secs) const; + uint32_t limitSleepToScheduledRadioWork(uint32_t sleep_secs) const; + bool hasStartedScheduledTempRadio() const; + int findFreeScheduledRadioSlot() const; + int countScheduledRadioSettings(bool temporary) const; + int findScheduledRadioSettingByIndex(bool temporary, int wanted) const; + int getScheduledRadioSettingIndex(bool temporary, int slot_idx) const; + bool scheduledRadioConflicts(bool temporary, uint32_t start_time, uint32_t end_time) const; + void clearScheduledRadioSetting(int idx, bool restore_if_started); + void formatScheduledRadioDuration(char* dest, size_t dest_len, uint32_t target_time) const; + void formatRadioParamTuple(char* dest, size_t dest_len, const ScheduledRadioSetting& setting) const; + void formatScheduledRadioSetting(char* reply, int setting_idx, int display_idx) const; protected: float getAirtimeBudgetFactor() const override { @@ -155,13 +221,22 @@ protected: uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override; uint8_t getDefaultTxCodingRate() const override { return active_cr; } bool allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const override; + bool maybeShortCircuitDirect(mesh::Packet* packet) override; void configureDirectRetryPacket(mesh::Packet* retry, const mesh::Packet* original, uint8_t retry_attempt) override; uint32_t getDirectRetryEchoDelay(const mesh::Packet* packet) const override; uint8_t getDirectRetryMaxAttempts(const mesh::Packet* packet) const override; uint32_t getDirectRetryAttemptDelay(const mesh::Packet* packet, uint8_t attempt_idx) override; - void onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) override; + void onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt, + const uint8_t* target_hash = NULL, uint8_t target_hash_len = 0, + int16_t payload_type = -1) override; void onDirectRetryFailed(const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) override; void onDirectRetrySucceeded(const uint8_t* next_hop_hash, uint8_t next_hop_hash_len, int8_t snr_x4) override; + bool allowFloodRetry(const mesh::Packet* packet) const override; + void onFloodRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) override; + bool hasFloodRetryTargetPrefix(const mesh::Packet* packet) const override; + uint8_t getFloodRetryMaxPathLength(const mesh::Packet* packet) const override; + uint8_t getFloodRetryMaxAttempts(const mesh::Packet* packet) const override; + bool isFloodRetryEchoTarget(const mesh::Packet* packet, uint8_t progress_marker) const override; int getInterferenceThreshold() const override { return _prefs.interference_threshold; @@ -215,6 +290,10 @@ public: // CommonCLICallbacks void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) override; + void addScheduledRadioParams(bool temporary, float freq, float bw, uint8_t sf, uint8_t cr, + uint32_t start_time, uint32_t end_time, char* reply) override; + void formatScheduledRadioParams(bool temporary, const char* selector, char* reply) override; + void deleteScheduledRadioParams(bool temporary, const char* selector, char* reply) override; bool formatFileSystem() override; void sendSelfAdvertisement(int delay_millis, bool flood) override; void updateAdvertTimer() override; @@ -250,6 +329,7 @@ public: handleCommand(sender_timestamp, NULL, command, reply); } void loop(); + uint32_t getPowerSaveSleepSeconds(uint32_t max_secs) const; #if defined(WITH_BRIDGE) void setBridgeState(bool enable) override { diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 82e2a212..e4f742e8 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -152,12 +152,15 @@ void loop() { #endif rtc_clock.tick(); - if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) { + if (the_mesh.getNodePrefs()->powersaving_enabled && !board.isUsbDataConnected()) { + uint32_t sleep_secs = the_mesh.getPowerSaveSleepSeconds(30); #if defined(NRF52_PLATFORM) - board.sleep(0); // nrf ignores seconds param, sleeps whenever possible + if (sleep_secs > 0) { + board.sleep(0); // nrf ignores seconds param, sleeps whenever possible + } #else - if (the_mesh.millisHasNowPassed(POWERSAVING_FIRSTSLEEP_SECS * 1000)) { // To check if it is time to sleep - board.sleep(30); // Sleep. Wake up after a while or when receiving a LoRa packet + if (sleep_secs > 0 && the_mesh.millisHasNowPassed(POWERSAVING_FIRSTSLEEP_SECS * 1000)) { // To check if it is time to sleep + board.sleep(sleep_secs); // Sleep. Wake up for scheduled jobs or when receiving a LoRa packet } #endif } diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index c311c941..637f1eb0 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -1032,10 +1032,48 @@ void MyMesh::loop() { last_millis = now; } +bool MyMesh::isMillisTimerDue(unsigned long timestamp) const { + return timestamp && millisHasNowPassed(timestamp); +} + +uint32_t MyMesh::limitSleepToMillisTimer(unsigned long timestamp, uint32_t sleep_secs) const { + if (!timestamp || sleep_secs == 0) { + return sleep_secs; + } + unsigned long now = millis(); + if ((long)(now - timestamp) >= 0) { + return 0; + } + unsigned long remaining_ms = timestamp - now; + uint32_t remaining_secs = (remaining_ms + 999UL) / 1000UL; + return remaining_secs < sleep_secs ? remaining_secs : sleep_secs; +} + +uint32_t MyMesh::getPowerSaveSleepSeconds(uint32_t max_secs) const { + if (max_secs == 0 || hasPendingWork()) { + return 0; + } + + uint32_t sleep_secs = max_secs; + if (acl.getNumClients() > 0) { + sleep_secs = limitSleepToMillisTimer(next_push, sleep_secs); + } + sleep_secs = limitSleepToMillisTimer(next_flood_advert, sleep_secs); + sleep_secs = limitSleepToMillisTimer(next_local_advert, sleep_secs); + sleep_secs = limitSleepToMillisTimer(set_radio_at, sleep_secs); + sleep_secs = limitSleepToMillisTimer(revert_radio_at, sleep_secs); + sleep_secs = limitSleepToMillisTimer(dirty_contacts_expiry, sleep_secs); + return sleep_secs; +} + // To check if there is pending work bool MyMesh::hasPendingWork() const { #if defined(WITH_BRIDGE) if (bridge.isRunning()) return true; // bridge needs WiFi radio, can't sleep #endif - return _mgr->getOutboundTotal() > 0; + if (_mgr->getOutboundTotal() > 0) return true; + if (acl.getNumClients() > 0 && isMillisTimerDue(next_push)) return true; + if (isMillisTimerDue(next_flood_advert) || isMillisTimerDue(next_local_advert)) return true; + if (isMillisTimerDue(set_radio_at) || isMillisTimerDue(revert_radio_at)) return true; + return isMillisTimerDue(dirty_contacts_expiry); } diff --git a/examples/simple_room_server/MyMesh.h b/examples/simple_room_server/MyMesh.h index 380e54da..98a79bb8 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -225,7 +225,12 @@ public: void clearStats() override; void handleCommand(uint32_t sender_timestamp, char* command, char* reply); void loop(); + uint32_t getPowerSaveSleepSeconds(uint32_t max_secs) const; // To check if there is pending work bool hasPendingWork() const; + +private: + bool isMillisTimerDue(unsigned long timestamp) const; + uint32_t limitSleepToMillisTimer(unsigned long timestamp, uint32_t sleep_secs) const; }; diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index ad8aa914..a3872684 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -119,12 +119,15 @@ void loop() { #endif rtc_clock.tick(); - if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) { + if (the_mesh.getNodePrefs()->powersaving_enabled && !board.isUsbDataConnected()) { + uint32_t sleep_secs = the_mesh.getPowerSaveSleepSeconds(30); #if defined(NRF52_PLATFORM) - board.sleep(0); // nrf ignores seconds param, sleeps whenever possible + if (sleep_secs > 0) { + board.sleep(0); // nrf ignores seconds param, sleeps whenever possible + } #else - if (the_mesh.millisHasNowPassed(POWERSAVING_FIRSTSLEEP_SECS * 1000)) { // To check if it is time to sleep - board.sleep(30); // Sleep. Wake up after a while or when receiving a LoRa packet + if (sleep_secs > 0 && the_mesh.millisHasNowPassed(POWERSAVING_FIRSTSLEEP_SECS * 1000)) { // To check if it is time to sleep + board.sleep(sleep_secs); // Sleep. Wake up for scheduled jobs or when receiving a LoRa packet } #endif } diff --git a/src/Mesh.cpp b/src/Mesh.cpp index b4977125..45eb031f 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -28,6 +28,41 @@ static uint8_t decodeTraceHashSize(uint8_t flags, uint8_t route_bytes) { return size_linear; } +static uint8_t getTraceRemainingHops(const Packet* packet) { + if (packet == NULL || packet->payload_len < 9) { + return 0; + } + + uint8_t route_bytes = packet->payload_len - 9; + uint8_t hash_size = decodeTraceHashSize(packet->payload[8], route_bytes); + if (hash_size == 0) { + return 0; + } + + uint8_t route_hops = route_bytes / hash_size; + if (packet->path_len >= route_hops) { + return 0; + } + return route_hops - packet->path_len; +} + +static uint8_t getTraceDirectPriority(const Packet* packet) { + uint8_t remaining_hops = getTraceRemainingHops(packet); + if (remaining_hops == 0) { + return 5; + } + if (remaining_hops <= 4) { + return 1; + } + if (remaining_hops <= 8) { + return 2; + } + if (remaining_hops <= 12) { + return 3; + } + return 5; +} + void Mesh::begin() { for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { _direct_retries[i].packet = NULL; @@ -39,6 +74,7 @@ void Mesh::begin() { _direct_retries[i].retry_attempts_sent = 0; memset(_direct_retries[i].next_hop_hash, 0, sizeof(_direct_retries[i].next_hop_hash)); _direct_retries[i].next_hop_hash_len = 0; + _direct_retries[i].payload_type = 0; _direct_retries[i].priority = 0; _direct_retries[i].progress_marker = 0; _direct_retries[i].expect_path_growth = false; @@ -78,8 +114,12 @@ void Mesh::loop() { uint32_t elapsed_millis = _direct_retries[i].retry_started_at == 0 ? 0 : (uint32_t)(_ms->getMillis() - _direct_retries[i].retry_started_at); - onDirectRetryEvent("failed_all_tries", _direct_retries[i].packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); - onDirectRetryEvent("failure", _direct_retries[i].packet, elapsed_millis, _direct_retries[i].retry_attempts_sent); + onDirectRetryEvent("failed_all_tries", _direct_retries[i].packet, elapsed_millis, _direct_retries[i].retry_attempts_sent, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len, + _direct_retries[i].payload_type); + onDirectRetryEvent("failure", _direct_retries[i].packet, elapsed_millis, _direct_retries[i].retry_attempts_sent, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len, + _direct_retries[i].payload_type); onDirectRetryFailed(_direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len); clearDirectRetrySlot(i); continue; @@ -230,9 +270,10 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { // append SNR (Not hash!) pkt->path[pkt->path_len++] = (int8_t) (pkt->getSNR()*4); + uint8_t pri = getTraceDirectPriority(pkt); uint32_t d = getDirectRetransmitDelay(pkt); - maybeScheduleDirectRetry(pkt, 5); - return ACTION_RETRANSMIT_DELAYED(5, d); // schedule with priority 5 (for now), maybe make configurable? + maybeScheduleDirectRetry(pkt, pri); + return ACTION_RETRANSMIT_DELAYED(pri, d); } } return ACTION_RELEASE; @@ -589,6 +630,7 @@ void Mesh::clearDirectRetrySlot(int idx) { _direct_retries[idx].retry_attempts_sent = 0; memset(_direct_retries[idx].next_hop_hash, 0, sizeof(_direct_retries[idx].next_hop_hash)); _direct_retries[idx].next_hop_hash_len = 0; + _direct_retries[idx].payload_type = 0; _direct_retries[idx].priority = 0; _direct_retries[idx].progress_marker = 0; _direct_retries[idx].expect_path_growth = false; @@ -641,7 +683,9 @@ bool Mesh::cancelDirectRetryOnEcho(const Packet* packet) { uint8_t retry_attempt = _direct_retries[i].waiting_final_echo ? _direct_retries[i].retry_attempts_sent : _direct_retries[i].retry_attempts_sent + 1; - onDirectRetryEvent("good", _direct_retries[i].packet, echo_millis, retry_attempt); + onDirectRetryEvent("good", _direct_retries[i].packet, echo_millis, retry_attempt, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len, + _direct_retries[i].payload_type); if (_direct_retries[i].queued) { for (int j = 0; j < _mgr->getOutboundTotal(); j++) { if (_mgr->getOutboundByIdx(j) == _direct_retries[i].packet) { @@ -661,7 +705,8 @@ bool Mesh::cancelDirectRetryOnEcho(const Packet* packet) { uint32_t echo_millis = _direct_retries[i].echo_wait_started_at == 0 ? 0 : (uint32_t)(_ms->getMillis() - _direct_retries[i].echo_wait_started_at); - onDirectRetryEvent("good", _direct_retries[i].trigger_packet, echo_millis, _direct_retries[i].retry_attempts_sent + 1); + onDirectRetryEvent("good", _direct_retries[i].trigger_packet, echo_millis, _direct_retries[i].retry_attempts_sent + 1, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len); clearDirectRetrySlot(i); } cleared = true; @@ -682,7 +727,8 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { uint32_t elapsed_millis = _direct_retries[i].retry_started_at == 0 ? 0 : (uint32_t)(_ms->getMillis() - _direct_retries[i].retry_started_at); - onDirectRetryEvent("resent", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent + 1); + onDirectRetryEvent("resent", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent + 1, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len); _direct_retries[i].echo_wait_started_at = _ms->getMillis(); _direct_retries[i].retry_attempts_sent++; uint8_t max_attempts = getDirectRetryMaxAttempts(packet); @@ -703,8 +749,10 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { Packet* retry = obtainNewPacket(); if (retry == NULL) { - onDirectRetryEvent("dropped_no_packet", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent + 1); - onDirectRetryEvent("failure", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent + 1); + onDirectRetryEvent("dropped_no_packet", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent + 1, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len); + onDirectRetryEvent("failure", packet, elapsed_millis, _direct_retries[i].retry_attempts_sent + 1, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len); clearDirectRetrySlot(i); continue; } @@ -719,10 +767,13 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { _direct_retries[i].retry_delay = retry_delay; _direct_retries[i].retry_at = futureMillis(retry_delay); _direct_retries[i].waiting_final_echo = false; - onDirectRetryEvent("queued", retry, retry_delay, retry_attempt); + onDirectRetryEvent("queued", retry, retry_delay, retry_attempt, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len); } else { - onDirectRetryEvent("dropped_queue_full", retry, retry_delay, retry_attempt); - onDirectRetryEvent("failure", retry, elapsed_millis, retry_attempt); + onDirectRetryEvent("dropped_queue_full", retry, retry_delay, retry_attempt, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len); + onDirectRetryEvent("failure", retry, elapsed_millis, retry_attempt, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len); releasePacket(retry); clearDirectRetrySlot(i); } @@ -737,8 +788,10 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { // Allocate the retry packet only after TX-complete so busy repeaters do not reserve pool slots early. Packet* retry = obtainNewPacket(); if (retry == NULL) { - onDirectRetryEvent("dropped_no_packet", packet, _direct_retries[i].retry_delay, 1); - onDirectRetryEvent("failure", packet, 0, 1); + onDirectRetryEvent("dropped_no_packet", packet, _direct_retries[i].retry_delay, 1, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len); + onDirectRetryEvent("failure", packet, 0, 1, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len); clearDirectRetrySlot(i); continue; } @@ -757,10 +810,13 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) { _direct_retries[i].retry_at = futureMillis(_direct_retries[i].retry_delay); _direct_retries[i].retry_started_at = now; _direct_retries[i].echo_wait_started_at = now; - onDirectRetryEvent("queued", retry, _direct_retries[i].retry_delay, 1); + onDirectRetryEvent("queued", retry, _direct_retries[i].retry_delay, 1, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len); } else { - onDirectRetryEvent("dropped_queue_full", retry, _direct_retries[i].retry_delay, 1); - onDirectRetryEvent("failure", retry, 0, 1); + onDirectRetryEvent("dropped_queue_full", retry, _direct_retries[i].retry_delay, 1, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len); + onDirectRetryEvent("failure", retry, 0, 1, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len); releasePacket(retry); clearDirectRetrySlot(i); } @@ -776,16 +832,20 @@ void Mesh::clearPendingDirectRetryOnSendFail(const Packet* packet) { if (_direct_retries[i].queued) { if (_direct_retries[i].packet == packet) { // The queued retry itself failed; Dispatcher will release it after this hook. - onDirectRetryEvent("dropped_send_fail", packet, 0, _direct_retries[i].retry_attempts_sent + 1); - onDirectRetryEvent("failure", packet, 0, _direct_retries[i].retry_attempts_sent + 1); + onDirectRetryEvent("dropped_send_fail", packet, 0, _direct_retries[i].retry_attempts_sent + 1, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len); + onDirectRetryEvent("failure", packet, 0, _direct_retries[i].retry_attempts_sent + 1, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len); clearDirectRetrySlot(i); } continue; } if (_direct_retries[i].trigger_packet == packet) { - onDirectRetryEvent("dropped_send_fail", packet, 0, 1); - onDirectRetryEvent("failure", packet, 0, 1); + onDirectRetryEvent("dropped_send_fail", packet, 0, 1, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len); + onDirectRetryEvent("failure", packet, 0, 1, + _direct_retries[i].next_hop_hash, _direct_retries[i].next_hop_hash_len); clearDirectRetrySlot(i); } } @@ -922,8 +982,8 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { } } if (slot_idx < 0) { - onDirectRetryEvent("dropped_no_slot", packet, 0, 0); - onDirectRetryEvent("failure", packet, 0, 0); + onDirectRetryEvent("dropped_no_slot", packet, 0, 0, next_hop_hash, next_hop_hash_len); + onDirectRetryEvent("failure", packet, 0, 0, next_hop_hash, next_hop_hash_len); return; } @@ -940,6 +1000,7 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) { memset(_direct_retries[slot_idx].next_hop_hash, 0, sizeof(_direct_retries[slot_idx].next_hop_hash)); memcpy(_direct_retries[slot_idx].next_hop_hash, next_hop_hash, next_hop_hash_len); _direct_retries[slot_idx].next_hop_hash_len = next_hop_hash_len; + _direct_retries[slot_idx].payload_type = packet->getPayloadType(); _direct_retries[slot_idx].priority = priority; _direct_retries[slot_idx].progress_marker = progress_marker; _direct_retries[slot_idx].expect_path_growth = expect_path_growth; @@ -1496,7 +1557,7 @@ void Mesh::sendDirect(Packet* packet, const uint8_t* path, uint8_t path_len, uin packet->payload_len += path_len; packet->path_len = 0; - pri = 5; // maybe make this configurable + pri = getTraceDirectPriority(packet); } else { packet->path_len = Packet::copyPath(packet->path, path, path_len); if (packet->getPayloadType() == PAYLOAD_TYPE_PATH) { diff --git a/src/Mesh.h b/src/Mesh.h index 10beb4e6..c306a30f 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -48,6 +48,7 @@ class Mesh : public Dispatcher { uint8_t retry_key[MAX_HASH_SIZE]; uint8_t next_hop_hash[MAX_HASH_SIZE]; uint8_t next_hop_hash_len; + uint8_t payload_type; uint8_t priority; uint8_t progress_marker; bool expect_path_growth; @@ -201,7 +202,9 @@ protected: /** * \brief Optional hook for logging direct-retry lifecycle events. */ - virtual void onDirectRetryEvent(const char* event, const Packet* packet, uint32_t delay_millis, uint8_t retry_attempt) { } + virtual void onDirectRetryEvent(const char* event, const Packet* packet, uint32_t delay_millis, uint8_t retry_attempt, + const uint8_t* target_hash = NULL, uint8_t target_hash_len = 0, + int16_t payload_type = -1) { } /** * \brief Optional hook for link-quality feedback when all direct-retry attempts fail. diff --git a/src/MeshCore.h b/src/MeshCore.h index 89e60b1f..cd0f72f1 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -64,12 +64,14 @@ public: virtual uint8_t getStartupReason() const = 0; virtual bool getBootloaderVersion(char* version, size_t max_len) { return false; } virtual bool startOTAUpdate(const char* id, char reply[]) { return false; } // not supported + virtual bool stopOTAUpdate(char reply[]) { return false; } // not supported virtual bool setLoRaFemLnaEnabled(bool enable) { return false; } virtual bool canControlLoRaFemLna() const { return false; } virtual bool isLoRaFemLnaEnabled() const { return false; } // Power management interface (boards with power management override these) virtual bool isExternalPowered() { return false; } + virtual bool isUsbDataConnected() { return false; } virtual uint16_t getBootVoltage() { return 0; } virtual uint32_t getResetReason() const { return 0; } virtual const char* getResetReasonString(uint32_t reason) { return "Not available"; } diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 3937dd50..788e4b33 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -95,6 +95,150 @@ static bool looksUnsignedInteger(const char* s) { return saw_digit; } +static bool parseUint8Strict(const char* value, uint8_t min_value, uint8_t max_value, uint8_t& result) { + if (value == NULL || *value == 0) { + return false; + } + + uint16_t parsed = 0; + const char* sp = value; + while (*sp) { + if (*sp < '0' || *sp > '9') { + return false; + } + parsed = (uint16_t)((parsed * 10) + (*sp - '0')); + if (parsed > max_value) { + return false; + } + sp++; + } + if (parsed < min_value) { + return false; + } + result = (uint8_t)parsed; + return true; +} + +static bool bwMatches(float bw, float allowed) { + float diff = bw - allowed; + if (diff < 0.0f) diff = -diff; + return diff <= 0.001f; +} + +static bool isValidLoRaBandwidth(float bw) { +#if defined(USE_LR1110) + return bwMatches(bw, 62.5f) + || bwMatches(bw, 125.0f) + || bwMatches(bw, 250.0f) + || bwMatches(bw, 500.0f); +#elif defined(USE_LLCC68) || defined(USE_SX1272) + return bwMatches(bw, 125.0f) + || bwMatches(bw, 250.0f) + || bwMatches(bw, 500.0f); +#else + return bwMatches(bw, 7.8f) + || bwMatches(bw, 10.4f) + || bwMatches(bw, 15.6f) + || bwMatches(bw, 20.8f) + || bwMatches(bw, 31.25f) + || bwMatches(bw, 41.7f) + || bwMatches(bw, 62.5f) + || bwMatches(bw, 125.0f) + || bwMatches(bw, 250.0f) + || bwMatches(bw, 500.0f); +#endif +} + +static float defaultLoRaBandwidth() { +#ifdef LORA_BW + if (isValidLoRaBandwidth((float)LORA_BW)) { + return (float)LORA_BW; + } +#endif + return 125.0f; +} + +static const char* skipSpacesConst(const char* s) { + while (s != NULL && *s == ' ') s++; + return s; +} + +static bool parseUint32Strict(const char* s, uint32_t& out) { + if (!looksUnsignedInteger(s)) { + return false; + } + + uint64_t n = 0; + s = skipSpacesConst(s); + while (*s >= '0' && *s <= '9') { + n = (n * 10) + (uint32_t)(*s - '0'); + if (n > 0xFFFFFFFFULL) { + return false; + } + s++; + } + out = (uint32_t)n; + return true; +} + +static int countSeparatedParts(const char* s, char separator) { + if (s == NULL || *s == 0) { + return 0; + } + + int count = 1; + while (*s) { + if (*s++ == separator) { + count++; + } + } + return count; +} + +static bool parseScheduledRadioArgs(const char* args, bool temporary, float& freq, float& bw, + uint8_t& sf, uint8_t& cr, uint32_t& start_time, + uint32_t& end_time) { + const int expected_parts = temporary ? 6 : 5; + args = skipSpacesConst(args); + if (countSeparatedParts(args, ',') != expected_parts) { + return false; + } + char local[96]; + if (strlen(args) >= sizeof(local)) { + return false; + } + StrHelper::strncpy(local, args, sizeof(local)); + const char* parts[6]; + int num = mesh::Utils::parseTextParts(local, parts, expected_parts, ','); + if (num != expected_parts) { + return false; + } + + uint32_t sf_u32 = 0; + uint32_t cr_u32 = 0; + if (!looksNumeric(parts[0]) || !looksNumeric(parts[1]) + || !parseUint32Strict(parts[2], sf_u32) + || !parseUint32Strict(parts[3], cr_u32) + || !parseUint32Strict(parts[4], start_time)) { + return false; + } + if (sf_u32 > 255 || cr_u32 > 255) { + return false; + } + + freq = atof(parts[0]); + bw = atof(parts[1]); + sf = (uint8_t)sf_u32; + cr = (uint8_t)cr_u32; + if (temporary && !parseUint32Strict(parts[5], end_time)) { + return false; + } + if (!temporary) { + end_time = 0; + } + return true; +} + static int16_t parseSnrDbX4(const char* s) { float db = atof(s); return (int16_t)(db * 4.0f + (db >= 0.0f ? 0.5f : -0.5f)); @@ -124,6 +268,93 @@ static void markDirectRetryPrefsValid(NodePrefs* prefs) { prefs->direct_retry_prefs_magic[1] = DIRECT_RETRY_PREFS_MAGIC_1; } +static void applyFloodRetryPreset(NodePrefs* prefs, uint8_t preset) { + if (preset == RETRY_PRESET_INFRA) { + prefs->flood_retry_attempts = FLOOD_RETRY_INFRA_COUNT; + prefs->flood_retry_max_path = FLOOD_RETRY_INFRA_MAX_PATH; + } else if (preset == RETRY_PRESET_MOBILE) { + prefs->flood_retry_attempts = FLOOD_RETRY_MOBILE_COUNT; + prefs->flood_retry_max_path = FLOOD_RETRY_MOBILE_MAX_PATH; + } else { + prefs->flood_retry_attempts = FLOOD_RETRY_ROOFTOP_COUNT; + prefs->flood_retry_max_path = FLOOD_RETRY_ROOFTOP_MAX_PATH; + } +} + +static bool parseFloodRetryPathGate(const char* value, uint8_t& path_gate) { + if (value == NULL) { + return false; + } + if (strcmp(value, "off") == 0 || strcmp(value, "disabled") == 0 || strcmp(value, "disable") == 0) { + path_gate = FLOOD_RETRY_PATH_GATE_DISABLED; + return true; + } + return parseUint8Strict(value, 0, 63, path_gate); +} + +static void formatFloodRetryPathGate(char* dest, uint8_t path_gate) { + if (path_gate == FLOOD_RETRY_PATH_GATE_DISABLED) { + strcpy(dest, "off"); + } else { + sprintf(dest, "%u", (unsigned int)path_gate); + } +} + +static void formatFloodRetryPrefixList(char* dest, const uint8_t prefixes[][FLOOD_RETRY_PREFIX_LEN], + uint8_t max_prefixes) { + char* out = dest; + bool first = true; + for (int i = 0; i < max_prefixes; i++) { + const uint8_t* prefix = prefixes[i]; + if (prefix[0] == 0 && prefix[1] == 0 && prefix[2] == 0) { + continue; + } + if (!first) { + *out++ = ','; + } + mesh::Utils::toHex(out, prefix, FLOOD_RETRY_PREFIX_LEN); + out += FLOOD_RETRY_PREFIX_LEN * 2; + first = false; + } + *out = 0; +} + +static bool parseFloodRetryPrefixList(uint8_t dest[][FLOOD_RETRY_PREFIX_LEN], uint8_t max_prefixes, const char* value) { + if (max_prefixes > FLOOD_RETRY_LIST_PREFIXES) { + return false; + } + uint8_t parsed[FLOOD_RETRY_LIST_PREFIXES][FLOOD_RETRY_PREFIX_LEN]; + memset(parsed, 0, sizeof(parsed)); + if (value == NULL || value[0] == 0 || strcmp(value, "none") == 0 || strcmp(value, "off") == 0) { + memcpy(dest, parsed, max_prefixes * FLOOD_RETRY_PREFIX_LEN); + return true; + } + + char local[FLOOD_RETRY_LIST_TEXT_MAX]; + StrHelper::strncpy(local, value, sizeof(local)); + const char* parts[FLOOD_RETRY_LIST_PREFIXES + 1]; + int num = mesh::Utils::parseTextParts(local, parts, FLOOD_RETRY_LIST_PREFIXES + 1); + if (num > max_prefixes) { + return false; + } + for (int i = 0; i < num; i++) { + if (strlen(parts[i]) != FLOOD_RETRY_PREFIX_LEN * 2) { + return false; + } + for (int j = 0; j < FLOOD_RETRY_PREFIX_LEN * 2; j++) { + if (!mesh::Utils::isHexChar(parts[i][j])) { + return false; + } + } + if (!mesh::Utils::fromHex(parsed[i], FLOOD_RETRY_PREFIX_LEN, parts[i]) + || (parsed[i][0] == 0 && parsed[i][1] == 0 && parsed[i][2] == 0)) { + return false; + } + } + memcpy(dest, parsed, max_prefixes * FLOOD_RETRY_PREFIX_LEN); + return true; +} + static void applyDirectRetryPreset(NodePrefs* prefs, uint8_t preset) { prefs->retry_preset = preset; if (preset == RETRY_PRESET_INFRA) { @@ -143,6 +374,7 @@ static void applyDirectRetryPreset(NodePrefs* prefs, uint8_t preset) { prefs->direct_retry_step_ms = DIRECT_RETRY_ROOFTOP_STEP_MS; prefs->direct_retry_snr_margin_x4 = DIRECT_RETRY_ROOFTOP_MARGIN_X4; } + applyFloodRetryPreset(prefs, prefs->retry_preset); markDirectRetryPrefsValid(prefs); } @@ -154,6 +386,7 @@ static void setDefaultDirectRetryPrefs(NodePrefs* prefs) { prefs->direct_retry_cr7_snr_x4 = DIRECT_RETRY_CR7_MIN_SNR_X4_DEFAULT; prefs->direct_retry_cr8_snr_x4 = DIRECT_RETRY_CR8_MAX_SNR_X4_DEFAULT; prefs->direct_retry_enabled = 1; + prefs->direct_retry_recent_enabled = DIRECT_RETRY_RECENT_DEFAULT; markDirectRetryPrefsValid(prefs); } @@ -280,7 +513,48 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->direct_retry_enabled, sizeof(_prefs->direct_retry_enabled)); // 307 file.read((uint8_t *)&_prefs->direct_retry_cr_enabled, sizeof(_prefs->direct_retry_cr_enabled)); // 308 file.read((uint8_t *)&_prefs->direct_retry_prefs_magic, sizeof(_prefs->direct_retry_prefs_magic)); // 309 - // next: 311 + memset(_prefs->flood_retry_prefixes, 0, sizeof(_prefs->flood_retry_prefixes)); + _prefs->flood_retry_bridge_enabled = 0; + memset(_prefs->flood_retry_bridge_buckets, 0, sizeof(_prefs->flood_retry_bridge_buckets)); + memset(_prefs->flood_retry_ignore_prefixes, 0, sizeof(_prefs->flood_retry_ignore_prefixes)); + _prefs->flood_retry_advert_enabled = FLOOD_RETRY_ADVERT_DEFAULT; + _prefs->battery_alert_enabled = 0; + _prefs->battery_alert_low_percent = BATTERY_ALERT_LOW_PERCENT_DEFAULT; + _prefs->battery_alert_critical_percent = BATTERY_ALERT_CRITICAL_PERCENT_DEFAULT; + _prefs->direct_retry_recent_enabled = DIRECT_RETRY_RECENT_DEFAULT; + bool has_flood_retry_prefs = file.available() >= 2; + if (has_flood_retry_prefs) { + file.read((uint8_t *)&_prefs->flood_retry_attempts, sizeof(_prefs->flood_retry_attempts)); // 311 + file.read((uint8_t *)&_prefs->flood_retry_max_path, sizeof(_prefs->flood_retry_max_path)); // 312 + if (file.available() >= (int)sizeof(_prefs->flood_retry_prefixes)) { + file.read((uint8_t *)&_prefs->flood_retry_prefixes[0][0], sizeof(_prefs->flood_retry_prefixes)); + } + if (file.available() >= (int)sizeof(_prefs->flood_retry_bridge_enabled)) { + file.read((uint8_t *)&_prefs->flood_retry_bridge_enabled, sizeof(_prefs->flood_retry_bridge_enabled)); + } + if (file.available() >= (int)sizeof(_prefs->flood_retry_bridge_buckets)) { + file.read((uint8_t *)&_prefs->flood_retry_bridge_buckets[0][0][0], sizeof(_prefs->flood_retry_bridge_buckets)); + } + if (file.available() >= (int)sizeof(_prefs->flood_retry_ignore_prefixes)) { + file.read((uint8_t *)&_prefs->flood_retry_ignore_prefixes[0][0], sizeof(_prefs->flood_retry_ignore_prefixes)); + } + if (file.available() >= (int)sizeof(_prefs->flood_retry_advert_enabled)) { + file.read((uint8_t *)&_prefs->flood_retry_advert_enabled, sizeof(_prefs->flood_retry_advert_enabled)); + } + if (file.available() >= (int)sizeof(_prefs->battery_alert_enabled)) { + file.read((uint8_t *)&_prefs->battery_alert_enabled, sizeof(_prefs->battery_alert_enabled)); + } + if (file.available() >= (int)sizeof(_prefs->battery_alert_low_percent)) { + file.read((uint8_t *)&_prefs->battery_alert_low_percent, sizeof(_prefs->battery_alert_low_percent)); + } + if (file.available() >= (int)sizeof(_prefs->battery_alert_critical_percent)) { + file.read((uint8_t *)&_prefs->battery_alert_critical_percent, sizeof(_prefs->battery_alert_critical_percent)); + } + if (file.available() >= (int)sizeof(_prefs->direct_retry_recent_enabled)) { + file.read((uint8_t *)&_prefs->direct_retry_recent_enabled, sizeof(_prefs->direct_retry_recent_enabled)); + } + } + // next: 672 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -288,7 +562,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->direct_tx_delay_factor = constrain(_prefs->direct_tx_delay_factor, 0, 2.0f); _prefs->airtime_factor = constrain(_prefs->airtime_factor, 0, 9.0f); _prefs->freq = constrain(_prefs->freq, 150.0f, 2500.0f); - _prefs->bw = constrain(_prefs->bw, 7.8f, 500.0f); + _prefs->bw = isValidLoRaBandwidth(_prefs->bw) ? _prefs->bw : defaultLoRaBandwidth(); _prefs->sf = constrain(_prefs->sf, 5, 12); _prefs->cr = constrain(_prefs->cr, 5, 8); _prefs->tx_power_dbm = constrain(_prefs->tx_power_dbm, -9, 30); @@ -315,6 +589,13 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->cad_enabled = constrain(_prefs->cad_enabled, 0, 1); // boolean if (!directRetryPrefsValid(_prefs)) { setDefaultDirectRetryPrefs(_prefs); + memset(_prefs->flood_retry_prefixes, 0, sizeof(_prefs->flood_retry_prefixes)); + _prefs->flood_retry_bridge_enabled = 0; + memset(_prefs->flood_retry_bridge_buckets, 0, sizeof(_prefs->flood_retry_bridge_buckets)); + memset(_prefs->flood_retry_ignore_prefixes, 0, sizeof(_prefs->flood_retry_ignore_prefixes)); + _prefs->flood_retry_advert_enabled = FLOOD_RETRY_ADVERT_DEFAULT; + } else if (!has_flood_retry_prefs) { + applyFloodRetryPreset(_prefs, _prefs->retry_preset); } if (_prefs->retry_preset > RETRY_PRESET_MOBILE && _prefs->retry_preset != RETRY_PRESET_CUSTOM) { _prefs->retry_preset = RETRY_PRESET_CUSTOM; @@ -325,6 +606,20 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->direct_retry_snr_margin_x4 = constrain(_prefs->direct_retry_snr_margin_x4, 0, 160); _prefs->direct_retry_enabled = constrain(_prefs->direct_retry_enabled, 0, 1); _prefs->direct_retry_cr_enabled = constrain(_prefs->direct_retry_cr_enabled, 0, 1); + _prefs->flood_retry_attempts = constrain(_prefs->flood_retry_attempts, 0, 15); + if (_prefs->flood_retry_max_path != FLOOD_RETRY_PATH_GATE_DISABLED) { + _prefs->flood_retry_max_path = constrain(_prefs->flood_retry_max_path, 0, 63); + } + _prefs->flood_retry_bridge_enabled = constrain(_prefs->flood_retry_bridge_enabled, 0, 1); + _prefs->flood_retry_advert_enabled = constrain(_prefs->flood_retry_advert_enabled, 0, 1); + _prefs->battery_alert_enabled = constrain(_prefs->battery_alert_enabled, 0, 1); + _prefs->direct_retry_recent_enabled = constrain(_prefs->direct_retry_recent_enabled, 0, 1); + if (_prefs->battery_alert_low_percent < 1 + || _prefs->battery_alert_low_percent > 100 + || _prefs->battery_alert_critical_percent >= _prefs->battery_alert_low_percent) { + _prefs->battery_alert_low_percent = BATTERY_ALERT_LOW_PERCENT_DEFAULT; + _prefs->battery_alert_critical_percent = BATTERY_ALERT_CRITICAL_PERCENT_DEFAULT; + } file.close(); } @@ -405,7 +700,18 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->direct_retry_enabled, sizeof(_prefs->direct_retry_enabled)); // 307 file.write((uint8_t *)&_prefs->direct_retry_cr_enabled, sizeof(_prefs->direct_retry_cr_enabled)); // 308 file.write((uint8_t *)&_prefs->direct_retry_prefs_magic, sizeof(_prefs->direct_retry_prefs_magic)); // 309 - // next: 311 + file.write((uint8_t *)&_prefs->flood_retry_attempts, sizeof(_prefs->flood_retry_attempts)); // 311 + file.write((uint8_t *)&_prefs->flood_retry_max_path, sizeof(_prefs->flood_retry_max_path)); // 312 + file.write((uint8_t *)&_prefs->flood_retry_prefixes[0][0], sizeof(_prefs->flood_retry_prefixes)); // 313 + file.write((uint8_t *)&_prefs->flood_retry_bridge_enabled, sizeof(_prefs->flood_retry_bridge_enabled)); + file.write((uint8_t *)&_prefs->flood_retry_bridge_buckets[0][0][0], sizeof(_prefs->flood_retry_bridge_buckets)); + file.write((uint8_t *)&_prefs->flood_retry_ignore_prefixes[0][0], sizeof(_prefs->flood_retry_ignore_prefixes)); + file.write((uint8_t *)&_prefs->flood_retry_advert_enabled, sizeof(_prefs->flood_retry_advert_enabled)); + file.write((uint8_t *)&_prefs->battery_alert_enabled, sizeof(_prefs->battery_alert_enabled)); + file.write((uint8_t *)&_prefs->battery_alert_low_percent, sizeof(_prefs->battery_alert_low_percent)); + file.write((uint8_t *)&_prefs->battery_alert_critical_percent, sizeof(_prefs->battery_alert_critical_percent)); + file.write((uint8_t *)&_prefs->direct_retry_recent_enabled, sizeof(_prefs->direct_retry_recent_enabled)); + // next: 672 file.close(); } @@ -466,10 +772,14 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else { strcpy(reply, "ERR: clock cannot go backwards"); } - } else if (memcmp(command, "start ota", 9) == 0) { + } else if (memcmp(command, "start ota", 9) == 0 && (command[9] == 0 || command[9] == ' ')) { if (!_board->startOTAUpdate(_prefs->node_name, reply)) { strcpy(reply, "Error"); } + } else if (memcmp(command, "stop ota", 8) == 0 && (command[8] == 0 || command[8] == ' ')) { + if (!_board->stopOTAUpdate(reply)) { + strcpy(reply, "Error"); + } } else if (memcmp(command, "clock", 5) == 0) { uint32_t now = getRTCClock()->getCurrentTime(); DateTime dt = DateTime(now); @@ -507,7 +817,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re uint8_t sf = num > 2 ? atoi(parts[2]) : 0; uint8_t cr = num > 3 ? atoi(parts[3]) : 0; int temp_timeout_mins = num > 4 ? atoi(parts[4]) : 0; - if (freq >= 150.0f && freq <= 2500.0f && sf >= 5 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7.0f && bw <= 500.0f && temp_timeout_mins > 0) { + if (freq >= 150.0f && freq <= 2500.0f && sf >= 5 && sf <= 12 && cr >= 5 && cr <= 8 && isValidLoRaBandwidth(bw) && temp_timeout_mins > 0) { _callbacks->applyTempRadioParams(freq, bw, sf, cr, temp_timeout_mins); sprintf(reply, "OK - temp params for %d mins", temp_timeout_mins); } else { @@ -529,6 +839,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re handleGetCmd(sender_timestamp, command, reply); } else if (memcmp(command, "set ", 4) == 0) { handleSetCmd(sender_timestamp, command, reply); + } else if (memcmp(command, "del ", 4) == 0) { + handleDelCmd(command, reply); } else if (sender_timestamp == 0 && strcmp(command, "erase") == 0) { bool s = _callbacks->formatFileSystem(); sprintf(reply, "File system erase: %s", s ? "OK" : "Err"); @@ -663,13 +975,21 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re #endif } else if (memcmp(command, "powersaving on", 14) == 0) { #if defined(NRF52_PLATFORM) - _prefs->powersaving_enabled = 1; - savePrefs(); - strcpy(reply, "on - Immediate effect"); + if (sender_timestamp == 0 || _board->isUsbDataConnected()) { + strcpy(reply, "Error: USB serial connected"); + } else { + _prefs->powersaving_enabled = 1; + savePrefs(); + strcpy(reply, "on - Immediate effect"); + } #elif defined(ESP32) && !defined(WITH_BRIDGE) - _prefs->powersaving_enabled = 1; - savePrefs(); - strcpy(reply, "on - After 2 minutes"); + if (sender_timestamp == 0 || _board->isUsbDataConnected()) { + strcpy(reply, "Error: USB serial connected"); + } else { + _prefs->powersaving_enabled = 1; + savePrefs(); + strcpy(reply, "on - After 2 minutes"); + } #elif defined(WITH_BRIDGE) strcpy(reply, "Bridge not supported"); #else @@ -862,7 +1182,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep float bw = num > 1 ? strtof(parts[1], nullptr) : 0.0f; uint8_t sf = num > 2 ? atoi(parts[2]) : 0; uint8_t cr = num > 3 ? atoi(parts[3]) : 0; - if (freq >= 150.0f && freq <= 2500.0f && sf >= 5 && sf <= 12 && cr >= 5 && cr <= 8 && bw >= 7.0f && bw <= 500.0f) { + if (freq >= 150.0f && freq <= 2500.0f && sf >= 5 && sf <= 12 && cr >= 5 && cr <= 8 && isValidLoRaBandwidth(bw)) { _prefs->sf = sf; _prefs->cr = cr; _prefs->freq = freq; @@ -872,6 +1192,28 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { strcpy(reply, "Error, invalid radio params"); } + } else if (memcmp(config, "radioat ", 8) == 0) { + float freq, bw; + uint8_t sf, cr; + uint32_t start_time, end_time; + if (!parseScheduledRadioArgs(&config[8], false, freq, bw, sf, cr, start_time, end_time)) { + strcpy(reply, "Error, use: set radioat f,bw,sf,cr,start"); + } else if (freq < 150.0f || freq > 2500.0f || sf < 5 || sf > 12 || cr < 5 || cr > 8 || !isValidLoRaBandwidth(bw)) { + strcpy(reply, "Error, invalid radio params"); + } else { + _callbacks->addScheduledRadioParams(false, freq, bw, sf, cr, start_time, end_time, reply); + } + } else if (memcmp(config, "tempradioat ", 12) == 0) { + float freq, bw; + uint8_t sf, cr; + uint32_t start_time, end_time; + if (!parseScheduledRadioArgs(&config[12], true, freq, bw, sf, cr, start_time, end_time)) { + strcpy(reply, "Error, use: set tempradioat f,bw,sf,cr,start,end"); + } else if (freq < 150.0f || freq > 2500.0f || sf < 5 || sf > 12 || cr < 5 || cr > 8 || !isValidLoRaBandwidth(bw)) { + strcpy(reply, "Error, invalid radio params"); + } else { + _callbacks->addScheduledRadioParams(true, freq, bw, sf, cr, start_time, end_time, reply); + } } else if (memcmp(config, "lat ", 4) == 0) { _prefs->node_lat = atof(&config[4]); savePrefs(); @@ -955,6 +1297,18 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { strcpy(reply, "Error, must be on or off"); } + } else if (memcmp(config, "direct.retry.heard ", 19) == 0) { + if (strcmp(&config[19], "on") == 0) { + _prefs->direct_retry_recent_enabled = 1; + savePrefs(); + strcpy(reply, "OK"); + } else if (strcmp(&config[19], "off") == 0) { + _prefs->direct_retry_recent_enabled = 0; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be on or off"); + } } else if (memcmp(config, "direct.retry.margin ", 20) == 0) { if (!looksNumeric(&config[20])) { strcpy(reply, "Error, must be 0-40 dB"); @@ -999,6 +1353,81 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { strcpy(reply, "Error, must be 0-5000 ms"); } + } else if (memcmp(config, "flood.retry.count ", 18) == 0) { + int attempts = looksUnsignedInteger(&config[18]) ? _atoi(&config[18]) : -1; + if (attempts >= 0 && attempts <= 15) { + _prefs->flood_retry_attempts = (uint8_t)attempts; + _prefs->retry_preset = RETRY_PRESET_CUSTOM; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be 0-15"); + } + } else if (memcmp(config, "flood.retry.path ", 17) == 0) { + uint8_t path_gate; + if (parseFloodRetryPathGate(&config[17], path_gate)) { + _prefs->flood_retry_max_path = path_gate; + _prefs->retry_preset = RETRY_PRESET_CUSTOM; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be 0-63 or off"); + } + } else if (memcmp(config, "flood.retry.prefixes ", 21) == 0) { + if (parseFloodRetryPrefixList(_prefs->flood_retry_prefixes, FLOOD_RETRY_PREFIX_SLOTS, &config[21])) { + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error, use up to %u comma-separated 3-byte hex prefixes", + (unsigned int)FLOOD_RETRY_PREFIX_SLOTS); + } + } else if (memcmp(config, "flood.retry.ignore ", 19) == 0) { + if (parseFloodRetryPrefixList(_prefs->flood_retry_ignore_prefixes, + FLOOD_RETRY_IGNORE_PREFIXES, &config[19])) { + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error, use up to %u comma-separated 3-byte hex prefixes", + (unsigned int)FLOOD_RETRY_IGNORE_PREFIXES); + } + } else if (memcmp(config, "flood.retry.advert ", 19) == 0) { + if (strcmp(&config[19], "on") == 0) { + _prefs->flood_retry_advert_enabled = 1; + savePrefs(); + strcpy(reply, "OK"); + } else if (strcmp(&config[19], "off") == 0) { + _prefs->flood_retry_advert_enabled = 0; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be on or off"); + } + } else if (memcmp(config, "flood.retry.bridge ", 19) == 0) { + if (strcmp(&config[19], "on") == 0) { + _prefs->flood_retry_bridge_enabled = 1; + savePrefs(); + strcpy(reply, "OK"); + } else if (strcmp(&config[19], "off") == 0) { + _prefs->flood_retry_bridge_enabled = 0; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be on or off"); + } + } else if (memcmp(config, "flood.retry.bucket ", 19) == 0) { + const char* params = &config[19]; + uint8_t bucket = atoi(params); + const char* list = strchr(params, ' '); + if (bucket < 1 || bucket > FLOOD_RETRY_BRIDGE_BUCKETS || list == NULL || *(list + 1) == 0) { + sprintf(reply, "Error, usage: set flood.retry.bucket <1-%d> ", FLOOD_RETRY_BRIDGE_BUCKETS); + } else if (parseFloodRetryPrefixList(_prefs->flood_retry_bridge_buckets[bucket - 1], + FLOOD_RETRY_BUCKET_PREFIXES, list + 1)) { + savePrefs(); + strcpy(reply, "OK"); + } else { + sprintf(reply, "Error, use up to %u comma-separated 3-byte hex prefixes", + (unsigned int)FLOOD_RETRY_BUCKET_PREFIXES); + } } else if (memcmp(config, "direct.retry.cr ", 16) == 0) { if (strcmp(&config[16], "off") == 0) { _prefs->direct_retry_cr_enabled = 0; @@ -1234,6 +1663,10 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep } else { sprintf(reply, "> %s", _board->isLoRaFemLnaEnabled() ? "on" : "off"); } + } else if (memcmp(config, "tempradioat", 11) == 0 && (config[11] == 0 || config[11] == ' ')) { + _callbacks->formatScheduledRadioParams(true, skipSpacesConst(&config[11]), reply); + } else if (memcmp(config, "radioat", 7) == 0 && (config[7] == 0 || config[7] == ' ')) { + _callbacks->formatScheduledRadioParams(false, skipSpacesConst(&config[7]), reply); } else if (memcmp(config, "radio", 5) == 0) { char freq[16], bw[16]; strcpy(freq, StrHelper::ftoa(_prefs->freq)); @@ -1255,6 +1688,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "> %s", retryPresetName(_prefs->retry_preset)); } else if (memcmp(config, "direct.retry", 12) == 0 && (config[12] == 0 || config[12] == ' ')) { sprintf(reply, "> %s", _prefs->direct_retry_enabled ? "on" : "off"); + } else if (memcmp(config, "direct.retry.heard", 18) == 0) { + sprintf(reply, "> %s", _prefs->direct_retry_recent_enabled ? "on" : "off"); } else if (memcmp(config, "direct.retry.margin", 19) == 0) { char margin[12]; formatSnrDbX4(margin, sizeof(margin), _prefs->direct_retry_snr_margin_x4); @@ -1265,6 +1700,30 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_base_ms); } else if (memcmp(config, "direct.retry.step", 17) == 0) { sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_step_ms); + } else if (memcmp(config, "flood.retry.count", 17) == 0) { + sprintf(reply, "> %d", (uint32_t)_prefs->flood_retry_attempts); + } else if (memcmp(config, "flood.retry.path", 16) == 0) { + char path_gate[8]; + formatFloodRetryPathGate(path_gate, _prefs->flood_retry_max_path); + sprintf(reply, "> %s", path_gate); + } else if (memcmp(config, "flood.retry.prefixes", 20) == 0) { + formatFloodRetryPrefixList(tmp, _prefs->flood_retry_prefixes, FLOOD_RETRY_PREFIX_SLOTS); + sprintf(reply, "> %s", tmp[0] ? tmp : "none"); + } else if (memcmp(config, "flood.retry.ignore", 18) == 0) { + formatFloodRetryPrefixList(tmp, _prefs->flood_retry_ignore_prefixes, FLOOD_RETRY_IGNORE_PREFIXES); + sprintf(reply, "> %s", tmp[0] ? tmp : "none"); + } else if (memcmp(config, "flood.retry.advert", 18) == 0) { + sprintf(reply, "> %s", _prefs->flood_retry_advert_enabled ? "on" : "off"); + } else if (memcmp(config, "flood.retry.bridge", 18) == 0) { + sprintf(reply, "> %s", _prefs->flood_retry_bridge_enabled ? "on" : "off"); + } else if (memcmp(config, "flood.retry.bucket.", 19) == 0) { + uint8_t bucket = atoi(&config[19]); + if (bucket >= 1 && bucket <= FLOOD_RETRY_BRIDGE_BUCKETS) { + formatFloodRetryPrefixList(tmp, _prefs->flood_retry_bridge_buckets[bucket - 1], FLOOD_RETRY_BUCKET_PREFIXES); + sprintf(reply, "> %s", tmp[0] ? tmp : "none"); + } else { + sprintf(reply, "Error, bucket 1-%d", FLOOD_RETRY_BRIDGE_BUCKETS); + } } else if (memcmp(config, "direct.retry.cr", 15) == 0) { if (!_prefs->direct_retry_cr_enabled) { strcpy(reply, "> off"); @@ -1398,6 +1857,18 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep } } +void CommonCLI::handleDelCmd(char* command, char* reply) { + const char* config = &command[4]; + if (memcmp(config, "tempradioat", 11) == 0 && (config[11] == 0 || config[11] == ' ')) { + _callbacks->deleteScheduledRadioParams(true, skipSpacesConst(&config[11]), reply); + } else if (memcmp(config, "radioat", 7) == 0 && (config[7] == 0 || config[7] == ' ')) { + _callbacks->deleteScheduledRadioParams(false, skipSpacesConst(&config[7]), reply); + } else { + strcpy(reply, "unknown del: "); + StrHelper::strncpy(&reply[13], config, 160 - 14); + } +} + static char* skipSpaces(char* s) { while (*s == ' ') s++; return s; diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 5a518ea9..00990703 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -38,6 +38,45 @@ #define DIRECT_RETRY_MOBILE_COUNT 15 #define DIRECT_RETRY_MOBILE_STEP_MS 50 #define DIRECT_RETRY_MOBILE_MARGIN_X4 0 +#define DIRECT_RETRY_RECENT_DEFAULT 1 + +#define FLOOD_RETRY_INFRA_COUNT 1 +#define FLOOD_RETRY_INFRA_MAX_PATH 1 + +#define FLOOD_RETRY_ROOFTOP_COUNT 3 +#define FLOOD_RETRY_ROOFTOP_MAX_PATH 2 + +#define FLOOD_RETRY_MOBILE_COUNT 15 +#define FLOOD_RETRY_MOBILE_MAX_PATH 1 +#define FLOOD_RETRY_ADVERT_DEFAULT 0 + +#define BATTERY_ALERT_LOW_PERCENT_DEFAULT 20 +#define BATTERY_ALERT_CRITICAL_PERCENT_DEFAULT 10 + +#ifndef FLOOD_RETRY_PREFIX_SLOTS + #define FLOOD_RETRY_PREFIX_SLOTS 8 +#endif +#ifndef FLOOD_RETRY_PREFIX_LEN + #define FLOOD_RETRY_PREFIX_LEN 3 +#endif +#ifndef FLOOD_RETRY_BRIDGE_BUCKETS + #define FLOOD_RETRY_BRIDGE_BUCKETS 6 +#endif +#ifndef FLOOD_RETRY_BUCKET_PREFIXES + #define FLOOD_RETRY_BUCKET_PREFIXES 17 +#endif +#ifndef FLOOD_RETRY_IGNORE_PREFIXES + #define FLOOD_RETRY_IGNORE_PREFIXES 8 +#endif +#ifndef FLOOD_RETRY_LIST_PREFIXES + #define FLOOD_RETRY_LIST_PREFIXES ((FLOOD_RETRY_IGNORE_PREFIXES > FLOOD_RETRY_BUCKET_PREFIXES) ? FLOOD_RETRY_IGNORE_PREFIXES : FLOOD_RETRY_BUCKET_PREFIXES) +#endif +#ifndef FLOOD_RETRY_LIST_TEXT_MAX + #define FLOOD_RETRY_LIST_TEXT_MAX (FLOOD_RETRY_LIST_PREFIXES * FLOOD_RETRY_PREFIX_LEN * 2 + FLOOD_RETRY_LIST_PREFIXES) +#endif +#ifndef COMMON_CLI_TMP_LEN + #define COMMON_CLI_TMP_LEN ((FLOOD_RETRY_LIST_TEXT_MAX > (PRV_KEY_SIZE * 2 + 4)) ? FLOOD_RETRY_LIST_TEXT_MAX : (PRV_KEY_SIZE * 2 + 4)) +#endif #define DIRECT_RETRY_CR4_MIN_SNR_X4_DEFAULT 40 #define DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT 30 @@ -106,6 +145,17 @@ struct NodePrefs { // persisted to file uint8_t direct_retry_enabled; uint8_t direct_retry_cr_enabled; uint8_t direct_retry_prefs_magic[2]; + uint8_t flood_retry_attempts; + uint8_t flood_retry_max_path; + uint8_t flood_retry_prefixes[FLOOD_RETRY_PREFIX_SLOTS][FLOOD_RETRY_PREFIX_LEN]; + uint8_t flood_retry_bridge_enabled; + uint8_t flood_retry_bridge_buckets[FLOOD_RETRY_BRIDGE_BUCKETS][FLOOD_RETRY_BUCKET_PREFIXES][FLOOD_RETRY_PREFIX_LEN]; + uint8_t flood_retry_ignore_prefixes[FLOOD_RETRY_IGNORE_PREFIXES][FLOOD_RETRY_PREFIX_LEN]; + uint8_t flood_retry_advert_enabled; + uint8_t battery_alert_enabled; + uint8_t battery_alert_low_percent; + uint8_t battery_alert_critical_percent; + uint8_t direct_retry_recent_enabled; }; class CommonCLICallbacks { @@ -145,6 +195,27 @@ public: virtual void saveIdentity(const mesh::LocalIdentity& new_id) = 0; virtual void clearStats() = 0; virtual void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) = 0; + virtual void addScheduledRadioParams(bool temporary, float freq, float bw, uint8_t sf, uint8_t cr, + uint32_t start_time, uint32_t end_time, char* reply) { + (void)temporary; + (void)freq; + (void)bw; + (void)sf; + (void)cr; + (void)start_time; + (void)end_time; + strcpy(reply, "Error: unsupported"); + } + virtual void formatScheduledRadioParams(bool temporary, const char* selector, char* reply) { + (void)temporary; + (void)selector; + strcpy(reply, "Error: unsupported"); + } + virtual void deleteScheduledRadioParams(bool temporary, const char* selector, char* reply) { + (void)temporary; + (void)selector; + strcpy(reply, "Error: unsupported"); + } virtual void startRegionsLoad() { // no op by default @@ -177,7 +248,7 @@ class CommonCLI { SensorManager* _sensors; RegionMap* _region_map; ClientACL* _acl; - char tmp[PRV_KEY_SIZE*2 + 4]; + char tmp[COMMON_CLI_TMP_LEN]; mesh::RTCClock* getRTCClock() { return _rtc; } void savePrefs(); @@ -186,6 +257,7 @@ class CommonCLI { void handleRegionCmd(char* command, char* reply); void handleGetCmd(uint32_t sender_timestamp, char* command, char* reply); void handleSetCmd(uint32_t sender_timestamp, char* command, char* reply); + void handleDelCmd(char* command, char* reply); public: CommonCLI(mesh::MainBoard& board, mesh::RTCClock& rtc, SensorManager& sensors, RegionMap& region_map, ClientACL& acl, NodePrefs* prefs, CommonCLICallbacks* callbacks) diff --git a/src/helpers/ESP32Board.cpp b/src/helpers/ESP32Board.cpp index 7da2c7ac..563acc9e 100644 --- a/src/helpers/ESP32Board.cpp +++ b/src/helpers/ESP32Board.cpp @@ -13,6 +13,12 @@ bool ESP32Board::startOTAUpdate(const char* id, char reply[]) { inhibit_sleep = true; // prevent sleep during OTA + + if (ota_server != nullptr) { + sprintf(reply, "Started: http://%s/update", WiFi.softAPIP().toString().c_str()); + return true; + } + WiFi.softAP("MeshCore-OTA", NULL); sprintf(reply, "Started: http://%s/update", WiFi.softAPIP().toString().c_str()); @@ -23,18 +29,36 @@ bool ESP32Board::startOTAUpdate(const char* id, char reply[]) { static char home_buf[90]; sprintf(home_buf, "

Hi! I am a MeshCore Repeater. ID: %s

", id); - AsyncWebServer* server = new AsyncWebServer(80); + ota_server = new AsyncWebServer(80); - server->on("/", HTTP_GET, [](AsyncWebServerRequest *request) { + ota_server->on("/", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(200, "text/html", home_buf); }); - server->on("/log", HTTP_GET, [](AsyncWebServerRequest *request) { + ota_server->on("/log", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(SPIFFS, "/packet_log", "text/plain"); }); AsyncElegantOTA.setID(id_buf); - AsyncElegantOTA.begin(server); // Start ElegantOTA - server->begin(); + AsyncElegantOTA.begin(ota_server); // Start ElegantOTA + ota_server->begin(); + + return true; +} + +bool ESP32Board::stopOTAUpdate(char reply[]) { + if (ota_server == nullptr) { + strcpy(reply, "OK - OTA not running"); + return true; + } + + ota_server->end(); + delete ota_server; + ota_server = nullptr; + WiFi.softAPdisconnect(true); + inhibit_sleep = false; + + strcpy(reply, "OK - OTA stopped"); + MESH_DEBUG_PRINTLN("stopOTAUpdate: %s", reply); return true; } @@ -43,6 +67,10 @@ bool ESP32Board::startOTAUpdate(const char* id, char reply[]) { bool ESP32Board::startOTAUpdate(const char* id, char reply[]) { return false; // not supported } + +bool ESP32Board::stopOTAUpdate(char reply[]) { + return false; // not supported +} #endif void ESP32Board::powerOff() { diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index 1f02b27a..1644fa6a 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -16,10 +16,13 @@ #include "esp_system.h" #include +class AsyncWebServer; + class ESP32Board : public mesh::MainBoard { protected: uint8_t startup_reason; bool inhibit_sleep = false; + AsyncWebServer* ota_server = nullptr; static inline portMUX_TYPE sleepMux = portMUX_INITIALIZER_UNLOCKED; public: @@ -155,6 +158,15 @@ public: } bool startOTAUpdate(const char* id, char reply[]) override; + bool stopOTAUpdate(char reply[]) override; + + bool isUsbDataConnected() override { +#if defined(ARDUINO_USB_CDC_ON_BOOT) && ARDUINO_USB_CDC_ON_BOOT + return (bool)Serial; +#else + return false; +#endif + } void setInhibitSleep(bool inhibit) { inhibit_sleep = inhibit; diff --git a/src/helpers/NRF52Board.cpp b/src/helpers/NRF52Board.cpp index beee3212..da5eb0f9 100644 --- a/src/helpers/NRF52Board.cpp +++ b/src/helpers/NRF52Board.cpp @@ -3,18 +3,33 @@ #include #include +#include "ble_gap.h" +#include "ble_hci.h" #include static BLEDfu bledfu; +static uint16_t ota_conn_handle = BLE_CONN_HANDLE_INVALID; +static bool ota_active = false; +static bool ota_ble_started = false; + +static void format_ota_reply(char reply[]) { + uint8_t mac_addr[6]; + memset(mac_addr, 0, sizeof(mac_addr)); + Bluefruit.getAddr(mac_addr); + sprintf(reply, "OK - mac: %02X:%02X:%02X:%02X:%02X:%02X", mac_addr[5], mac_addr[4], mac_addr[3], + mac_addr[2], mac_addr[1], mac_addr[0]); +} static void connect_callback(uint16_t conn_handle) { - (void)conn_handle; + ota_conn_handle = conn_handle; MESH_DEBUG_PRINTLN("BLE client connected"); } static void disconnect_callback(uint16_t conn_handle, uint8_t reason) { - (void)conn_handle; (void)reason; + if (ota_conn_handle == conn_handle) { + ota_conn_handle = BLE_CONN_HANDLE_INVALID; + } MESH_DEBUG_PRINTLN("BLE client disconnected"); } @@ -252,6 +267,14 @@ bool NRF52Board::isExternalPowered() { } } +bool NRF52Board::isUsbDataConnected() { +#ifdef USE_TINYUSB + return Serial.dtr(); +#else + return false; +#endif +} + void NRF52Board::sleep(uint32_t secs) { // Clear FPU interrupt flags to avoid insomnia // see errata 87 for details https://docs.nordicsemi.com/bundle/errata_nRF52840_Rev3/page/ERR/nRF52840/Rev3/latest/anomaly_840_87.html @@ -349,13 +372,27 @@ bool NRF52Board::getBootloaderVersion(char* out, size_t max_len) { } bool NRF52Board::startOTAUpdate(const char *id, char reply[]) { - // Config the peripheral connection with maximum bandwidth - // more SRAM required by SoftDevice - // Note: All config***() function must be called before begin() - Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); - Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16); + (void)id; + + if (ota_active) { + format_ota_reply(reply); + return true; + } + + if (!ota_ble_started) { + // Config the peripheral connection with maximum bandwidth + // more SRAM required by SoftDevice + // Note: All config***() function must be called before begin() + Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); + Bluefruit.configPrphConn(92, BLE_GAP_EVENT_LENGTH_MIN, 16, 16); + + Bluefruit.begin(1, 0); + ota_ble_started = true; + + // To be consistent OTA DFU should be added first if it exists + bledfu.begin(); + } - Bluefruit.begin(1, 0); // Set max power. Accepted values are: -40, -30, -20, -16, -12, -8, -4, 0, 4 Bluefruit.setTxPower(4); // Set the BLE device name @@ -364,8 +401,8 @@ bool NRF52Board::startOTAUpdate(const char *id, char reply[]) { Bluefruit.Periph.setConnectCallback(connect_callback); Bluefruit.Periph.setDisconnectCallback(disconnect_callback); - // To be consistent OTA DFU should be added first if it exists - bledfu.begin(); + Bluefruit.Advertising.clearData(); + Bluefruit.ScanResponse.clearData(); // Set up and start advertising // Advertising packet @@ -387,12 +424,27 @@ bool NRF52Board::startOTAUpdate(const char *id, char reply[]) { Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds - uint8_t mac_addr[6]; - memset(mac_addr, 0, sizeof(mac_addr)); - Bluefruit.getAddr(mac_addr); - sprintf(reply, "OK - mac: %02X:%02X:%02X:%02X:%02X:%02X", mac_addr[5], mac_addr[4], mac_addr[3], - mac_addr[2], mac_addr[1], mac_addr[0]); + ota_active = true; + format_ota_reply(reply); return true; } + +bool NRF52Board::stopOTAUpdate(char reply[]) { + if (!ota_active) { + strcpy(reply, "OK - OTA not running"); + return true; + } + + Bluefruit.Advertising.restartOnDisconnect(false); + Bluefruit.Advertising.stop(); + if (ota_conn_handle != BLE_CONN_HANDLE_INVALID) { + sd_ble_gap_disconnect(ota_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); + ota_conn_handle = BLE_CONN_HANDLE_INVALID; + } + ota_active = false; + + strcpy(reply, "OK - OTA stopped"); + return true; +} #endif diff --git a/src/helpers/NRF52Board.h b/src/helpers/NRF52Board.h index cbf4cd49..f578c390 100644 --- a/src/helpers/NRF52Board.h +++ b/src/helpers/NRF52Board.h @@ -53,8 +53,10 @@ public: virtual void powerOff() override; virtual bool getBootloaderVersion(char* version, size_t max_len) override; virtual bool startOTAUpdate(const char *id, char reply[]) override; + virtual bool stopOTAUpdate(char reply[]) override; virtual void sleep(uint32_t secs) override; bool isExternalPowered() override; + bool isUsbDataConnected() override; #ifdef NRF52_POWER_MANAGEMENT uint16_t getBootVoltage() override { return boot_voltage_mv; } @@ -77,4 +79,4 @@ public: NRF52BoardDCDC() {} virtual void begin() override; }; -#endif \ No newline at end of file +#endif diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index fb717bfc..f2420473 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -1,6 +1,9 @@ #pragma once #include +#if ARDUINO + #include +#endif #ifdef ESP32 #include @@ -26,6 +29,7 @@ public: uint8_t prefix[MAX_ROUTE_HASH_BYTES]; uint8_t prefix_len; int8_t snr_x4; + uint32_t last_heard_millis; }; private: @@ -194,7 +198,10 @@ public: uint32_t getNumDirectDups() const { return _direct_dups; } uint32_t getNumFloodDups() const { return _flood_dups; } - bool setRecentRepeater(const uint8_t* prefix, uint8_t prefix_len, int8_t snr_x4) { + bool setRecentRepeater(const uint8_t* prefix, uint8_t prefix_len, int8_t snr_x4, + bool snr_locked = false, bool bypass_allow_filter = false) { + (void)snr_locked; + (void)bypass_allow_filter; if (prefix == NULL || prefix_len == 0) { return false; } @@ -211,6 +218,11 @@ public: continue; } existing.snr_x4 = weightedSnrX4RoundUp(existing.snr_x4, snr_x4); +#if ARDUINO + existing.last_heard_millis = millis(); +#else + existing.last_heard_millis = 0; +#endif return true; } @@ -222,15 +234,19 @@ public: } } if (slot_idx < 0) { - // Table is full: evict the weakest observed SNR entry. + // Table is full: evict the oldest heard entry. slot_idx = 0; - int8_t min_snr_x4 = _recent_repeaters[0].snr_x4; +#if ARDUINO + uint32_t now = millis(); + uint32_t oldest_age = (uint32_t)(now - _recent_repeaters[0].last_heard_millis); for (int i = 1; i < MAX_RECENT_REPEATERS; i++) { - if (_recent_repeaters[i].snr_x4 < min_snr_x4) { - min_snr_x4 = _recent_repeaters[i].snr_x4; + uint32_t age = (uint32_t)(now - _recent_repeaters[i].last_heard_millis); + if (age > oldest_age) { + oldest_age = age; slot_idx = i; } } +#endif } RecentRepeaterInfo& slot = _recent_repeaters[slot_idx]; @@ -238,6 +254,11 @@ public: memcpy(slot.prefix, prefix, prefix_len); slot.prefix_len = prefix_len; slot.snr_x4 = snr_x4; +#if ARDUINO + slot.last_heard_millis = millis(); +#else + slot.last_heard_millis = 0; +#endif return true; } bool decrementRecentRepeaterSnrX4(const uint8_t* prefix, uint8_t prefix_len, uint8_t amount_x4 = 1) { diff --git a/src/helpers/radiolib/CustomLR1110Wrapper.h b/src/helpers/radiolib/CustomLR1110Wrapper.h index 4d30f515..85f6e2b4 100644 --- a/src/helpers/radiolib/CustomLR1110Wrapper.h +++ b/src/helpers/radiolib/CustomLR1110Wrapper.h @@ -4,6 +4,10 @@ #include "RadioLibWrappers.h" #include "LR11x0Reset.h" +#ifndef USE_LR1110 +#define USE_LR1110 +#endif + class CustomLR1110Wrapper : public RadioLibWrapper { public: CustomLR1110Wrapper(CustomLR1110& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } diff --git a/variants/minewsemi_me25ls01/platformio.ini b/variants/minewsemi_me25ls01/platformio.ini index 39d4252d..3468e072 100644 --- a/variants/minewsemi_me25ls01/platformio.ini +++ b/variants/minewsemi_me25ls01/platformio.ini @@ -12,6 +12,7 @@ build_flags = ${nrf52_base.build_flags} -D PIN_STATUS_LED=39 -D P_LORA_TX_LED=22 -D RADIO_CLASS=CustomLR1110 + -D USE_LR1110 -D WRAPPER_CLASS=CustomLR1110Wrapper -D LORA_TX_POWER=22 -D ENV_INCLUDE_GPS=0 diff --git a/variants/thinknode_m3/platformio.ini b/variants/thinknode_m3/platformio.ini index 0a3d4eda..f3047ea4 100644 --- a/variants/thinknode_m3/platformio.ini +++ b/variants/thinknode_m3/platformio.ini @@ -12,6 +12,7 @@ build_flags = ${nrf52_base.build_flags} -D PIN_USER_BTN=12 -D PIN_STATUS_LED=35 -D RADIO_CLASS=CustomLR1110 + -D USE_LR1110 -D WRAPPER_CLASS=CustomLR1110Wrapper -D LORA_TX_POWER=22 -D RF_SWITCH_TABLE diff --git a/variants/wio_wm1110/platformio.ini b/variants/wio_wm1110/platformio.ini index a7eac916..e1255df2 100644 --- a/variants/wio_wm1110/platformio.ini +++ b/variants/wio_wm1110/platformio.ini @@ -11,6 +11,7 @@ build_flags = ${nrf52_base.build_flags} -D WIO_WM1110 ; -D MESH_DEBUG=1 -D RADIO_CLASS=CustomLR1110 + -D USE_LR1110 -D WRAPPER_CLASS=CustomLR1110Wrapper -D LORA_TX_POWER=22 -D RX_BOOSTED_GAIN=true From af131517a7f68b09c4febb9b5a8172a63f908c49 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Wed, 24 Jun 2026 17:16:42 -0700 Subject: [PATCH 153/214] Fix nRF52 USB data detection build --- src/helpers/NRF52Board.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/helpers/NRF52Board.cpp b/src/helpers/NRF52Board.cpp index da5eb0f9..802ff042 100644 --- a/src/helpers/NRF52Board.cpp +++ b/src/helpers/NRF52Board.cpp @@ -6,6 +6,9 @@ #include "ble_gap.h" #include "ble_hci.h" #include +#ifdef USE_TINYUSB +#include +#endif static BLEDfu bledfu; static uint16_t ota_conn_handle = BLE_CONN_HANDLE_INVALID; @@ -268,8 +271,12 @@ bool NRF52Board::isExternalPowered() { } bool NRF52Board::isUsbDataConnected() { -#ifdef USE_TINYUSB - return Serial.dtr(); +#if defined(USE_TINYUSB) + #if defined(CFG_TUD_CDC) && CFG_TUD_CDC + return tud_mounted() && tud_cdc_connected(); + #else + return tud_mounted(); + #endif #else return false; #endif From f606ae9aebc80c67a1e27df8acfa22ef37439ad9 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Wed, 24 Jun 2026 17:28:19 -0700 Subject: [PATCH 154/214] Fix repeater build artifacts and recent paging --- build.sh | 4 +++- docs/cli_commands.md | 1 + docs/halo_keymind_settings.md | 1 + examples/simple_repeater/MyMesh.cpp | 3 +-- src/helpers/CommonCLI.cpp | 35 +++++++++++++++++++++++------ 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/build.sh b/build.sh index 6a01595d..a497256d 100755 --- a/build.sh +++ b/build.sh @@ -1089,7 +1089,9 @@ collect_nrf52_artifacts() { python3 bin/uf2conv/uf2conv.py ".pio/build/${env_name}/firmware.hex" -c -o ".pio/build/${env_name}/firmware.uf2" -f 0xADA52840 || return $? copy_build_output ".pio/build/${env_name}/firmware.uf2" "out/${firmware_filename}.uf2" || return $? - copy_build_output ".pio/build/${env_name}/firmware.zip" "out/${firmware_filename}.zip" || return $? + if [ -f ".pio/build/${env_name}/firmware.zip" ]; then + copy_build_output ".pio/build/${env_name}/firmware.zip" "out/${firmware_filename}.zip" || return $? + fi } collect_stm32_artifacts() { diff --git a/docs/cli_commands.md b/docs/cli_commands.md index fe3464c3..bfbeb8cb 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -1439,6 +1439,7 @@ set direct.retry.cr 20.0,12.0,6.0,2.0 **Usage:** - `get recent.repeater` - `get recent.repeater ` +- `get recent.repeaters ` - `set recent.repeater [snr_db]` - `clear recent.repeater` diff --git a/docs/halo_keymind_settings.md b/docs/halo_keymind_settings.md index 62bc4779..18400104 100644 --- a/docs/halo_keymind_settings.md +++ b/docs/halo_keymind_settings.md @@ -122,6 +122,7 @@ Show learned rows: ```text get recent.repeater get recent.repeater 2 +get recent.repeaters 2 get recent.repeater page 3 ``` diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 8f301412..06cc31f9 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -3076,8 +3076,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char * } } } else if (sender_timestamp == 0 && sender == NULL - && memcmp(command, "get recent.repeater", 19) == 0 - && (command[19] == 0 || command[19] == ' ')) { + && (strcmp(command, "get recent.repeater") == 0 || strcmp(command, "get recent.repeaters") == 0)) { printRecentRepeatersSerial(); reply_start[0] = 0; } else if (sender_timestamp == 0 && strcmp(command, "get acl") == 0) { diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 788e4b33..bb06542f 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -47,6 +47,31 @@ static uint32_t _atoi(const char* sp) { return n; } +static bool parseRecentRepeaterGet(const char* config, int& page) { + if (strncmp(config, "recent.repeater", 15) != 0) { + return false; + } + + const char* cursor = &config[15]; + if (*cursor == 's') { + cursor++; + } + if (*cursor != 0 && *cursor != ' ') { + return false; + } + + while (*cursor == ' ') cursor++; + if (strncmp(cursor, "page", 4) == 0 && (cursor[4] == 0 || cursor[4] == ' ')) { + cursor += 4; + while (*cursor == ' ') cursor++; + } + + page = 1; + if (*cursor) page = _atoi(cursor); + if (page < 1) page = 1; + return true; +} + static bool isValidName(const char *n) { while (*n) { if (*n == '[' || *n == ']' || *n == '\\' || *n == ':' || *n == ',' || *n == '?' || *n == '*') return false; @@ -1617,6 +1642,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* reply) { const char* config = &command[4]; + int recent_page = 1; if (memcmp(config, "dutycycle", 9) == 0) { float dc = 100.0f / (_prefs->airtime_factor + 1.0f); int dc_int = (int)dc; @@ -1735,13 +1761,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep formatSnrDbX4(cr8, sizeof(cr8), _prefs->direct_retry_cr8_snr_x4); sprintf(reply, "> %s,%s,%s,%s", cr4, cr5, cr7, cr8); } - } else if (memcmp(config, "recent.repeater", 15) == 0) { - int page = 1; - const char* cursor = &config[15]; - while (*cursor == ' ') cursor++; - if (*cursor) page = _atoi(cursor); - if (page < 1) page = 1; - _callbacks->formatRecentRepeatersReply(reply, page); + } else if (parseRecentRepeaterGet(config, recent_page)) { + _callbacks->formatRecentRepeatersReply(reply, recent_page); } else if (memcmp(config, "owner.info", 10) == 0) { auto start = reply; *reply++ = '>'; From 4e63941c92c68e87c71c785a60eb4872045443ef Mon Sep 17 00:00:00 2001 From: mikecarper Date: Thu, 25 Jun 2026 01:07:32 -0700 Subject: [PATCH 155/214] Fix repeater build compile errors --- src/Dispatcher.h | 2 +- src/helpers/bridges/ESPNowBridge.cpp | 13 +++++++++++-- src/helpers/bridges/ESPNowBridge.h | 9 +++++++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/Dispatcher.h b/src/Dispatcher.h index cb86bb4b..e6bf9ebd 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -189,6 +189,7 @@ protected: virtual unsigned long getDutyCycleWindowMs() const { return 3600000; } const Packet* getOutboundInFlight() const { return outbound; } bool queueOutboundPacket(Packet* packet, uint8_t priority, uint32_t delay_millis); + bool tryParsePacket(Packet* pkt, const uint8_t* raw, int len); public: void begin(); @@ -215,7 +216,6 @@ public: unsigned long futureMillis(int millis_from_now) const; private: - bool tryParsePacket(Packet* pkt, const uint8_t* raw, int len); void checkRecv(); void checkSend(); }; diff --git a/src/helpers/bridges/ESPNowBridge.cpp b/src/helpers/bridges/ESPNowBridge.cpp index b9eb1c10..ac72284c 100644 --- a/src/helpers/bridges/ESPNowBridge.cpp +++ b/src/helpers/bridges/ESPNowBridge.cpp @@ -9,11 +9,20 @@ ESPNowBridge *ESPNowBridge::_instance = nullptr; // Static callback wrappers -void ESPNowBridge::recv_cb(const uint8_t *mac, const uint8_t *data, int32_t len) { +#if ESP_IDF_VERSION_MAJOR >= 5 +void ESPNowBridge::recv_cb(const esp_now_recv_info_t *info, const uint8_t *data, int len) { + const uint8_t *mac = info != nullptr ? info->src_addr : nullptr; if (_instance) { _instance->onDataRecv(mac, data, len); } } +#else +void ESPNowBridge::recv_cb(const uint8_t *mac, const uint8_t *data, int len) { + if (_instance) { + _instance->onDataRecv(mac, data, len); + } +} +#endif void ESPNowBridge::send_cb(const uint8_t *mac, esp_now_send_status_t status) { if (_instance) { @@ -100,7 +109,7 @@ void ESPNowBridge::xorCrypt(uint8_t *data, size_t len) { } } -void ESPNowBridge::onDataRecv(const uint8_t *mac, const uint8_t *data, int32_t len) { +void ESPNowBridge::onDataRecv(const uint8_t *mac, const uint8_t *data, int len) { // Ignore packets that are too small to contain header + checksum if (len < (BRIDGE_MAGIC_SIZE + BRIDGE_CHECKSUM_SIZE)) { BRIDGE_DEBUG_PRINTLN("RX packet too small, len=%d\n", len); diff --git a/src/helpers/bridges/ESPNowBridge.h b/src/helpers/bridges/ESPNowBridge.h index 431a036b..95658875 100644 --- a/src/helpers/bridges/ESPNowBridge.h +++ b/src/helpers/bridges/ESPNowBridge.h @@ -2,6 +2,7 @@ #include "MeshCore.h" #include "esp_now.h" +#include "esp_idf_version.h" #include "helpers/bridges/BridgeBase.h" #ifdef WITH_ESPNOW_BRIDGE @@ -42,7 +43,11 @@ class ESPNowBridge : public BridgeBase { private: static ESPNowBridge *_instance; - static void recv_cb(const uint8_t *mac, const uint8_t *data, int32_t len); +#if ESP_IDF_VERSION_MAJOR >= 5 + static void recv_cb(const esp_now_recv_info_t *info, const uint8_t *data, int len); +#else + static void recv_cb(const uint8_t *mac, const uint8_t *data, int len); +#endif static void send_cb(const uint8_t *mac, esp_now_send_status_t status); /** @@ -90,7 +95,7 @@ private: * @param data Received data * @param len Length of received data */ - void onDataRecv(const uint8_t *mac, const uint8_t *data, int32_t len); + void onDataRecv(const uint8_t *mac, const uint8_t *data, int len); /** * ESP-NOW send callback From 98f8b11c572aa0a8b6ae81cc986b9ac7919e7279 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Thu, 25 Jun 2026 14:02:01 -0700 Subject: [PATCH 156/214] Fix ESP32 repeater build targets --- src/helpers/ESP32Board.cpp | 2 ++ src/helpers/ESP32Board.h | 18 +++++++++++++++--- variants/tenstar_c3/platformio.ini | 1 + variants/tenstar_c3/target.h | 3 +-- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/helpers/ESP32Board.cpp b/src/helpers/ESP32Board.cpp index 563acc9e..12715b7e 100644 --- a/src/helpers/ESP32Board.cpp +++ b/src/helpers/ESP32Board.cpp @@ -86,6 +86,7 @@ void ESP32Board::enterDeepSleep(uint32_t secs) { // Power off LoRa radio_driver.powerOff(); + #ifdef P_LORA_NSS // Keep LoRa inactive during deepsleep digitalWrite(P_LORA_NSS, HIGH); #if CONFIG_IDF_TARGET_ESP32C3 @@ -93,6 +94,7 @@ void ESP32Board::enterDeepSleep(uint32_t secs) { #else rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); #endif + #endif // Power off GPS if any if (sensors.getLocationProvider() != NULL) { diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index 1644fa6a..a418bf36 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -70,7 +70,11 @@ public: void enterDeepSleep(uint32_t secs); uint32_t getIRQGpio() override { + #ifdef P_LORA_DIO_1 return P_LORA_DIO_1; // default for SX1262 + #else + return -1; + #endif } void sleep(uint32_t secs) override { @@ -80,14 +84,22 @@ public: return; } - // Set GPIO wakeup - gpio_num_t wakeupPin = (gpio_num_t)getIRQGpio(); - // Configure timer wakeup if (secs > 0) { esp_sleep_enable_timer_wakeup(secs * 1000000ULL); // Wake up periodically to do scheduled jobs } + uint32_t irqGpio = getIRQGpio(); + if (irqGpio == static_cast(-1)) { + if (secs > 0) { + esp_light_sleep_start(); + } + return; + } + + // Set GPIO wakeup + gpio_num_t wakeupPin = (gpio_num_t)irqGpio; + // Disable CPU interrupt servicing portENTER_CRITICAL(&sleepMux); diff --git a/variants/tenstar_c3/platformio.ini b/variants/tenstar_c3/platformio.ini index e7988823..45e9fc0c 100644 --- a/variants/tenstar_c3/platformio.ini +++ b/variants/tenstar_c3/platformio.ini @@ -4,6 +4,7 @@ board = esp32-c3-devkitm-1 build_flags = ${esp32_base.build_flags} -I variants/tenstar_c3 + -I variants/xiao_c3 -D ESP32_CPU_FREQ=80 -D LORA_TX_BOOST_PIN=4 -D P_LORA_TX_NEOPIXEL_LED=10 diff --git a/variants/tenstar_c3/target.h b/variants/tenstar_c3/target.h index b3ee6d17..a27efac3 100644 --- a/variants/tenstar_c3/target.h +++ b/variants/tenstar_c3/target.h @@ -3,7 +3,7 @@ #define RADIOLIB_STATIC_ONLY 1 #include #include -#include +#include #include #include #include @@ -16,4 +16,3 @@ extern SensorManager sensors; bool radio_init(); mesh::LocalIdentity radio_new_identity(); - From 5d0ff7fe6e402704dd8c3ad10199ca8054c731b2 Mon Sep 17 00:00:00 2001 From: taco Date: Fri, 26 Jun 2026 07:06:37 +1000 Subject: [PATCH 157/214] remove guard on MyMesh::setRxBoostedGain() --- examples/simple_repeater/MyMesh.cpp | 2 -- examples/simple_repeater/MyMesh.h | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 5cc3a9a1..5f928452 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1062,11 +1062,9 @@ void MyMesh::setTxPower(int8_t power_dbm) { radio_driver.setTxPower(power_dbm); } -#if defined(USE_SX1262) || defined(USE_SX1268) void MyMesh::setRxBoostedGain(bool enable) { radio_driver.setRxBoostedGainMode(enable); } -#endif void MyMesh::formatNeighborsReply(char *reply) { char *dp = reply; diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 24c4b1f2..e66f7e59 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -252,7 +252,6 @@ public: // To check if there is pending work bool hasPendingWork() const; -#if defined(USE_SX1262) || defined(USE_SX1268) void setRxBoostedGain(bool enable) override; -#endif + }; From 9c7d71b9be9115e776d7aae28030d09023813a84 Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Mon, 8 Dec 2025 11:21:29 +0100 Subject: [PATCH 158/214] Added support for the nibble zero connect --- variants/nibble_zero_connect/platformio.ini | 163 ++++++++++++++++++++ variants/nibble_zero_connect/target.cpp | 50 ++++++ variants/nibble_zero_connect/target.h | 31 ++++ 3 files changed, 244 insertions(+) create mode 100644 variants/nibble_zero_connect/platformio.ini create mode 100644 variants/nibble_zero_connect/target.cpp create mode 100644 variants/nibble_zero_connect/target.h diff --git a/variants/nibble_zero_connect/platformio.ini b/variants/nibble_zero_connect/platformio.ini new file mode 100644 index 00000000..4e40ff09 --- /dev/null +++ b/variants/nibble_zero_connect/platformio.ini @@ -0,0 +1,163 @@ +[nibble_zero_connect_base] +extends = esp32_base +board = esp32-s3-zero +build_flags = + ${esp32_base.build_flags} + -I variants/nibble_zero_connect + -D NIBBLE_ZERO_CONNECT + -D P_LORA_DIO_1=4 + -D P_LORA_NSS=10 + -D P_LORA_RESET=6 + -D P_LORA_BUSY=5 + -D P_LORA_SCLK=12 + -D P_LORA_MISO=13 + -D P_LORA_MOSI=11 + -D PIN_USER_BTN=1 + -D PIN_BOARD_SDA=8 + -D PIN_BOARD_SCL=7 + -D PIN_STATUS_LED=39 + -D P_LORA_TX_LED=39 + -D DISPLAY_ROTATION=0 + -D HAS_NEOPIXEL + -D NEOPIXEL_COUNT=1 + -D NEOPIXEL_DATA=17 + -D NEOPIXEL_TYPE=(NEO_GRB+NEO_KHZ800) + -D SX126X_DIO2_AS_RF_SWITCH=true + -D SX126X_DIO3_TCXO_VOLTAGE=1.8 + -D SX126X_CURRENT_LIMIT=140 + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D SX126X_RX_BOOSTED_GAIN=1 +build_src_filter = ${esp32_base.build_src_filter} + +<../variants/nibble_zero_connect> +lib_deps = + ${esp32_base.lib_deps} + adafruit/Adafruit SSD1306 @ ^2.5.13 + adafruit/Adafruit NeoPixel @ ^1.12.3 + +[env:nibble_zero_connect_repeater] +extends = nibble_zero_connect_base +build_flags = + ${nibble_zero_connect_base.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D ADVERT_NAME='"Nibble Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +build_src_filter = ${nibble_zero_connect_base.build_src_filter} + + + +<../examples/simple_repeater> +lib_deps = + ${nibble_zero_connect_base.lib_deps} + ${esp32_ota.lib_deps} + +[env:nibble_zero_connect_repeater_bridge_espnow] +extends = nibble_zero_connect_base +build_flags = + ${nibble_zero_connect_base.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D ADVERT_NAME='"ESPNow Bridge"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 + -D WITH_ESPNOW_BRIDGE=1 +build_src_filter = ${nibble_zero_connect_base.build_src_filter} + + + + + +<../examples/simple_repeater> +lib_deps = + ${nibble_zero_connect_base.lib_deps} + ${esp32_ota.lib_deps} + +[env:nibble_zero_connect_terminal_chat] +extends = nibble_zero_connect_base +build_flags = + ${nibble_zero_connect_base.build_flags} + -D MAX_CONTACTS=300 + -D MAX_GROUP_CHANNELS=1 +build_src_filter = ${nibble_zero_connect_base.build_src_filter} + +<../examples/simple_secure_chat/main.cpp> +lib_deps = + ${nibble_zero_connect_base.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:nibble_zero_connect_room_server] +extends = nibble_zero_connect_base +build_flags = + ${nibble_zero_connect_base.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D ADVERT_NAME='"Nibble Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +build_src_filter = ${nibble_zero_connect_base.build_src_filter} + + + +<../examples/simple_room_server> +lib_deps = + ${nibble_zero_connect_base.lib_deps} + ${esp32_ota.lib_deps} + +[env:nibble_zero_connect_companion_radio_usb] +extends = nibble_zero_connect_base +build_flags = + ${nibble_zero_connect_base.build_flags} + -I examples/companion_radio/ui-new + -D DISPLAY_CLASS=SSD1306Display + -D MAX_CONTACTS=300 + -D MAX_GROUP_CHANNELS=8 +build_src_filter = ${nibble_zero_connect_base.build_src_filter} + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${nibble_zero_connect_base.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:nibble_zero_connect_companion_radio_ble] +extends = nibble_zero_connect_base +build_flags = + ${nibble_zero_connect_base.build_flags} + -I examples/companion_radio/ui-new + -D DISPLAY_CLASS=SSD1306Display + -D MAX_CONTACTS=300 + -D MAX_GROUP_CHANNELS=8 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 +build_src_filter = ${nibble_zero_connect_base.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${nibble_zero_connect_base.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:nibble_zero_connect_companion_radio_wifi] +extends = nibble_zero_connect_base +build_flags = + ${nibble_zero_connect_base.build_flags} + -I examples/companion_radio/ui-new + -D DISPLAY_CLASS=SSD1306Display + -D MAX_CONTACTS=300 + -D MAX_GROUP_CHANNELS=8 + -D WIFI_DEBUG_LOGGING=1 + -D WIFI_SSID='"myssid"' + -D WIFI_PWD='"mypwd"' +build_src_filter = ${nibble_zero_connect_base.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${nibble_zero_connect_base.lib_deps} + densaugeo/base64 @ ~1.4.0 + + diff --git a/variants/nibble_zero_connect/target.cpp b/variants/nibble_zero_connect/target.cpp new file mode 100644 index 00000000..73ac0a84 --- /dev/null +++ b/variants/nibble_zero_connect/target.cpp @@ -0,0 +1,50 @@ +#include +#include "target.h" + +ESP32Board board; + +static SPIClass spi; +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); +WRAPPER_CLASS radio_driver(radio, board); + +ESP32RTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); +SensorManager sensors; + +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true, true); +#endif + +#ifndef LORA_CR + #define LORA_CR 5 +#endif + +bool radio_init() { + fallback_clock.begin(); + rtc_clock.begin(Wire); + + return radio.std_init(&spi); +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); +} + +void radio_set_tx_power(uint8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); +} + + diff --git a/variants/nibble_zero_connect/target.h b/variants/nibble_zero_connect/target.h new file mode 100644 index 00000000..ea558f63 --- /dev/null +++ b/variants/nibble_zero_connect/target.h @@ -0,0 +1,31 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include +#ifdef DISPLAY_CLASS + #include + #include +#endif + +extern ESP32Board board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern SensorManager sensors; + +#ifdef DISPLAY_CLASS + extern DISPLAY_CLASS display; + extern MomentaryButton user_btn; +#endif + +bool radio_init(); +uint32_t radio_get_rng_seed(); +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); +void radio_set_tx_power(uint8_t dbm); +mesh::LocalIdentity radio_new_identity(); + + From 69df5c7a9588b6fcb436f474d63179074ea08abb Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Thu, 5 Feb 2026 08:51:16 +0100 Subject: [PATCH 159/214] Remove NeoPixel configuration as its unused. --- variants/nibble_zero_connect/platformio.ini | 5 ----- 1 file changed, 5 deletions(-) diff --git a/variants/nibble_zero_connect/platformio.ini b/variants/nibble_zero_connect/platformio.ini index 4e40ff09..14801920 100644 --- a/variants/nibble_zero_connect/platformio.ini +++ b/variants/nibble_zero_connect/platformio.ini @@ -18,10 +18,6 @@ build_flags = -D PIN_STATUS_LED=39 -D P_LORA_TX_LED=39 -D DISPLAY_ROTATION=0 - -D HAS_NEOPIXEL - -D NEOPIXEL_COUNT=1 - -D NEOPIXEL_DATA=17 - -D NEOPIXEL_TYPE=(NEO_GRB+NEO_KHZ800) -D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_CURRENT_LIMIT=140 @@ -34,7 +30,6 @@ build_src_filter = ${esp32_base.build_src_filter} lib_deps = ${esp32_base.lib_deps} adafruit/Adafruit SSD1306 @ ^2.5.13 - adafruit/Adafruit NeoPixel @ ^1.12.3 [env:nibble_zero_connect_repeater] extends = nibble_zero_connect_base From eb938a9b78f1d8b4b46d694a955115fc001bcd6e Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Tue, 12 May 2026 09:01:38 +0200 Subject: [PATCH 160/214] Address review comments and sync with dev --- variants/nibble_zero_connect/platformio.ini | 14 +++++++------- variants/nibble_zero_connect/target.cpp | 17 +---------------- variants/nibble_zero_connect/target.h | 3 --- 3 files changed, 8 insertions(+), 26 deletions(-) diff --git a/variants/nibble_zero_connect/platformio.ini b/variants/nibble_zero_connect/platformio.ini index 14801920..83495cd8 100644 --- a/variants/nibble_zero_connect/platformio.ini +++ b/variants/nibble_zero_connect/platformio.ini @@ -31,7 +31,7 @@ lib_deps = ${esp32_base.lib_deps} adafruit/Adafruit SSD1306 @ ^2.5.13 -[env:nibble_zero_connect_repeater] +[env:nibble_zero_connect_repeater_] extends = nibble_zero_connect_base build_flags = ${nibble_zero_connect_base.build_flags} @@ -48,7 +48,7 @@ lib_deps = ${nibble_zero_connect_base.lib_deps} ${esp32_ota.lib_deps} -[env:nibble_zero_connect_repeater_bridge_espnow] +[env:nibble_zero_connect_repeater_bridge_espnow_] extends = nibble_zero_connect_base build_flags = ${nibble_zero_connect_base.build_flags} @@ -67,7 +67,7 @@ lib_deps = ${nibble_zero_connect_base.lib_deps} ${esp32_ota.lib_deps} -[env:nibble_zero_connect_terminal_chat] +[env:nibble_zero_connect_terminal_chat_] extends = nibble_zero_connect_base build_flags = ${nibble_zero_connect_base.build_flags} @@ -79,7 +79,7 @@ lib_deps = ${nibble_zero_connect_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:nibble_zero_connect_room_server] +[env:nibble_zero_connect_room_server_] extends = nibble_zero_connect_base build_flags = ${nibble_zero_connect_base.build_flags} @@ -96,7 +96,7 @@ lib_deps = ${nibble_zero_connect_base.lib_deps} ${esp32_ota.lib_deps} -[env:nibble_zero_connect_companion_radio_usb] +[env:nibble_zero_connect_companion_radio_usb_] extends = nibble_zero_connect_base build_flags = ${nibble_zero_connect_base.build_flags} @@ -113,7 +113,7 @@ lib_deps = ${nibble_zero_connect_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:nibble_zero_connect_companion_radio_ble] +[env:nibble_zero_connect_companion_radio_ble_] extends = nibble_zero_connect_base build_flags = ${nibble_zero_connect_base.build_flags} @@ -134,7 +134,7 @@ lib_deps = ${nibble_zero_connect_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:nibble_zero_connect_companion_radio_wifi] +[env:nibble_zero_connect_companion_radio_wifi_] extends = nibble_zero_connect_base build_flags = ${nibble_zero_connect_base.build_flags} diff --git a/variants/nibble_zero_connect/target.cpp b/variants/nibble_zero_connect/target.cpp index 73ac0a84..d53b6a1c 100644 --- a/variants/nibble_zero_connect/target.cpp +++ b/variants/nibble_zero_connect/target.cpp @@ -13,7 +13,7 @@ SensorManager sensors; #ifdef DISPLAY_CLASS DISPLAY_CLASS display; - MomentaryButton user_btn(PIN_USER_BTN, 1000, true, true); + MomentaryButton user_btn(PIN_USER_BTN, 1000, true); #endif #ifndef LORA_CR @@ -27,21 +27,6 @@ bool radio_init() { return radio.std_init(&spi); } -uint32_t radio_get_rng_seed() { - return radio.random(0x7FFFFFFF); -} - -void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { - radio.setFrequency(freq); - radio.setSpreadingFactor(sf); - radio.setBandwidth(bw); - radio.setCodingRate(cr); -} - -void radio_set_tx_power(uint8_t dbm) { - radio.setOutputPower(dbm); -} - mesh::LocalIdentity radio_new_identity() { RadioNoiseListener rng(radio); return mesh::LocalIdentity(&rng); diff --git a/variants/nibble_zero_connect/target.h b/variants/nibble_zero_connect/target.h index ea558f63..87c55f0f 100644 --- a/variants/nibble_zero_connect/target.h +++ b/variants/nibble_zero_connect/target.h @@ -23,9 +23,6 @@ extern SensorManager sensors; #endif bool radio_init(); -uint32_t radio_get_rng_seed(); -void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); -void radio_set_tx_power(uint8_t dbm); mesh::LocalIdentity radio_new_identity(); From b8f1fad65446bb22803b6a74b7bbbd6a99a6f761 Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Thu, 25 Jun 2026 23:26:53 +0200 Subject: [PATCH 161/214] Add trailing underscore to nibble_screen_connect env names to exclude from automatic builds --- variants/nibble_screen_connect/platformio.ini | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/variants/nibble_screen_connect/platformio.ini b/variants/nibble_screen_connect/platformio.ini index 20f7dc24..6a2f3dec 100644 --- a/variants/nibble_screen_connect/platformio.ini +++ b/variants/nibble_screen_connect/platformio.ini @@ -34,7 +34,7 @@ lib_deps = adafruit/Adafruit SSD1306 @ ^2.5.13 adafruit/Adafruit NeoPixel @ ^1.12.3 -[env:nibble_screen_connect_repeater] +[env:nibble_screen_connect_repeater_] extends = nibble_screen_connect_base build_flags = ${nibble_screen_connect_base.build_flags} @@ -51,7 +51,7 @@ lib_deps = ${nibble_screen_connect_base.lib_deps} ${esp32_ota.lib_deps} -[env:nibble_screen_connect_repeater_bridge_espnow] +[env:nibble_screen_connect_repeater_bridge_espnow_] extends = nibble_screen_connect_base build_flags = ${nibble_screen_connect_base.build_flags} @@ -70,7 +70,7 @@ lib_deps = ${nibble_screen_connect_base.lib_deps} ${esp32_ota.lib_deps} -[env:nibble_screen_connect_terminal_chat] +[env:nibble_screen_connect_terminal_chat_] extends = nibble_screen_connect_base build_flags = ${nibble_screen_connect_base.build_flags} @@ -82,7 +82,7 @@ lib_deps = ${nibble_screen_connect_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:nibble_screen_connect_room_server] +[env:nibble_screen_connect_room_server_] extends = nibble_screen_connect_base build_flags = ${nibble_screen_connect_base.build_flags} @@ -99,7 +99,7 @@ lib_deps = ${nibble_screen_connect_base.lib_deps} ${esp32_ota.lib_deps} -[env:nibble_screen_connect_companion_radio_usb] +[env:nibble_screen_connect_companion_radio_usb_] extends = nibble_screen_connect_base build_flags = ${nibble_screen_connect_base.build_flags} @@ -116,7 +116,7 @@ lib_deps = ${nibble_screen_connect_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:nibble_screen_connect_companion_radio_ble] +[env:nibble_screen_connect_companion_radio_ble_] extends = nibble_screen_connect_base build_flags = ${nibble_screen_connect_base.build_flags} @@ -137,7 +137,7 @@ lib_deps = ${nibble_screen_connect_base.lib_deps} densaugeo/base64 @ ~1.4.0 -[env:nibble_screen_connect_companion_radio_wifi] +[env:nibble_screen_connect_companion_radio_wifi_] extends = nibble_screen_connect_base build_flags = ${nibble_screen_connect_base.build_flags} @@ -160,7 +160,7 @@ lib_deps = densaugeo/base64 @ ~1.4.0 -[env:nibble_screen_connect_kiss_modem] +[env:nibble_screen_connect_kiss_modem_] extends = nibble_screen_connect_base build_src_filter = ${nibble_screen_connect_base.build_src_filter} +<../examples/kiss_modem/> From 1f9bb6740048c08708718e3b26b628af98fe3ff1 Mon Sep 17 00:00:00 2001 From: taco Date: Fri, 26 Jun 2026 07:44:01 +1000 Subject: [PATCH 162/214] return bool when setting rx boost --- examples/simple_repeater/MyMesh.cpp | 4 ++-- examples/simple_repeater/MyMesh.h | 2 +- src/helpers/CommonCLI.h | 4 ++-- src/helpers/esp32/ESPNOWRadio.h | 2 +- src/helpers/radiolib/CustomLLCC68Wrapper.h | 4 ++-- src/helpers/radiolib/CustomLR1110Wrapper.h | 4 ++-- src/helpers/radiolib/CustomSX1262Wrapper.h | 4 ++-- src/helpers/radiolib/CustomSX1268Wrapper.h | 4 ++-- src/helpers/radiolib/RadioLibWrappers.h | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 5f928452..ca4cfad2 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1062,8 +1062,8 @@ void MyMesh::setTxPower(int8_t power_dbm) { radio_driver.setTxPower(power_dbm); } -void MyMesh::setRxBoostedGain(bool enable) { - radio_driver.setRxBoostedGainMode(enable); +bool MyMesh::setRxBoostedGain(bool enable) { + return radio_driver.setRxBoostedGainMode(enable); } void MyMesh::formatNeighborsReply(char *reply) { diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index e66f7e59..fb091a4c 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -252,6 +252,6 @@ public: // To check if there is pending work bool hasPendingWork() const; - void setRxBoostedGain(bool enable) override; + bool setRxBoostedGain(bool enable) override; }; diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 10cb00c7..f3abcf47 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -111,8 +111,8 @@ public: // no op by default }; - virtual void setRxBoostedGain(bool enable) { - // no op by default + virtual bool setRxBoostedGain(bool enable) { + return false; // CommonCLI reports unsupported if not overridden by wrapper }; }; diff --git a/src/helpers/esp32/ESPNOWRadio.h b/src/helpers/esp32/ESPNOWRadio.h index 67b1448e..f474215a 100644 --- a/src/helpers/esp32/ESPNOWRadio.h +++ b/src/helpers/esp32/ESPNOWRadio.h @@ -38,7 +38,7 @@ public: * These two functions do nothing for ESP-NOW, but are needed for the * Radio interface. */ - virtual void setRxBoostedGainMode(bool) { } + virtual bool setRxBoostedGainMode(bool) { } virtual bool getRxBoostedGainMode() const { return false; } uint32_t intID(); diff --git a/src/helpers/radiolib/CustomLLCC68Wrapper.h b/src/helpers/radiolib/CustomLLCC68Wrapper.h index 8861f76d..851fd644 100644 --- a/src/helpers/radiolib/CustomLLCC68Wrapper.h +++ b/src/helpers/radiolib/CustomLLCC68Wrapper.h @@ -33,8 +33,8 @@ public: void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); } - void setRxBoostedGainMode(bool en) override { - ((CustomLLCC68 *)_radio)->setRxBoostedGainMode(en); + bool setRxBoostedGainMode(bool en) override { + return ((CustomLLCC68 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE; } bool getRxBoostedGainMode() const override { return ((CustomLLCC68 *)_radio)->getRxBoostedGainMode(); diff --git a/src/helpers/radiolib/CustomLR1110Wrapper.h b/src/helpers/radiolib/CustomLR1110Wrapper.h index 13efd25b..fc505283 100644 --- a/src/helpers/radiolib/CustomLR1110Wrapper.h +++ b/src/helpers/radiolib/CustomLR1110Wrapper.h @@ -36,8 +36,8 @@ public: uint8_t getSpreadingFactor() const override { return ((CustomLR1110 *)_radio)->getSpreadingFactor(); } - void setRxBoostedGainMode(bool en) override { - ((CustomLR1110 *)_radio)->setRxBoostedGainMode(en); + bool setRxBoostedGainMode(bool en) override { + return ((CustomLR1110 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE; } bool getRxBoostedGainMode() const override { return ((CustomLR1110 *)_radio)->getRxBoostedGainMode(); diff --git a/src/helpers/radiolib/CustomSX1262Wrapper.h b/src/helpers/radiolib/CustomSX1262Wrapper.h index cc7bb223..1d103f57 100644 --- a/src/helpers/radiolib/CustomSX1262Wrapper.h +++ b/src/helpers/radiolib/CustomSX1262Wrapper.h @@ -40,8 +40,8 @@ public: void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); } - void setRxBoostedGainMode(bool en) override { - ((CustomSX1262 *)_radio)->setRxBoostedGainMode(en); + bool setRxBoostedGainMode(bool en) override { + return ((CustomSX1262 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE; } bool getRxBoostedGainMode() const override { return ((CustomSX1262 *)_radio)->getRxBoostedGainMode(); diff --git a/src/helpers/radiolib/CustomSX1268Wrapper.h b/src/helpers/radiolib/CustomSX1268Wrapper.h index 9ddea78f..bce56b99 100644 --- a/src/helpers/radiolib/CustomSX1268Wrapper.h +++ b/src/helpers/radiolib/CustomSX1268Wrapper.h @@ -37,8 +37,8 @@ public: void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); } - void setRxBoostedGainMode(bool en) override { - ((CustomSX1268 *)_radio)->setRxBoostedGainMode(en); + bool setRxBoostedGainMode(bool en) override { + return ((CustomSX1268 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE; } bool getRxBoostedGainMode() const override { return ((CustomSX1268 *)_radio)->getRxBoostedGainMode(); diff --git a/src/helpers/radiolib/RadioLibWrappers.h b/src/helpers/radiolib/RadioLibWrappers.h index 9943bcab..3091832f 100644 --- a/src/helpers/radiolib/RadioLibWrappers.h +++ b/src/helpers/radiolib/RadioLibWrappers.h @@ -66,7 +66,7 @@ public: float packetScore(float snr, int packet_len) override { return packetScoreInt(snr, 10, packet_len); } // assume sf=10 - virtual void setRxBoostedGainMode(bool) { } + virtual bool setRxBoostedGainMode(bool) { return false; } virtual bool getRxBoostedGainMode() const { return false; } }; From b07aba793724e24b52138ea2a7172d9a3bd34e91 Mon Sep 17 00:00:00 2001 From: taco Date: Fri, 26 Jun 2026 08:45:03 +1000 Subject: [PATCH 163/214] fix: report error when rxgain setting is unsupported or rejected --- src/helpers/CommonCLI.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index dce1c5d8..f82d40e2 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -571,13 +571,15 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep _prefs->disable_fwd = memcmp(&config[7], "off", 3) == 0; savePrefs(); strcpy(reply, _prefs->disable_fwd ? "OK - repeat is now OFF" : "OK - repeat is now ON"); -#if defined(USE_SX1262) || defined(USE_SX1268) || defined(USE_LR1110) } else if (memcmp(config, "radio.rxgain ", 13) == 0) { - _prefs->rx_boosted_gain = memcmp(&config[13], "on", 2) == 0; - strcpy(reply, "OK"); - savePrefs(); - _callbacks->setRxBoostedGain(_prefs->rx_boosted_gain); -#endif + bool enabled = memcmp(&config[13], "on", 2) == 0; + if (_callbacks->setRxBoostedGain(enabled)) { + _prefs->rx_boosted_gain = enabled; + strcpy(reply, "OK"); + savePrefs(); + } else { + strcpy(reply, "Error: unsupported or rejected"); + } } else if (memcmp(config, "radio.fem.rxgain ", 17) == 0) { if (!_board->canControlLoRaFemLna()) { strcpy(reply, "Error: unsupported"); @@ -835,10 +837,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "> %s", StrHelper::ftoa(_prefs->node_lat)); } else if (memcmp(config, "lon", 3) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->node_lon)); -#if defined(USE_SX1262) || defined(USE_SX1268) || defined(USE_LR1110) } else if (memcmp(config, "radio.rxgain", 12) == 0) { sprintf(reply, "> %s", _prefs->rx_boosted_gain ? "on" : "off"); -#endif } else if (memcmp(config, "radio.fem.rxgain", 16) == 0) { if (!_board->canControlLoRaFemLna()) { strcpy(reply, "Error: unsupported"); From f3d4d8cd5eda7428435a1a7769e3891cdab67344 Mon Sep 17 00:00:00 2001 From: taco Date: Fri, 26 Jun 2026 22:36:49 +1000 Subject: [PATCH 164/214] always save boosted gain setting --- src/helpers/CommonCLI.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index f82d40e2..c95e3e34 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -573,13 +573,13 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep strcpy(reply, _prefs->disable_fwd ? "OK - repeat is now OFF" : "OK - repeat is now ON"); } else if (memcmp(config, "radio.rxgain ", 13) == 0) { bool enabled = memcmp(&config[13], "on", 2) == 0; + _prefs->rx_boosted_gain = enabled; + savePrefs(); if (_callbacks->setRxBoostedGain(enabled)) { - _prefs->rx_boosted_gain = enabled; strcpy(reply, "OK"); - savePrefs(); } else { - strcpy(reply, "Error: unsupported or rejected"); - } + strcpy(reply, "Error: unsupported"); + } } else if (memcmp(config, "radio.fem.rxgain ", 17) == 0) { if (!_board->canControlLoRaFemLna()) { strcpy(reply, "Error: unsupported"); From 609e393b46a944750aa5946dc08a56e5bad54be1 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 26 Jun 2026 15:06:02 -0700 Subject: [PATCH 165/214] Fix repeater flood text and recent paging --- docs/cli_commands.md | 2 +- docs/halo_keymind_settings.md | 2 +- examples/simple_repeater/MyMesh.cpp | 43 ++++++++++++++++++++++++++++- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index bfbeb8cb..43ab975f 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -455,7 +455,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore - `send text.flood ` **Parameters:** -- `message`: Text to send to the shared `#repeaters` flood channel, prefixed with this node's name. +- `message`: Text to send to the shared `#repeaters` flood channel, prefixed with this node's name. Any `:` in the node name is sent as `;` so the prefix delimiter stays unambiguous. **Example:** ``` diff --git a/docs/halo_keymind_settings.md b/docs/halo_keymind_settings.md index 18400104..863285eb 100644 --- a/docs/halo_keymind_settings.md +++ b/docs/halo_keymind_settings.md @@ -83,7 +83,7 @@ set flood.retry.ignore none | Command | What it does | How to use | Example | | --- | --- | --- | --- | -| `send text.flood` | Sends a `#repeaters` flood text message formatted as `: `. | `send text.flood ` | `send text.flood checking ridge link` | +| `send text.flood` | Sends a `#repeaters` flood text message formatted as `: `, with `:` in the node name sent as `;`. | `send text.flood ` | `send text.flood checking ridge link` | ## Battery Alerts diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 06cc31f9..fbbec54a 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1617,6 +1617,39 @@ static void formatLocalSnrX4(char* dest, size_t dest_len, int16_t snr_x4) { } } +static bool parseRecentRepeatersPageCommand(const char* command, int& page) { + if (strncmp(command, "get ", 4) != 0) { + return false; + } + + const char* cursor = command + 4; + if (strncmp(cursor, "recent.repeater", 15) != 0) { + return false; + } + cursor += 15; + + if (*cursor == 's') { + cursor++; + } + if (*cursor == 0) { + return false; + } + if (*cursor != ' ') { + return false; + } + + while (*cursor == ' ') cursor++; + if (strncmp(cursor, "page", 4) == 0 && (cursor[4] == 0 || cursor[4] == ' ')) { + cursor += 4; + while (*cursor == ' ') cursor++; + } + + page = 1; + if (*cursor) page = atoi(cursor); + if (page < 1) page = 1; + return true; +} + void MyMesh::formatRecentRepeatersReply(char *reply, int page) { const SimpleMeshTables* tables = static_cast(getTables()); if (tables == NULL) { @@ -2178,8 +2211,13 @@ bool MyMesh::sendRepeatersFloodText(const char* text) { const size_t max_data_len = MAX_PACKET_PAYLOAD - CIPHER_BLOCK_SIZE; const size_t prefix_cap = max_data_len > 5 ? max_data_len - 5 + 1 : 0; + char node_name[sizeof(_prefs.node_name)]; + StrHelper::strncpy(node_name, _prefs.node_name, sizeof(node_name)); + for (char* p = node_name; *p; p++) { + if (*p == ':') *p = ';'; + } int prefix_written = prefix_cap > 0 - ? snprintf((char*)&temp[5], prefix_cap, "%s: ", _prefs.node_name) + ? snprintf((char*)&temp[5], prefix_cap, "%s: ", node_name) : -1; if (prefix_written < 0) { return false; @@ -3010,6 +3048,7 @@ static void formatPathReply(const uint8_t* path, uint8_t path_len, char* out, si void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *command, char *reply) { char* reply_start = reply; + int recent_page = 1; if (region_load_active) { if (StrHelper::isBlank(command)) { // empty/blank line, signal to terminate 'load' operation region_map = temp_map; // copy over the temp instance as new current map @@ -3075,6 +3114,8 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char * strcpy(reply, "Err - bad pubkey"); } } + } else if (sender_timestamp == 0 && sender == NULL && parseRecentRepeatersPageCommand(command, recent_page)) { + formatRecentRepeatersReply(reply, recent_page); } else if (sender_timestamp == 0 && sender == NULL && (strcmp(command, "get recent.repeater") == 0 || strcmp(command, "get recent.repeaters") == 0)) { printRecentRepeatersSerial(); From a8506c916c9a146c71cff70591ea63c4ea824949 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Sat, 27 Jun 2026 09:31:29 +0700 Subject: [PATCH 166/214] Added to Xiao S3 Wio: -D PIN_VBAT_READ=1 ; D0 --- variants/xiao_s3_wio/platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/xiao_s3_wio/platformio.ini b/variants/xiao_s3_wio/platformio.ini index 41420acf..6686de55 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -8,6 +8,7 @@ build_flags = ${esp32_base.build_flags} -I variants/xiao_s3_wio -UENV_INCLUDE_GPS -D SEEED_XIAO_S3 + -D PIN_VBAT_READ=1 ; D0 -D P_LORA_DIO_1=39 -D P_LORA_NSS=41 -D P_LORA_RESET=42 ; RADIOLIB_NC From 4d2f304f63976cc4d2a4b3e67f3eb2df3111723d Mon Sep 17 00:00:00 2001 From: mikecarper Date: Sat, 27 Jun 2026 01:51:23 -0700 Subject: [PATCH 167/214] Tune mesh retry behavior and builds --- build.sh | 90 ++++++++++++++++++++++-- docs/cli_commands.md | 4 +- docs/halo_keymind_settings.md | 10 +-- examples/companion_radio/MyMesh.h | 1 + examples/simple_repeater/MyMesh.cpp | 52 +++++++++++--- examples/simple_repeater/MyMesh.h | 4 ++ examples/simple_room_server/MyMesh.h | 3 + examples/simple_secure_chat/main.cpp | 4 ++ examples/simple_sensor/SensorMesh.h | 3 + src/Mesh.cpp | 100 ++++++++++++++++++++++++--- src/Mesh.h | 11 ++- src/helpers/SimpleMeshTables.h | 32 ++++++++- 12 files changed, 278 insertions(+), 36 deletions(-) diff --git a/build.sh b/build.sh index a497256d..521bfff4 100755 --- a/build.sh +++ b/build.sh @@ -9,6 +9,7 @@ SELECTED_TARGET="" SELECTED_COMMAND_ARGS=() MESHDEBUG_OVERRIDE="" PACKET_LOGGING_OVERRIDE="" +FIRMWARE_VERSION_SUFFIX="" RADIO_SETTINGS_API_URL="https://api.meshcore.nz/api/v1/config" RADIO_SETTING_TITLE="" RADIO_FREQ_OVERRIDE="" @@ -52,6 +53,7 @@ Commands: list|-l: List firmwares available to build. build-firmware : Build the firmware for the given build target. build-firmwares: Build all firmwares for all targets. + build-firmwares-logging-matrix: Build all firmwares twice, first with MESH_DEBUG/MESH_PACKET_LOGGING off and then with both on. build-matching-firmwares : Build all firmwares for build targets containing the string given for . build-companion-firmwares: Build all companion firmwares for all build targets. build-repeater-firmwares: Build all repeater firmwares for all build targets. @@ -67,6 +69,9 @@ $ bash build.sh Build all firmwares for device targets containing the string "RAK_4631" $ bash build.sh build-matching-firmwares +Build all firmwares twice, with logging-off artifacts using the base version and logging-on artifacts using a "-logging" version suffix: +$ bash build.sh build-firmwares-logging-matrix + Build all companion firmwares $ bash build.sh build-companion-firmwares @@ -257,6 +262,7 @@ prompt_for_build_mode() { local options=( "Build one firmware target" "Build all firmwares" + "Build all firmwares twice (logging off, then MESH_DEBUG + MESH_PACKET_LOGGING on)" "Build all repeater firmwares" "Build all companion firmwares" "Build all chat room server firmwares" @@ -282,14 +288,18 @@ prompt_for_build_mode() { return 0 ;; 3) - SELECTED_COMMAND_ARGS=(build-repeater-firmwares) + SELECTED_COMMAND_ARGS=(build-firmwares-logging-matrix) return 0 ;; 4) - SELECTED_COMMAND_ARGS=(build-companion-firmwares) + SELECTED_COMMAND_ARGS=(build-repeater-firmwares) return 0 ;; 5) + SELECTED_COMMAND_ARGS=(build-companion-firmwares) + return 0 + ;; + 6) SELECTED_COMMAND_ARGS=(build-room-server-firmwares) return 0 ;; @@ -308,6 +318,10 @@ prompt_for_debug_build_settings() { echo "Using debug options: meshdebug=${MESHDEBUG_OVERRIDE}, packet_logging=${PACKET_LOGGING_OVERRIDE}" } +is_logging_matrix_command() { + [ "$1" == "build-firmwares-logging-matrix" ] +} + clear_radio_overrides() { RADIO_SETTING_TITLE="" RADIO_FREQ_OVERRIDE="" @@ -983,7 +997,7 @@ is_supported_build_env() { disable_debug_flags() { if [ "$DISABLE_DEBUG" == "1" ]; then - export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -UMESH_DEBUG -UBLE_DEBUG_LOGGING -UWIFI_DEBUG_LOGGING -UBRIDGE_DEBUG -UGPS_NMEA_DEBUG -UCORE_DEBUG_LEVEL -UESPNOW_DEBUG_LOGGING -UDEBUG_RP2040_WIRE -UDEBUG_RP2040_SPI -UDEBUG_RP2040_CORE -UDEBUG_RP2040_PORT -URADIOLIB_DEBUG_SPI -UCFG_DEBUG -URADIOLIB_DEBUG_BASIC -URADIOLIB_DEBUG_PROTOCOL" + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -UMESH_DEBUG -UMESH_PACKET_LOGGING -UBLE_DEBUG_LOGGING -UWIFI_DEBUG_LOGGING -UBRIDGE_DEBUG -UGPS_NMEA_DEBUG -UCORE_DEBUG_LEVEL -UESPNOW_DEBUG_LOGGING -UDEBUG_RP2040_WIRE -UDEBUG_RP2040_SPI -UDEBUG_RP2040_CORE -UDEBUG_RP2040_PORT -URADIOLIB_DEBUG_SPI -DCFG_DEBUG=0 -URADIOLIB_DEBUG_BASIC -URADIOLIB_DEBUG_PROTOCOL" fi } @@ -1021,6 +1035,25 @@ apply_firmware_profile_overrides() { esac } +append_firmware_version_suffix() { + local firmware_version=$1 + local suffix=${2#-} + + if [ -z "$suffix" ]; then + echo "$firmware_version" + return 0 + fi + + case "$firmware_version" in + *-"$suffix") + echo "$firmware_version" + ;; + *) + echo "${firmware_version}-${suffix}" + ;; + esac +} + print_build_flags() { local env_name=$1 @@ -1180,6 +1213,7 @@ build_firmware() { fi firmware_version_string="${firmware_version}-${commit_hash}" + firmware_version_string=$(append_firmware_version_suffix "$firmware_version_string" "$FIRMWARE_VERSION_SUFFIX") firmware_filename="${env_name}-${firmware_version_string}" if [ "${PLATFORMIO_BUILD_FLAGS+x}" ]; then @@ -1238,6 +1272,9 @@ get_bulk_build_resolver_name() { build-firmwares) echo "resolve_all_firmwares" ;; + build-firmwares-logging-matrix) + echo "resolve_all_firmwares" + ;; build-companion-firmwares) echo "resolve_companion_firmwares" ;; @@ -1356,6 +1393,42 @@ run_resolved_build_targets() { return "$build_status" } +run_logging_matrix_build_targets() { + local targets=("$@") + local original_meshdebug_override=$MESHDEBUG_OVERRIDE + local original_packet_logging_override=$PACKET_LOGGING_OVERRIDE + local original_firmware_version_suffix=$FIRMWARE_VERSION_SUFFIX + local build_status=0 + + if [ ${#targets[@]} -eq 0 ]; then + echo "No build targets resolved." + return 1 + fi + + echo "Building ${#targets[@]} target(s) with MESH_DEBUG=off and MESH_PACKET_LOGGING=off." + MESHDEBUG_OVERRIDE="off" + PACKET_LOGGING_OVERRIDE="off" + FIRMWARE_VERSION_SUFFIX="" + run_resolved_build_targets "${targets[@]}" + build_status=$? + + if [ "$build_status" -eq 0 ]; then + echo "Building ${#targets[@]} target(s) with MESH_DEBUG=on and MESH_PACKET_LOGGING=on." + echo "Logging-on artifacts use firmware version suffix: -logging" + MESHDEBUG_OVERRIDE="on" + PACKET_LOGGING_OVERRIDE="on" + FIRMWARE_VERSION_SUFFIX="logging" + run_resolved_build_targets "${targets[@]}" + build_status=$? + fi + + MESHDEBUG_OVERRIDE=$original_meshdebug_override + PACKET_LOGGING_OVERRIDE=$original_packet_logging_override + FIRMWARE_VERSION_SUFFIX=$original_firmware_version_suffix + + return "$build_status" +} + validate_command() { case "$1" in build-firmware) @@ -1383,6 +1456,11 @@ validate_command() { run_command() { # All build commands share execution after validation resolves their target list. + if is_logging_matrix_command "$1"; then + run_logging_matrix_build_targets "${RESOLVED_BUILD_TARGETS[@]}" + return $? + fi + if is_build_command "$1"; then run_resolved_build_targets "${RESOLVED_BUILD_TARGETS[@]}" return $? @@ -1419,7 +1497,11 @@ main() { fi prompt_for_build_mode - prompt_for_debug_build_settings + if is_logging_matrix_command "${SELECTED_COMMAND_ARGS[0]}"; then + echo "Skipping debug option prompts; this action builds logging off and logging on passes." + else + prompt_for_debug_build_settings + fi prompt_for_radio_build_settings prompt_for_firmware_profile_settings set -- "${SELECTED_COMMAND_ARGS[@]}" diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 43ab975f..48b76245 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -1396,7 +1396,9 @@ set direct.retry.margin 10 **Explanation:** - Higher SNR uses faster coding rates. - Lower SNR uses more robust coding rates. -- CR6 is intentionally skipped. +- Repeater retry attempts escalate from the adaptive starting CR. CR4 starts as CR4, CR5, CR7, CR7, then CR8. CR5 starts as CR5, CR7, CR7, then CR8. CR7 gets two attempts, then CR8. +- Repeater adaptive CR selection intentionally skips CR6. +- Non-repeater retry packets start at the current radio CR and follow the same escalation pattern, clamped at CR8. With the normal CR5 radio setting this is CR5, CR7, CR7, then CR8. - `off` disables per-packet retry CR overrides and uses the current radio CR. - Direct path retry packets sent at CR4 or CR5 temporarily use a shorter 16-symbol preamble, then restore the radio's default preamble. - Unknown repeaters start at `+3.00 dB` for adaptive CR selection. diff --git a/docs/halo_keymind_settings.md b/docs/halo_keymind_settings.md index 863285eb..826753a2 100644 --- a/docs/halo_keymind_settings.md +++ b/docs/halo_keymind_settings.md @@ -177,15 +177,17 @@ Direct retry applies to direct-routed packets. A queued resend is canceled when | `direct.retry.count` | Maximum direct retry attempts after initial TX. | `get direct.retry.count`, `set direct.retry.count <1-15>` | `set direct.retry.count 15` | | `direct.retry.base` | Base wait in milliseconds before retry; non-TRACE paths under 6 remaining hops scale by `hops / 6`, TRACE paths under 16 by `hops / 16`. | `get direct.retry.base`, `set direct.retry.base <10-5000>` | `set direct.retry.base 175` | | `direct.retry.step` | Milliseconds added per retry attempt before the same short-path scaling. | `get direct.retry.step`, `set direct.retry.step <0-5000>` | `set direct.retry.step 100` | -| `direct.retry.cr` | Adaptive coding-rate thresholds for direct retry packets. Uses `CR4`, `CR5`, `CR7`, or `CR8`; `CR6` is never selected. | `get direct.retry.cr`, `set direct.retry.cr ,,,`, `set direct.retry.cr off` | `set direct.retry.cr 10.0,7.5,2.5,0` | +| `direct.retry.cr` | Adaptive coding-rate thresholds for repeater direct retry packets. Repeaters use `CR4`, `CR5`, `CR7`, or `CR8`, then escalate by attempt: CR4, CR5, CR7, CR7, then CR8 from a CR4 start; CR5, CR7, CR7, then CR8 from a CR5 start. Non-repeaters start at the current radio CR and follow the same escalation pattern, clamped at CR8. | `get direct.retry.cr`, `set direct.retry.cr ,,,`, `set direct.retry.cr off` | `set direct.retry.cr 10.0,7.5,2.5,0` | The default adaptive coding-rate profile is `10.0,7.5,2.5,2.5`. SNR `10.0 dB` and up uses `CR4`, `7.5 dB` and up uses `CR5`, `2.5 dB` and down uses `CR8`, and the middle band uses `CR7`. If no recent repeater table entry is available, retry packets use `CR5`. Use -`set direct.retry.cr off` to disable adaptive coding-rate overrides. If -adaptive selection chooses `CR4`, retries after the third attempt use -`CR5`. +`set direct.retry.cr off` to disable adaptive coding-rate overrides. Repeater +attempts escalate from the adaptive starting CR: `CR4`, `CR5`, `CR7`, `CR7`, +then `CR8` from a `CR4` start; `CR5`, `CR7`, `CR7`, then `CR8` from a `CR5` +start. Non-repeaters use the current radio CR as the first retry CR and follow +the same pattern up to `CR8`. Preset details: diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 0d687cfd..1a048791 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -109,6 +109,7 @@ protected: int calcRxDelay(float score, uint32_t air_time) const override; uint32_t getRetransmitDelay(const mesh::Packet *packet) override; uint32_t getDirectRetransmitDelay(const mesh::Packet *packet) override; + uint8_t getDefaultTxCodingRate() const override { return _prefs.cr; } uint8_t getExtraAckTransmitCount() const override; bool filterRecvFloodPacket(mesh::Packet* packet) override; bool allowPacketForward(const mesh::Packet* packet) override; diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index fbbec54a..4486c886 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -913,7 +913,6 @@ bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_ho } void MyMesh::configureDirectRetryPacket(mesh::Packet* retry, const mesh::Packet* original, uint8_t retry_attempt) { - (void)retry_attempt; int8_t snr_x4 = 12; // unknown repeaters start at +3.00 dB const SimpleMeshTables* tables = static_cast(getTables()); if (tables != NULL) { @@ -927,7 +926,7 @@ void MyMesh::configureDirectRetryPacket(mesh::Packet* retry, const mesh::Packet* } } - retry->tx_cr = getDirectRetryCodingRateForSNR(snr_x4); + retry->tx_cr = getDirectRetryCodingRateForAttempt(getDirectRetryCodingRateForSNR(snr_x4), retry_attempt); } uint32_t MyMesh::getDirectRetryEchoDelay(const mesh::Packet* packet) const { @@ -964,6 +963,26 @@ static void formatDirectRetryTarget(char* dest, size_t dest_len, const uint8_t* dest[hex_len] = 0; } +static uint8_t getRetryLogCodingRate(const mesh::Packet* packet, uint8_t default_cr) { + if (packet != NULL && packet->tx_cr >= 4 && packet->tx_cr <= 8) { + return packet->tx_cr; + } + return default_cr; +} + +static uint16_t getRetryLogPreambleLength(const mesh::Packet* packet, uint16_t default_preamble_len) { + if (packet == NULL || default_preamble_len <= 16 || !(packet->tx_cr == 4 || packet->tx_cr == 5)) { + return default_preamble_len; + } + + bool has_direct_path = packet->getPathHashCount() > 0 + || (packet->getPayloadType() == PAYLOAD_TYPE_TRACE && packet->payload_len > 9); + if (packet->isRouteDirect() && has_direct_path) { + return 16; + } + return default_preamble_len; +} + void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis, uint8_t retry_attempt, const uint8_t* target_hash, uint8_t target_hash_len, int16_t payload_type) { char type_label[8]; @@ -977,27 +996,33 @@ void MyMesh::onDirectRetryEvent(const char* event, const mesh::Packet* packet, u strcpy(type_label, "?"); } formatDirectRetryTarget(target_label, sizeof(target_label), target_hash, target_hash_len); + uint8_t log_cr = getRetryLogCodingRate(packet, getDefaultTxCodingRate()); + uint16_t log_preamble_len = getRetryLogPreambleLength(packet, radio_driver.getDefaultPreambleLength()); #if MESH_DEBUG - MESH_DEBUG_PRINTLN("direct retry %s attempt=%u delay=%lu type=%s route=%s target=%s", + MESH_DEBUG_PRINTLN("direct retry %s attempt=%u delay=%lu type=%s route=%s target=%s cr=%u preamble_len=%u", event ? event : "?", (uint32_t)retry_attempt, (unsigned long)delay_millis, type_label, route_label, - target_label); + target_label, + (uint32_t)log_cr, + (uint32_t)log_preamble_len); #endif if (_logging) { File f = openAppend(PACKET_LOG_FILE); if (f) { f.print(getLogDateTime()); - f.printf(": direct retry %s attempt=%u delay=%lu type=%s route=%s target=%s\n", + f.printf(": direct retry %s attempt=%u delay=%lu type=%s route=%s target=%s cr=%u preamble_len=%u\n", event ? event : "?", (uint32_t)retry_attempt, (unsigned long)delay_millis, type_label, route_label, - target_label); + target_label, + (uint32_t)log_cr, + (uint32_t)log_preamble_len); f.close(); } } @@ -1497,9 +1522,10 @@ void MyMesh::onFloodRetryEvent(const char* event, const mesh::Packet* packet, ui refreshFloodRetryHeardRecent(packet); snprintf(heard_suffix, sizeof(heard_suffix), ", heard=%s", heard_log); } + uint8_t log_cr = getRetryLogCodingRate(packet, getDefaultTxCodingRate()); + uint16_t log_preamble_len = getRetryLogPreambleLength(packet, radio_driver.getDefaultPreambleLength()); - MESH_DEBUG_PRINTLN("%s flood retry %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%u, path=%s%s, %s=%lu)", - getLogDateTime(), + MESH_DEBUG_PRINTLN("flood retry %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%u, path=%s%s, %s=%lu, cr=%u, preamble_len=%u)", event, (unsigned int)retry_attempt, (uint32_t)packet->getPayloadType(), @@ -1509,13 +1535,15 @@ void MyMesh::onFloodRetryEvent(const char* event, const mesh::Packet* packet, ui path_log, heard_suffix, time_label, - (unsigned long)delay_millis); + (unsigned long)delay_millis, + (uint32_t)log_cr, + (uint32_t)log_preamble_len); if (_logging) { File f = openAppend(PACKET_LOG_FILE); if (f) { f.print(getLogDateTime()); - f.printf(": FLOOD RETRY %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%u, path=%s%s, %s=%lu)\n", + f.printf(": FLOOD RETRY %s (retry=%u, type=%d, route=%s, payload_len=%d, hop=%u, path=%s%s, %s=%lu, cr=%u, preamble_len=%u)\n", event, (unsigned int)retry_attempt, (uint32_t)packet->getPayloadType(), @@ -1525,7 +1553,9 @@ void MyMesh::onFloodRetryEvent(const char* event, const mesh::Packet* packet, ui path_log, heard_suffix, time_label, - (unsigned long)delay_millis); + (unsigned long)delay_millis, + (uint32_t)log_cr, + (uint32_t)log_preamble_len); f.close(); } } diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index dc0580d5..6e7a2a87 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -5,6 +5,10 @@ #include #include +#ifndef MESH_ENABLE_RECENT_REPEATERS + #define MESH_ENABLE_RECENT_REPEATERS 1 +#endif + #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) #include #elif defined(RP2040_PLATFORM) diff --git a/examples/simple_room_server/MyMesh.h b/examples/simple_room_server/MyMesh.h index 98a79bb8..c57b2056 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -153,6 +153,9 @@ protected: uint8_t getExtraAckTransmitCount() const override { return _prefs.multi_acks; } + uint8_t getDefaultTxCodingRate() const override { + return set_radio_at == 0 && revert_radio_at != 0 ? pending_cr : _prefs.cr; + } bool filterRecvFloodPacket(mesh::Packet* pkt) override; diff --git a/examples/simple_secure_chat/main.cpp b/examples/simple_secure_chat/main.cpp index d93810ed..ba8d03e9 100644 --- a/examples/simple_secure_chat/main.cpp +++ b/examples/simple_secure_chat/main.cpp @@ -202,6 +202,10 @@ protected: return true; } + uint8_t getDefaultTxCodingRate() const override { + return LORA_CR; + } + void onDiscoveredContact(ContactInfo& contact, bool is_new, uint8_t path_len, const uint8_t* path) override { // TODO: if not in favs, prompt to add as fav(?) diff --git a/examples/simple_sensor/SensorMesh.h b/examples/simple_sensor/SensorMesh.h index 1d65b877..779b9bc0 100644 --- a/examples/simple_sensor/SensorMesh.h +++ b/examples/simple_sensor/SensorMesh.h @@ -122,6 +122,9 @@ protected: int getInterferenceThreshold() const override; bool getCADEnabled() const override; int getAGCResetInterval() const override; + uint8_t getDefaultTxCodingRate() const override { + return set_radio_at == 0 && revert_radio_at != 0 ? pending_cr : _prefs.cr; + } void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override; int searchPeersByHash(const uint8_t* hash) override; void getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) override; diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 45eb031f..eaff68c0 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -5,8 +5,13 @@ namespace mesh { static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT = 15; static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX = 15; -static const uint8_t FLOOD_RETRY_MAX_ATTEMPTS_DEFAULT = 3; +static const uint32_t DIRECT_RETRY_BASE_MS_DEFAULT = 175; +static const uint32_t DIRECT_RETRY_STEP_MS_DEFAULT = 50; +static const uint8_t DIRECT_RETRY_SHORT_PATH_SCALE_HOPS = 6; +static const uint8_t DIRECT_RETRY_TRACE_SCALE_HOPS = 16; +static const uint8_t FLOOD_RETRY_MAX_ATTEMPTS_DEFAULT = 15; static const uint8_t FLOOD_RETRY_MAX_ATTEMPTS_HARD_MAX = 15; +static const uint8_t FLOOD_RETRY_MAX_PATH_DEFAULT = 1; static uint8_t decodeTraceHashSize(uint8_t flags, uint8_t route_bytes) { uint8_t code = flags & 0x03; @@ -63,6 +68,74 @@ static uint8_t getTraceDirectPriority(const Packet* packet) { return 5; } +static uint8_t getDirectRetryRemainingHops(const Packet* packet) { + if (packet == NULL) { + return 0; + } + if (packet->getPayloadType() == PAYLOAD_TYPE_TRACE) { + return getTraceRemainingHops(packet); + } + return packet->getPathHashCount(); +} + +static uint32_t scaleDirectRetryDelayForPath(const Packet* packet, uint32_t delay_ms) { + uint8_t hops = getDirectRetryRemainingHops(packet); + if (hops == 0) { + return delay_ms; + } + + uint8_t scale_hops = packet != NULL && packet->getPayloadType() == PAYLOAD_TYPE_TRACE + ? DIRECT_RETRY_TRACE_SCALE_HOPS + : DIRECT_RETRY_SHORT_PATH_SCALE_HOPS; + if (hops >= scale_hops) { + return delay_ms; + } + + uint32_t scaled = ((delay_ms * hops) + (scale_hops - 1U)) / scale_hops; + return scaled > 0 ? scaled : 1; +} + +uint8_t Mesh::getDirectRetryCodingRateForAttempt(uint8_t start_cr, uint8_t retry_attempt) { + if (start_cr < 4 || start_cr > 8) { + return start_cr; + } + + if (retry_attempt < 1) { + retry_attempt = 1; + } + + if (start_cr >= 8) { + return 8; + } + if (start_cr >= 7) { + return retry_attempt <= 2 ? 7 : 8; + } + if (start_cr <= 4) { + if (retry_attempt == 1) return 4; + if (retry_attempt == 2) return 5; + if (retry_attempt <= 4) return 7; + return 8; + } + + if (retry_attempt == 1) return start_cr; + if (retry_attempt <= 3) return 7; + return 8; +} + +void Mesh::configureDirectRetryPacket(Packet* retry, const Packet* original, uint8_t retry_attempt) { + (void)original; + if (retry == NULL) { + return; + } + + uint8_t default_cr = getDefaultTxCodingRate(); + if (default_cr < 4 || default_cr > 8) { + return; + } + + retry->tx_cr = getDirectRetryCodingRateForAttempt(default_cr, retry_attempt); +} + void Mesh::begin() { for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { _direct_retries[i].packet = NULL; @@ -181,30 +254,38 @@ uint32_t Mesh::getDirectRetransmitDelay(const Packet* packet) { return 0; // by default, no delay } bool Mesh::allowDirectRetry(const Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const { - return false; + (void)packet; + (void)next_hop_hash; + (void)next_hop_hash_len; + return true; } uint32_t Mesh::getDirectRetryEchoDelay(const Packet* packet) const { - // Keep the base fallback aligned with the repeater's minimum retry wait. - return 200; + (void)packet; + return DIRECT_RETRY_BASE_MS_DEFAULT; } uint8_t Mesh::getDirectRetryMaxAttempts(const Packet* packet) const { + (void)packet; return DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT; } uint32_t Mesh::getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx) { uint32_t base = getDirectRetryEchoDelay(packet); - // Keep the historical linear spacing while allowing the base wait to vary by platform/profile. - return base + ((uint32_t)attempt_idx * 100UL); + uint32_t delay_ms = base + ((uint32_t)attempt_idx * DIRECT_RETRY_STEP_MS_DEFAULT); + return scaleDirectRetryDelayForPath(packet, delay_ms); } bool Mesh::allowFloodRetry(const Packet* packet) const { + (void)packet; return true; } bool Mesh::hasFloodRetryTargetPrefix(const Packet* packet) const { + (void)packet; return false; } uint8_t Mesh::getFloodRetryMaxPathLength(const Packet* packet) const { - return 2; + (void)packet; + return FLOOD_RETRY_MAX_PATH_DEFAULT; } uint8_t Mesh::getFloodRetryMaxAttempts(const Packet* packet) const { + (void)packet; return FLOOD_RETRY_MAX_ATTEMPTS_DEFAULT; } uint32_t Mesh::getFloodRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx) { @@ -892,9 +973,6 @@ bool Mesh::getDirectRetryTarget(const Packet* packet, const uint8_t*& next_hop_h if (offset + hash_size > route_bytes) { return false; } - if (offset + (2 * hash_size) > route_bytes) { - return false; // no downstream repeater means there will be no forward echo to overhear. - } next_hop_hash = &packet->payload[9 + offset]; next_hop_hash_len = hash_size; @@ -1514,6 +1592,7 @@ void Mesh::sendFlood(Packet* packet, uint32_t delay_millis, uint8_t path_hash_si } else { pri = 1; } + maybeScheduleFloodRetry(packet, pri); sendPacket(packet, pri, delay_millis); } @@ -1543,6 +1622,7 @@ void Mesh::sendFlood(Packet* packet, uint16_t* transport_codes, uint32_t delay_m } else { pri = 1; } + maybeScheduleFloodRetry(packet, pri); sendPacket(packet, pri, delay_millis); } diff --git a/src/Mesh.h b/src/Mesh.h index c306a30f..1dfbde6e 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -134,7 +134,7 @@ protected: virtual uint32_t getDirectRetransmitDelay(const Packet* packet); /** - * \brief Decide whether a DIRECT packet should get one delayed retry if the next hop echo is not overheard. + * \brief Decide whether a DIRECT packet should retry if the next hop echo is not overheard. * Sub-classes can use recent repeater or other link-quality data to opt in selectively. */ virtual bool allowDirectRetry(const Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const; @@ -145,7 +145,7 @@ protected: virtual bool maybeShortCircuitDirect(Packet* packet) { return false; } /** - * \returns milliseconds to wait for the next-hop echo before queueing one retry of the DIRECT packet. + * \returns milliseconds to wait for the next-hop echo before queueing a retry of the DIRECT packet. */ virtual uint32_t getDirectRetryEchoDelay(const Packet* packet) const; @@ -216,10 +216,15 @@ protected: */ virtual void onDirectRetrySucceeded(const uint8_t* next_hop_hash, uint8_t next_hop_hash_len, int8_t snr_x4) { } + /** + * \returns Coding rate to use for a retry attempt, starting from the current/adaptive CR. + */ + static uint8_t getDirectRetryCodingRateForAttempt(uint8_t start_cr, uint8_t retry_attempt); + /** * \brief Optional hook to set local-only transmit options on a retry packet before it is queued. */ - virtual void configureDirectRetryPacket(Packet* retry, const Packet* original, uint8_t retry_attempt) { } + virtual void configureDirectRetryPacket(Packet* retry, const Packet* original, uint8_t retry_attempt); /** * \brief Perform search of local DB of peers/contacts. diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index f2420473..d7b0477f 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -10,9 +10,15 @@ #endif #define MAX_PACKET_HASHES (128+32) +#ifndef MESH_ENABLE_RECENT_REPEATERS + #define MESH_ENABLE_RECENT_REPEATERS 0 +#endif #ifndef MAX_RECENT_REPEATERS - // Platform defaults. Can be overridden with -D MAX_RECENT_REPEATERS=. - #if defined(ESP32) || defined(ESP32_PLATFORM) + // Recent repeater history is only needed by repeater firmware. Other roles + // use unconditional retry decisions and skip this RAM-heavy cache. + #if !MESH_ENABLE_RECENT_REPEATERS + #define MAX_RECENT_REPEATERS 0 + #elif defined(ESP32) || defined(ESP32_PLATFORM) #define MAX_RECENT_REPEATERS 2048 #elif defined(NRF52_PLATFORM) #define MAX_RECENT_REPEATERS 512 @@ -20,6 +26,7 @@ #define MAX_RECENT_REPEATERS 64 #endif #endif +#define RECENT_REPEATER_STORAGE_SLOTS (MAX_RECENT_REPEATERS > 0 ? MAX_RECENT_REPEATERS : 1) #define MAX_ROUTE_HASH_BYTES 3 class SimpleMeshTables : public mesh::MeshTables { @@ -36,7 +43,7 @@ private: uint8_t _hashes[MAX_PACKET_HASHES*MAX_HASH_SIZE]; int _next_idx; uint32_t _direct_dups, _flood_dups; - RecentRepeaterInfo _recent_repeaters[MAX_RECENT_REPEATERS]; + RecentRepeaterInfo _recent_repeaters[RECENT_REPEATER_STORAGE_SLOTS]; bool hasSeenHash(const uint8_t* hash) const { const uint8_t* sp = _hashes; @@ -124,6 +131,10 @@ private: } void recordRecentRepeater(const mesh::Packet* packet) { + if (MAX_RECENT_REPEATERS == 0) { + return; + } + uint8_t prefix[MAX_ROUTE_HASH_BYTES] = {0}; uint8_t prefix_len = 0; if (!extractRecentRepeater(packet, prefix, prefix_len) || prefix_len == 0) { @@ -202,6 +213,9 @@ public: bool snr_locked = false, bool bypass_allow_filter = false) { (void)snr_locked; (void)bypass_allow_filter; + if (MAX_RECENT_REPEATERS == 0) { + return false; + } if (prefix == NULL || prefix_len == 0) { return false; } @@ -262,6 +276,9 @@ public: return true; } bool decrementRecentRepeaterSnrX4(const uint8_t* prefix, uint8_t prefix_len, uint8_t amount_x4 = 1) { + if (MAX_RECENT_REPEATERS == 0) { + return false; + } if (prefix == NULL || prefix_len == 0 || amount_x4 == 0) { return false; } @@ -284,6 +301,9 @@ public: return false; } int getRecentRepeaterCount() const { + if (MAX_RECENT_REPEATERS == 0) { + return 0; + } int count = 0; for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { if (_recent_repeaters[i].prefix_len > 0) { @@ -293,6 +313,9 @@ public: return count; } const RecentRepeaterInfo* getRecentRepeaterBySortedIdx(int idx_wanted) const { + if (MAX_RECENT_REPEATERS == 0) { + return NULL; + } if (idx_wanted < 0) { return NULL; } @@ -325,6 +348,9 @@ public: } const RecentRepeaterInfo* findRecentRepeaterByHash(const uint8_t* hash, uint8_t hash_len) const { + if (MAX_RECENT_REPEATERS == 0) { + return NULL; + } if (hash == NULL || hash_len == 0) { return NULL; } From 4f8cb8db78c908bc350cd990f116a3ff7d4b9976 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Sat, 27 Jun 2026 21:06:01 +1000 Subject: [PATCH 168/214] * ACK packets being 'clipped' (in Dispatcher send). Needed to extend max_airtime timeout calc for short packets --- src/helpers/radiolib/CustomLR1110Wrapper.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/helpers/radiolib/CustomLR1110Wrapper.h b/src/helpers/radiolib/CustomLR1110Wrapper.h index fc505283..c6b1acb4 100644 --- a/src/helpers/radiolib/CustomLR1110Wrapper.h +++ b/src/helpers/radiolib/CustomLR1110Wrapper.h @@ -26,6 +26,11 @@ public: return rssi; } + uint32_t getEstAirtimeFor(int len_bytes) override { + auto airtime = RadioLibWrapper::getEstAirtimeFor(len_bytes); + return airtime < 200 ? 200 : airtime; // at least 200 millis + } + void onSendFinished() override { RadioLibWrapper::onSendFinished(); _radio->setPreambleLength(preambleLengthForSF(getSpreadingFactor())); // overcomes weird issues with small and big pkts From 7a57b5381ec47b441aa5d393ce7fc96cc32efdb9 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Sat, 27 Jun 2026 23:05:07 +0700 Subject: [PATCH 169/214] Disabled annoying Blue LED for T-echo Lite --- variants/lilygo_techo_lite/variant.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/variants/lilygo_techo_lite/variant.cpp b/variants/lilygo_techo_lite/variant.cpp index 3cd82d70..2a56e586 100644 --- a/variants/lilygo_techo_lite/variant.cpp +++ b/variants/lilygo_techo_lite/variant.cpp @@ -22,8 +22,8 @@ void initVariant() { pinMode(LED_RED, OUTPUT); pinMode(LED_GREEN, OUTPUT); - pinMode(LED_BLUE, OUTPUT); - digitalWrite(LED_BLUE, HIGH); + // pinMode(LED_BLUE, OUTPUT); + // digitalWrite(LED_BLUE, HIGH); digitalWrite(LED_GREEN, HIGH); digitalWrite(LED_RED, HIGH); From dd3150533912607653ebf51d1cb2239f80cf6c48 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Sat, 27 Jun 2026 09:51:03 -0700 Subject: [PATCH 170/214] Remove local ESP32 framework symlink overrides --- variants/heltec_ct62/platformio.ini | 1 - variants/heltec_tracker/platformio.ini | 1 - variants/heltec_tracker_v2/platformio.ini | 1 - variants/heltec_v2/platformio.ini | 1 - variants/heltec_v3/platformio.ini | 2 -- variants/heltec_v4/platformio.ini | 2 -- variants/heltec_wireless_paper/platformio.ini | 1 - variants/lilygo_t3s3/platformio.ini | 1 - variants/lilygo_tbeam_1w/platformio.ini | 1 - variants/lilygo_tbeam_SX1262/platformio.ini | 1 - variants/lilygo_tbeam_SX1276/platformio.ini | 1 - variants/lilygo_tbeam_supreme_SX1262/platformio.ini | 1 - variants/lilygo_tlora_v2_1/platformio.ini | 1 - variants/thinknode_m5/platformio.ini | 1 - variants/xiao_c3/platformio.ini | 1 - variants/xiao_s3/platformio.ini | 1 - variants/xiao_s3_wio/platformio.ini | 1 - 17 files changed, 19 deletions(-) diff --git a/variants/heltec_ct62/platformio.ini b/variants/heltec_ct62/platformio.ini index e8becc7e..2dd55881 100644 --- a/variants/heltec_ct62/platformio.ini +++ b/variants/heltec_ct62/platformio.ini @@ -132,7 +132,6 @@ lib_deps = [env:Heltec_ct62_companion_radio_ble_ps] extends = env:Heltec_ct62_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 [env:Heltec_ct62_sensor] extends = Heltec_ct62 diff --git a/variants/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini index 2cd0cea6..f25d0c4b 100644 --- a/variants/heltec_tracker/platformio.ini +++ b/variants/heltec_tracker/platformio.ini @@ -101,7 +101,6 @@ lib_deps = [env:Heltec_Wireless_Tracker_companion_radio_ble_ps] extends = env:Heltec_Wireless_Tracker_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 [env:Heltec_Wireless_Tracker_repeater] extends = Heltec_tracker_base diff --git a/variants/heltec_tracker_v2/platformio.ini b/variants/heltec_tracker_v2/platformio.ini index d914ce6a..37d15980 100644 --- a/variants/heltec_tracker_v2/platformio.ini +++ b/variants/heltec_tracker_v2/platformio.ini @@ -178,7 +178,6 @@ lib_deps = [env:heltec_tracker_v2_companion_radio_ble_ps] extends = env:heltec_tracker_v2_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 [env:heltec_tracker_v2_companion_radio_wifi] extends = Heltec_tracker_v2 diff --git a/variants/heltec_v2/platformio.ini b/variants/heltec_v2/platformio.ini index e9cf56f0..2723fdc2 100644 --- a/variants/heltec_v2/platformio.ini +++ b/variants/heltec_v2/platformio.ini @@ -174,7 +174,6 @@ lib_deps = [env:Heltec_v2_companion_radio_ble_ps] extends = env:Heltec_v2_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 [env:Heltec_v2_companion_radio_wifi] extends = Heltec_lora32_v2 diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 69e4ed05..ec881fa9 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -181,7 +181,6 @@ lib_deps = [env:Heltec_v3_companion_radio_ble_ps] extends = env:Heltec_v3_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 [env:Heltec_v3_companion_radio_wifi] extends = Heltec_lora32_v3 @@ -326,7 +325,6 @@ lib_deps = [env:Heltec_WSL3_companion_radio_ble_ps] extends = env:Heltec_WSL3_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 [env:Heltec_WSL3_companion_radio_usb] extends = Heltec_lora32_v3 diff --git a/variants/heltec_v4/platformio.ini b/variants/heltec_v4/platformio.ini index dc681ada..e5a478c3 100644 --- a/variants/heltec_v4/platformio.ini +++ b/variants/heltec_v4/platformio.ini @@ -224,7 +224,6 @@ lib_deps = [env:heltec_v4_companion_radio_ble_ps_femon] extends = env:heltec_v4_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 [env:heltec_v4_companion_radio_ble_ps] extends = env:heltec_v4_companion_radio_ble_ps_femon @@ -400,7 +399,6 @@ lib_deps = [env:heltec_v4_expansionkit_tft_companion_radio_ble_ps] extends = env:heltec_v4_tft_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 build_flags = ${env:heltec_v4_tft_companion_radio_ble.build_flags} -D ENV_PIN_SDA=4 diff --git a/variants/heltec_wireless_paper/platformio.ini b/variants/heltec_wireless_paper/platformio.ini index e7a107a5..f059b212 100644 --- a/variants/heltec_wireless_paper/platformio.ini +++ b/variants/heltec_wireless_paper/platformio.ini @@ -66,7 +66,6 @@ lib_deps = [env:Heltec_Wireless_Paper_companion_radio_ble_ps] extends = env:Heltec_Wireless_Paper_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 [env:Heltec_Wireless_Paper_companion_radio_usb] extends = Heltec_Wireless_Paper_base diff --git a/variants/lilygo_t3s3/platformio.ini b/variants/lilygo_t3s3/platformio.ini index e7f9f11f..35f21ec5 100644 --- a/variants/lilygo_t3s3/platformio.ini +++ b/variants/lilygo_t3s3/platformio.ini @@ -176,7 +176,6 @@ lib_deps = [env:LilyGo_T3S3_sx1262_companion_radio_ble_ps] extends = env:LilyGo_T3S3_sx1262_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 [env:LilyGo_T3S3_sx1262_kiss_modem] extends = LilyGo_T3S3_sx1262 diff --git a/variants/lilygo_tbeam_1w/platformio.ini b/variants/lilygo_tbeam_1w/platformio.ini index 4516e4cd..6c039a80 100644 --- a/variants/lilygo_tbeam_1w/platformio.ini +++ b/variants/lilygo_tbeam_1w/platformio.ini @@ -149,7 +149,6 @@ lib_deps = ; === LILYGO T-Beam 1W Companion Radio PS (BLE PS) === [env:LilyGo_TBeam_1W_companion_radio_ble_ps] extends = env:LilyGo_TBeam_1W_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 ; === LILYGO T-Beam 1W Companion Radio (WiFi) === [env:LilyGo_TBeam_1W_companion_radio_wifi] diff --git a/variants/lilygo_tbeam_SX1262/platformio.ini b/variants/lilygo_tbeam_SX1262/platformio.ini index 5bc0efdd..b4a56a45 100644 --- a/variants/lilygo_tbeam_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_SX1262/platformio.ini @@ -67,7 +67,6 @@ lib_deps = [env:Tbeam_SX1262_companion_radio_ble_ps] extends = env:Tbeam_SX1262_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 build_flags = ${env:Tbeam_SX1262_companion_radio_ble.build_flags} -D MAX_CONTACTS=150 diff --git a/variants/lilygo_tbeam_SX1276/platformio.ini b/variants/lilygo_tbeam_SX1276/platformio.ini index edf84e21..cb9a6607 100644 --- a/variants/lilygo_tbeam_SX1276/platformio.ini +++ b/variants/lilygo_tbeam_SX1276/platformio.ini @@ -63,7 +63,6 @@ lib_deps = [env:Tbeam_SX1276_companion_radio_ble_ps] extends = env:Tbeam_SX1276_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 [env:Tbeam_SX1276_repeater] extends = LilyGo_TBeam_SX1276 diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index b930a25b..fe117aa1 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -144,7 +144,6 @@ lib_deps = [env:T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps] extends = env:T_Beam_S3_Supreme_SX1262_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 [env:T_Beam_S3_Supreme_SX1262_companion_radio_wifi] extends = T_Beam_S3_Supreme_SX1262 diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index bb173524..28edb6a9 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -110,7 +110,6 @@ lib_deps = [env:LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps] extends = env:LilyGo_TLora_V2_1_1_6_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 [env:LilyGo_TLora_V2_1_1_6_room_server] extends = LilyGo_TLora_V2_1_1_6 diff --git a/variants/thinknode_m5/platformio.ini b/variants/thinknode_m5/platformio.ini index 4ff37c0e..f4a9e728 100644 --- a/variants/thinknode_m5/platformio.ini +++ b/variants/thinknode_m5/platformio.ini @@ -167,7 +167,6 @@ lib_deps = [env:ThinkNode_M5_companion_radio_ble_ps] extends = env:ThinkNode_M5_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 [env:ThinkNode_M5_companion_radio_usb] extends = ThinkNode_M5 diff --git a/variants/xiao_c3/platformio.ini b/variants/xiao_c3/platformio.ini index fa70d8a0..0eddb0e5 100644 --- a/variants/xiao_c3/platformio.ini +++ b/variants/xiao_c3/platformio.ini @@ -92,7 +92,6 @@ lib_deps = [env:Xiao_C3_companion_radio_ble_ps] extends = env:Xiao_C3_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 board_build.partitions = min_spiffs.csv ; get around 4mb flash limit [env:Xiao_C3_companion_radio_usb] diff --git a/variants/xiao_s3/platformio.ini b/variants/xiao_s3/platformio.ini index b59e69ae..c70e16f2 100644 --- a/variants/xiao_s3/platformio.ini +++ b/variants/xiao_s3/platformio.ini @@ -116,7 +116,6 @@ lib_deps = [env:Xiao_S3_companion_radio_ble_ps] extends = env:Xiao_S3_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 [env:Xiao_S3_companion_radio_usb] extends = Xiao_S3 diff --git a/variants/xiao_s3_wio/platformio.ini b/variants/xiao_s3_wio/platformio.ini index 41420acf..1c7e3d5a 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -175,7 +175,6 @@ lib_deps = [env:Xiao_S3_WIO_companion_radio_ble_ps] extends = env:Xiao_S3_WIO_companion_radio_ble -platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 [env:Xiao_S3_WIO_companion_radio_serial] extends = Xiao_S3_WIO From bc35208682126f55f2ceae7c55ce8237d3ec51e2 Mon Sep 17 00:00:00 2001 From: HDDen <62592944+HDDen@users.noreply.github.com> Date: Sun, 28 Jun 2026 00:32:22 +0300 Subject: [PATCH 171/214] Update number_allocations.md Added Data-Type range for MeshCore Images codec (range is for small reserve to future versions) --- docs/number_allocations.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/number_allocations.md b/docs/number_allocations.md index 857525d4..f35f3b2d 100644 --- a/docs/number_allocations.md +++ b/docs/number_allocations.md @@ -17,6 +17,7 @@ Once you have a working app/project, you need to be able to demonstrate it exist | 0000 - 00FF | -reserved for internal use- | | | 0100 | MeshCore Open | zsylvester@monitormx.com — https://github.com/zjs81/meshcore-open | | 0110 - 011F | Ripple | ripple_biz@protonmail.com — https://buymeacoffee.com/ripplebiz | +| 0120 - 0121 | MCOimg | most.original.address@gmail.com — https://hdden.ru/MCOimg/ | | FF00 - FFFF | -reserved for testing/dev- | | (add rows, inside the range 0100 - FEFF for custom apps) From a92dd908d8f6744de3947db16f6320d82ad842b9 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Sun, 28 Jun 2026 02:12:27 -0700 Subject: [PATCH 172/214] Fix Generic E22 KISS modem build --- variants/generic-e22/platformio.ini | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/variants/generic-e22/platformio.ini b/variants/generic-e22/platformio.ini index dfa2ff64..fa701c45 100644 --- a/variants/generic-e22/platformio.ini +++ b/variants/generic-e22/platformio.ini @@ -99,6 +99,11 @@ lib_deps = extends = Generic_E22 build_src_filter = ${Generic_E22.build_src_filter} +<../examples/kiss_modem/> +build_flags = + ${Generic_E22.build_flags} + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 [env:Generic_E22_sx1268_repeater] extends = Generic_E22 From cccdeff1b9066b36703b3b88b39f8cc5f49849be Mon Sep 17 00:00:00 2001 From: mikecarper Date: Sun, 28 Jun 2026 15:47:43 -0700 Subject: [PATCH 173/214] Fix ThinkNode M5 serial companion pins --- variants/thinknode_m5/platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/variants/thinknode_m5/platformio.ini b/variants/thinknode_m5/platformio.ini index f4a9e728..cab1c1a3 100644 --- a/variants/thinknode_m5/platformio.ini +++ b/variants/thinknode_m5/platformio.ini @@ -216,8 +216,8 @@ build_flags = -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 - -D SERIAL_TX=D6 - -D SERIAL_RX=D7 + -D SERIAL_TX=43 + -D SERIAL_RX=44 ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${ThinkNode_M5.build_src_filter} From 2a632e15df957298aafb8a06461681cc5684155a Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Mon, 29 Jun 2026 09:45:37 +0700 Subject: [PATCH 174/214] Added ARDUINO_USB_MODE=1 for Heltec v4 --- variants/heltec_v4/platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/heltec_v4/platformio.ini b/variants/heltec_v4/platformio.ini index caa71a33..113ba24a 100644 --- a/variants/heltec_v4/platformio.ini +++ b/variants/heltec_v4/platformio.ini @@ -7,6 +7,7 @@ build_flags = -I variants/heltec_v4 -D HELTEC_LORA_V4 -D USE_SX1262 + -D ARDUINO_USB_MODE=1 -D ESP32_CPU_FREQ=80 -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper From 205a4a6ff87b5328546a4017f0ba27b8c88e8cea Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Mon, 29 Jun 2026 12:24:40 +0700 Subject: [PATCH 175/214] Fixed hibernate for T-echo Lite to stay at 1mA. --- variants/lilygo_techo_lite/TechoBoard.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/variants/lilygo_techo_lite/TechoBoard.h b/variants/lilygo_techo_lite/TechoBoard.h index 225ed831..f4c16016 100644 --- a/variants/lilygo_techo_lite/TechoBoard.h +++ b/variants/lilygo_techo_lite/TechoBoard.h @@ -22,6 +22,8 @@ public: } void powerOff() override { + NRF52Board::powerOff(); + digitalWrite(PIN_VBAT_MEAS_EN, LOW); #ifdef LED_RED digitalWrite(LED_RED, LOW); @@ -38,6 +40,5 @@ public: #ifdef PIN_PWR_EN digitalWrite(PIN_PWR_EN, LOW); #endif - NRF52Board::powerOff(); } }; \ No newline at end of file From 58b0f7df9cce97987540c84454131e090bb00dd7 Mon Sep 17 00:00:00 2001 From: HDDen <62592944+HDDen@users.noreply.github.com> Date: Mon, 29 Jun 2026 12:41:44 +0300 Subject: [PATCH 176/214] Update number_allocations.md --- docs/number_allocations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/number_allocations.md b/docs/number_allocations.md index f35f3b2d..2ccf950f 100644 --- a/docs/number_allocations.md +++ b/docs/number_allocations.md @@ -17,7 +17,7 @@ Once you have a working app/project, you need to be able to demonstrate it exist | 0000 - 00FF | -reserved for internal use- | | | 0100 | MeshCore Open | zsylvester@monitormx.com — https://github.com/zjs81/meshcore-open | | 0110 - 011F | Ripple | ripple_biz@protonmail.com — https://buymeacoffee.com/ripplebiz | -| 0120 - 0121 | MCOimg | most.original.address@gmail.com — https://hdden.ru/MCOimg/ | +| 0120 | MCO Advanced | most.original.address@gmail.com — https://hdden.ru/MCOa/ | | FF00 - FFFF | -reserved for testing/dev- | | (add rows, inside the range 0100 - FEFF for custom apps) From 88fec6e512dd71aa30861d46fc099a595d38884c Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 29 Jun 2026 12:55:58 -0700 Subject: [PATCH 177/214] Use keymind retry timing for direct paths --- examples/simple_repeater/MyMesh.cpp | 128 +++++++++++++--------------- examples/simple_repeater/MyMesh.h | 1 + src/Mesh.cpp | 39 ++------- 3 files changed, 65 insertions(+), 103 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 4486c886..a7bf17ac 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -97,10 +97,6 @@ #define LOW_BATTERY_WARN_INTERVAL (24UL * 60UL * 60UL * 1000UL) #define LOW_BATTERY_CRITICAL_INTERVAL (12UL * 60UL * 60UL * 1000UL) -#ifndef DIRECT_RETRY_TRACE_SCALE_HOPS - #define DIRECT_RETRY_TRACE_SCALE_HOPS 32U -#endif - static const char* skipLocalSpaces(const char* text) { while (text != NULL && *text == ' ') text++; return text; @@ -828,65 +824,6 @@ uint32_t MyMesh::getDirectRetryAttemptStepMillis() const { return _prefs.direct_retry_step_ms; } -static uint8_t decodeRetryTraceHashSize(uint8_t flags, uint8_t route_bytes) { - uint8_t code = flags & 0x03; - uint8_t size_pow2 = (uint8_t)(1U << code); - uint8_t size_linear = (uint8_t)(code + 1U); - - bool pow2_ok = size_pow2 > 0 && (route_bytes % size_pow2) == 0; - bool linear_ok = size_linear > 0 && (route_bytes % size_linear) == 0; - - if (pow2_ok && !linear_ok) return size_pow2; - if (linear_ok && !pow2_ok) return size_linear; - if (pow2_ok) return size_pow2; - return size_linear; -} - -static uint8_t getDirectRetryRemainingHops(const mesh::Packet* packet) { - if (packet == NULL) { - return 0; - } - if (packet->getPayloadType() != PAYLOAD_TYPE_TRACE) { - return packet->getPathHashCount(); - } - if (packet->payload_len < 9) { - return 0; - } - - uint8_t route_bytes = packet->payload_len - 9; - uint8_t hash_size = decodeRetryTraceHashSize(packet->payload[8], route_bytes); - if (hash_size == 0) { - return 0; - } - - uint8_t route_hops = route_bytes / hash_size; - if (packet->path_len >= route_hops) { - return 0; - } - return route_hops - packet->path_len; -} - -static uint32_t scaleDirectRetryDelayForPath(const mesh::Packet* packet, uint32_t delay_ms) { - uint8_t hops = getDirectRetryRemainingHops(packet); - if (hops == 0) { - return delay_ms; - } - - if (packet != NULL && packet->getPayloadType() == PAYLOAD_TYPE_TRACE) { - if (hops >= DIRECT_RETRY_TRACE_SCALE_HOPS) { - return delay_ms; - } - uint32_t scaled = ((delay_ms * hops) + (DIRECT_RETRY_TRACE_SCALE_HOPS - 1U)) / DIRECT_RETRY_TRACE_SCALE_HOPS; - return scaled > 0 ? scaled : 1; - } - - if (hops >= 6) { - return delay_ms; - } - uint32_t scaled = ((delay_ms * hops) + 5U) / 6U; - return scaled > 0 ? scaled : 1; -} - bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const { (void)packet; if (!_prefs.direct_retry_enabled) { @@ -930,18 +867,69 @@ void MyMesh::configureDirectRetryPacket(mesh::Packet* retry, const mesh::Packet* } uint32_t MyMesh::getDirectRetryEchoDelay(const mesh::Packet* packet) const { - (void)packet; - return 200; + uint32_t base_wait_millis = constrain((uint32_t)_prefs.direct_retry_base_ms, (uint32_t)10, (uint32_t)5000); + if (packet == NULL) { + return base_wait_millis; + } + + // Approximate LoRa line rate in kilobits/sec from the live radio params the repeater is using now. + float kbps = (((float)active_sf) * active_bw * ((float)active_cr)) / ((float)(1UL << active_sf)); + if (kbps <= 0.0f) { + return base_wait_millis; + } + + // Wait roughly long enough for our TX, the next hop's receive/forward window, and its echo back. + uint32_t bits = ((uint32_t)packet->getRawLength()) * 8; + uint32_t scaled_wait_millis = (uint32_t)((((float)bits) * 4.0f) / kbps); + return base_wait_millis + scaled_wait_millis; +} + +static uint8_t decodeDirectRetryTraceHashSize(uint8_t flags, uint8_t route_bytes) { + uint8_t code = flags & 0x03; + uint8_t size_pow2 = (uint8_t)(1U << code); + uint8_t size_linear = (uint8_t)(code + 1U); + + bool pow2_ok = size_pow2 > 0 && (route_bytes % size_pow2) == 0; + bool linear_ok = size_linear > 0 && (route_bytes % size_linear) == 0; + + if (pow2_ok && !linear_ok) return size_pow2; + if (linear_ok && !pow2_ok) return size_linear; + if (pow2_ok) return size_pow2; + return size_linear; } uint8_t MyMesh::getDirectRetryMaxAttempts(const mesh::Packet* packet) const { - (void)packet; - return getDirectRetryConfiguredMaxAttempts(); + uint8_t configured_attempts = getDirectRetryConfiguredMaxAttempts(); + uint8_t total_hops = 0; + + if (packet != NULL) { + if (packet->isRouteDirect() && packet->getPayloadType() == PAYLOAD_TYPE_TRACE && packet->payload_len >= 9) { + uint8_t route_bytes = packet->payload_len - 9; + uint8_t hash_size = decodeDirectRetryTraceHashSize(packet->payload[8], route_bytes); + if (hash_size > 0) { + total_hops = (uint8_t)(route_bytes / hash_size); + } + } else { + total_hops = packet->getPathHashCount(); + } + } + + uint8_t path_cap = 15; + if (total_hops <= 3) { + path_cap = 8; + } else if (total_hops == 4) { + path_cap = 12; + } + + return configured_attempts < path_cap ? configured_attempts : path_cap; } uint32_t MyMesh::getDirectRetryAttemptDelay(const mesh::Packet* packet, uint8_t attempt_idx) { - uint32_t delay_ms = _prefs.direct_retry_base_ms + ((uint32_t)attempt_idx * getDirectRetryAttemptStepMillis()); - return scaleDirectRetryDelayForPath(packet, delay_ms); + uint32_t retry_delay = getDirectRetryEchoDelay(packet) + ((uint32_t)attempt_idx * getDirectRetryAttemptStepMillis()); + if (packet == NULL) { + return retry_delay; + } + return getDirectRetransmitDelay(packet) + retry_delay; } static void formatDirectRetryTarget(char* dest, size_t dest_len, const uint8_t* target_hash, uint8_t target_hash_len) { @@ -2072,6 +2060,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc last_battery_alert_sent = 0; battery_alert_sent = false; dirty_contacts_expiry = 0; + active_bw = 0.0f; active_sf = 0; active_cr = 0; memset(scheduled_radio_settings, 0, sizeof(scheduled_radio_settings)); @@ -2309,6 +2298,7 @@ void MyMesh::checkBatteryAlert() { void MyMesh::applyRadioParams(float freq, float bw, uint8_t sf, uint8_t cr) { radio_driver.setParams(freq, bw, sf, cr); + active_bw = bw; active_sf = sf; active_cr = cr; } diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 6e7a2a87..63e00cdf 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -141,6 +141,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { NeighbourInfo neighbours[MAX_NEIGHBOURS]; #endif CayenneLPP telemetry; + float active_bw; // live BW, including temporary radio overrides uint8_t active_sf; // live SF, including temporary radio overrides uint8_t active_cr; // live CR, including temporary radio overrides ScheduledRadioSetting scheduled_radio_settings[MAX_SCHEDULED_RADIO_SETTINGS]; diff --git a/src/Mesh.cpp b/src/Mesh.cpp index eaff68c0..ec886a77 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -5,10 +5,6 @@ namespace mesh { static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT = 15; static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX = 15; -static const uint32_t DIRECT_RETRY_BASE_MS_DEFAULT = 175; -static const uint32_t DIRECT_RETRY_STEP_MS_DEFAULT = 50; -static const uint8_t DIRECT_RETRY_SHORT_PATH_SCALE_HOPS = 6; -static const uint8_t DIRECT_RETRY_TRACE_SCALE_HOPS = 16; static const uint8_t FLOOD_RETRY_MAX_ATTEMPTS_DEFAULT = 15; static const uint8_t FLOOD_RETRY_MAX_ATTEMPTS_HARD_MAX = 15; static const uint8_t FLOOD_RETRY_MAX_PATH_DEFAULT = 1; @@ -68,33 +64,6 @@ static uint8_t getTraceDirectPriority(const Packet* packet) { return 5; } -static uint8_t getDirectRetryRemainingHops(const Packet* packet) { - if (packet == NULL) { - return 0; - } - if (packet->getPayloadType() == PAYLOAD_TYPE_TRACE) { - return getTraceRemainingHops(packet); - } - return packet->getPathHashCount(); -} - -static uint32_t scaleDirectRetryDelayForPath(const Packet* packet, uint32_t delay_ms) { - uint8_t hops = getDirectRetryRemainingHops(packet); - if (hops == 0) { - return delay_ms; - } - - uint8_t scale_hops = packet != NULL && packet->getPayloadType() == PAYLOAD_TYPE_TRACE - ? DIRECT_RETRY_TRACE_SCALE_HOPS - : DIRECT_RETRY_SHORT_PATH_SCALE_HOPS; - if (hops >= scale_hops) { - return delay_ms; - } - - uint32_t scaled = ((delay_ms * hops) + (scale_hops - 1U)) / scale_hops; - return scaled > 0 ? scaled : 1; -} - uint8_t Mesh::getDirectRetryCodingRateForAttempt(uint8_t start_cr, uint8_t retry_attempt) { if (start_cr < 4 || start_cr > 8) { return start_cr; @@ -261,16 +230,18 @@ bool Mesh::allowDirectRetry(const Packet* packet, const uint8_t* next_hop_hash, } uint32_t Mesh::getDirectRetryEchoDelay(const Packet* packet) const { (void)packet; - return DIRECT_RETRY_BASE_MS_DEFAULT; + // Keep the base fallback aligned with the repeater's minimum retry wait. + return 200; } uint8_t Mesh::getDirectRetryMaxAttempts(const Packet* packet) const { (void)packet; return DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT; } uint32_t Mesh::getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx) { + (void)packet; uint32_t base = getDirectRetryEchoDelay(packet); - uint32_t delay_ms = base + ((uint32_t)attempt_idx * DIRECT_RETRY_STEP_MS_DEFAULT); - return scaleDirectRetryDelayForPath(packet, delay_ms); + // Keep the historical linear spacing while allowing the base wait to vary by platform/profile. + return base + ((uint32_t)attempt_idx * 100UL); } bool Mesh::allowFloodRetry(const Packet* packet) const { (void)packet; From d686c25108b6e6b939b8186856786ce8513dd928 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 29 Jun 2026 13:10:54 -0700 Subject: [PATCH 178/214] Use logging infix for build artifacts --- build.sh | 52 ++++++++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/build.sh b/build.sh index 521bfff4..4d1f8107 100755 --- a/build.sh +++ b/build.sh @@ -9,7 +9,7 @@ SELECTED_TARGET="" SELECTED_COMMAND_ARGS=() MESHDEBUG_OVERRIDE="" PACKET_LOGGING_OVERRIDE="" -FIRMWARE_VERSION_SUFFIX="" +FIRMWARE_FILENAME_INFIX="" RADIO_SETTINGS_API_URL="https://api.meshcore.nz/api/v1/config" RADIO_SETTING_TITLE="" RADIO_FREQ_OVERRIDE="" @@ -69,7 +69,7 @@ $ bash build.sh Build all firmwares for device targets containing the string "RAK_4631" $ bash build.sh build-matching-firmwares -Build all firmwares twice, with logging-off artifacts using the base version and logging-on artifacts using a "-logging" version suffix: +Build all firmwares twice, with logging-off artifacts named "name-version" and logging-on artifacts named "name-logging-version": $ bash build.sh build-firmwares-logging-matrix Build all companion firmwares @@ -1035,25 +1035,6 @@ apply_firmware_profile_overrides() { esac } -append_firmware_version_suffix() { - local firmware_version=$1 - local suffix=${2#-} - - if [ -z "$suffix" ]; then - echo "$firmware_version" - return 0 - fi - - case "$firmware_version" in - *-"$suffix") - echo "$firmware_version" - ;; - *) - echo "${firmware_version}-${suffix}" - ;; - esac -} - print_build_flags() { local env_name=$1 @@ -1170,6 +1151,22 @@ collect_build_artifacts() { esac } +get_firmware_filename() { + local env_name=$1 + local firmware_version_string=$2 + local filename_infix=$FIRMWARE_FILENAME_INFIX + + if [ -z "$filename_infix" ] && [ "${PACKET_LOGGING_OVERRIDE,,}" == "on" ]; then + filename_infix="logging" + fi + + if [ -n "$filename_infix" ]; then + echo "${env_name}-${filename_infix}-${firmware_version_string}" + else + echo "${env_name}-${firmware_version_string}" + fi +} + restore_platformio_build_flags() { local had_platformio_build_flags=$1 local original_platformio_build_flags=${2:-} @@ -1213,8 +1210,7 @@ build_firmware() { fi firmware_version_string="${firmware_version}-${commit_hash}" - firmware_version_string=$(append_firmware_version_suffix "$firmware_version_string" "$FIRMWARE_VERSION_SUFFIX") - firmware_filename="${env_name}-${firmware_version_string}" + firmware_filename=$(get_firmware_filename "$env_name" "$firmware_version_string") if [ "${PLATFORMIO_BUILD_FLAGS+x}" ]; then had_platformio_build_flags=1 @@ -1397,7 +1393,7 @@ run_logging_matrix_build_targets() { local targets=("$@") local original_meshdebug_override=$MESHDEBUG_OVERRIDE local original_packet_logging_override=$PACKET_LOGGING_OVERRIDE - local original_firmware_version_suffix=$FIRMWARE_VERSION_SUFFIX + local original_firmware_filename_infix=$FIRMWARE_FILENAME_INFIX local build_status=0 if [ ${#targets[@]} -eq 0 ]; then @@ -1408,23 +1404,23 @@ run_logging_matrix_build_targets() { echo "Building ${#targets[@]} target(s) with MESH_DEBUG=off and MESH_PACKET_LOGGING=off." MESHDEBUG_OVERRIDE="off" PACKET_LOGGING_OVERRIDE="off" - FIRMWARE_VERSION_SUFFIX="" + FIRMWARE_FILENAME_INFIX="" run_resolved_build_targets "${targets[@]}" build_status=$? if [ "$build_status" -eq 0 ]; then echo "Building ${#targets[@]} target(s) with MESH_DEBUG=on and MESH_PACKET_LOGGING=on." - echo "Logging-on artifacts use firmware version suffix: -logging" + echo "Logging-on artifacts use filename form: name-logging-version" MESHDEBUG_OVERRIDE="on" PACKET_LOGGING_OVERRIDE="on" - FIRMWARE_VERSION_SUFFIX="logging" + FIRMWARE_FILENAME_INFIX="logging" run_resolved_build_targets "${targets[@]}" build_status=$? fi MESHDEBUG_OVERRIDE=$original_meshdebug_override PACKET_LOGGING_OVERRIDE=$original_packet_logging_override - FIRMWARE_VERSION_SUFFIX=$original_firmware_version_suffix + FIRMWARE_FILENAME_INFIX=$original_firmware_filename_infix return "$build_status" } From 37892bb90572053d051ed38da7cea7779712a1a3 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 29 Jun 2026 23:05:32 -0700 Subject: [PATCH 179/214] Update build all target handling --- build.sh | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 125 insertions(+), 9 deletions(-) diff --git a/build.sh b/build.sh index 4d1f8107..5fa2cdc2 100755 --- a/build.sh +++ b/build.sh @@ -53,7 +53,7 @@ Commands: list|-l: List firmwares available to build. build-firmware : Build the firmware for the given build target. build-firmwares: Build all firmwares for all targets. - build-firmwares-logging-matrix: Build all firmwares twice, first with MESH_DEBUG/MESH_PACKET_LOGGING off and then with both on. + build-firmwares-logging-matrix: Build all firmwares twice, first with MESH_DEBUG/MESH_PACKET_LOGGING off and then with both on for non-Bluetooth targets. build-matching-firmwares : Build all firmwares for build targets containing the string given for . build-companion-firmwares: Build all companion firmwares for all build targets. build-repeater-firmwares: Build all repeater firmwares for all build targets. @@ -262,7 +262,7 @@ prompt_for_build_mode() { local options=( "Build one firmware target" "Build all firmwares" - "Build all firmwares twice (logging off, then MESH_DEBUG + MESH_PACKET_LOGGING on)" + "Build all firmwares twice (logging off, then logging on for non-Bluetooth targets)" "Build all repeater firmwares" "Build all companion firmwares" "Build all chat room server firmwares" @@ -944,6 +944,101 @@ get_pio_envs_for_variant_role() { done } +is_kiss_modem_target() { + case "$(get_variant_name_for_env "$1")" in + kiss_modem|*_kiss_modem) + return 0 + ;; + *) + return 1 + ;; + esac +} + +is_bluetooth_target() { + case "$(get_variant_name_for_env "$1")" in + companion_radio_ble|*_companion_radio_ble) + return 0 + ;; + esac + + case "${1,,}" in + *companion_radio_ble*|*companion_ble*) + return 0 + ;; + *) + return 1 + ;; + esac +} + +filter_out_kiss_modem_targets() { + local target + local -a filtered_targets=() + + for target in "${RESOLVED_BUILD_TARGETS[@]}"; do + if ! is_kiss_modem_target "$target"; then + filtered_targets+=("$target") + fi + done + + RESOLVED_BUILD_TARGETS=("${filtered_targets[@]}") +} + +filter_out_bluetooth_targets() { + local target + + for target in "$@"; do + if ! is_bluetooth_target "$target"; then + printf '%s\n' "$target" + fi + done +} + +prompt_for_kiss_modem_build_policy() { + local kiss_count=0 + local target + local choice + + for target in "${RESOLVED_BUILD_TARGETS[@]}"; do + if is_kiss_modem_target "$target"; then + kiss_count=$((kiss_count + 1)) + fi + done + + if [ "$kiss_count" -eq 0 ]; then + return 0 + fi + + if ! [ -t 0 ]; then + echo "Including ${kiss_count} KISS modem target(s)." + return 0 + fi + + while true; do + read -r -p "KISS modem targets found: ${kiss_count}. Build or skip them? [build/skip] (default: build): " choice + choice=${choice,,} + if [ -z "$choice" ]; then + choice="build" + fi + + case "$choice" in + build) + echo "Including ${kiss_count} KISS modem target(s)." + return 0 + ;; + skip) + filter_out_kiss_modem_targets + echo "Skipped ${kiss_count} KISS modem target(s)." + return 0 + ;; + *) + echo "Invalid selection. Choose 'build' or 'skip'." + ;; + esac + done +} + get_platform_for_env() { local env_name=$1 @@ -1349,6 +1444,14 @@ resolve_command_targets() { echo "No supported build targets matched: ${*:2}" return 1 fi + + if is_bulk_build_command "$1"; then + prompt_for_kiss_modem_build_policy + if [ ${#RESOLVED_BUILD_TARGETS[@]} -eq 0 ]; then + echo "No build targets remain after skipping KISS modem targets." + return 1 + fi + fi } prepare_output_dir() { @@ -1391,9 +1494,11 @@ run_resolved_build_targets() { run_logging_matrix_build_targets() { local targets=("$@") + local logging_targets=() local original_meshdebug_override=$MESHDEBUG_OVERRIDE local original_packet_logging_override=$PACKET_LOGGING_OVERRIDE local original_firmware_filename_infix=$FIRMWARE_FILENAME_INFIX + local bluetooth_skip_count=0 local build_status=0 if [ ${#targets[@]} -eq 0 ]; then @@ -1409,13 +1514,24 @@ run_logging_matrix_build_targets() { build_status=$? if [ "$build_status" -eq 0 ]; then - echo "Building ${#targets[@]} target(s) with MESH_DEBUG=on and MESH_PACKET_LOGGING=on." - echo "Logging-on artifacts use filename form: name-logging-version" - MESHDEBUG_OVERRIDE="on" - PACKET_LOGGING_OVERRIDE="on" - FIRMWARE_FILENAME_INFIX="logging" - run_resolved_build_targets "${targets[@]}" - build_status=$? + mapfile -t logging_targets < <(filter_out_bluetooth_targets "${targets[@]}") + bluetooth_skip_count=$((${#targets[@]} - ${#logging_targets[@]})) + + if [ "$bluetooth_skip_count" -gt 0 ]; then + echo "Skipping ${bluetooth_skip_count} Bluetooth target(s) for logging-on pass." + fi + + if [ ${#logging_targets[@]} -gt 0 ]; then + echo "Building ${#logging_targets[@]} target(s) with MESH_DEBUG=on and MESH_PACKET_LOGGING=on." + echo "Logging-on artifacts use filename form: name-logging-version" + MESHDEBUG_OVERRIDE="on" + PACKET_LOGGING_OVERRIDE="on" + FIRMWARE_FILENAME_INFIX="logging" + run_resolved_build_targets "${logging_targets[@]}" + build_status=$? + else + echo "No non-Bluetooth targets remain for logging-on pass." + fi fi MESHDEBUG_OVERRIDE=$original_meshdebug_override From 28ee5d2c4b2e7772d3883aa4a9c2fa53208f17db Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 29 Jun 2026 23:09:39 -0700 Subject: [PATCH 180/214] Add resume support for logging matrix builds --- build.sh | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 108 insertions(+), 8 deletions(-) diff --git a/build.sh b/build.sh index 5fa2cdc2..0f868ed6 100755 --- a/build.sh +++ b/build.sh @@ -19,6 +19,7 @@ RADIO_CR_OVERRIDE="" FIRMWARE_PROFILE_OVERRIDE="${FIRMWARE_PROFILE_OVERRIDE:-}" BATCH_BUILD_MODE=0 RESOLVED_BUILD_TARGETS=() +RESUME_BUILD_OUTPUT="${RESUME_BUILD_OUTPUT:-0}" ENV_VARIANT_SUFFIX_PATTERN='companion_radio_serial|companion_radio_wifi|companion_radio_usb|comp_radio_usb|companion_usb|companion_radio_ble|companion_ble|repeater_bridge_rs232_serial1|repeater_bridge_rs232_serial2|repeater_bridge_rs232|repeater_bridge_espnow|terminal_chat|room_server|room_svr|kiss_modem|sensor|repeatr|repeater' BOARD_MODIFIER_WITHOUT_DISPLAY="_without_display" @@ -87,6 +88,8 @@ Environment Variables: In interactive builds, this value is offered as the editable default. DISABLE_DEBUG=1: Disables all debug logging flags (MESH_DEBUG, MESH_PACKET_LOGGING, etc.) If not set, debug flags from variant platformio.ini files are used. + RESUME_BUILD_OUTPUT=1: For build-firmwares-logging-matrix, preserves out/ and skips + targets whose expected output artifacts already exist. Examples: Build without debug logging: @@ -1039,6 +1042,57 @@ prompt_for_kiss_modem_build_policy() { done } +normalize_resume_build_output() { + case "${RESUME_BUILD_OUTPUT,,}" in + 1|true|yes|y|on|resume) + RESUME_BUILD_OUTPUT=1 + ;; + *) + RESUME_BUILD_OUTPUT=0 + ;; + esac +} + +prompt_for_logging_matrix_output_policy() { + local choice + + normalize_resume_build_output + + if ! [ -d "$OUTPUT_DIR" ]; then + RESUME_BUILD_OUTPUT=0 + return 0 + fi + + if ! [ -t 0 ]; then + if [ "$RESUME_BUILD_OUTPUT" == "1" ]; then + echo "Resuming previous logging matrix output in ${OUTPUT_DIR}." + fi + return 0 + fi + + while true; do + read -r -p "Output directory '${OUTPUT_DIR}' exists. Resume previous option 3 progress or clean it? [resume/clean] (default: clean): " choice + choice=${choice,,} + if [ -z "$choice" ]; then + choice="clean" + fi + + case "$choice" in + resume) + RESUME_BUILD_OUTPUT=1 + return 0 + ;; + clean) + RESUME_BUILD_OUTPUT=0 + return 0 + ;; + *) + echo "Invalid selection. Choose 'resume' or 'clean'." + ;; + esac + done +} + get_platform_for_env() { local env_name=$1 @@ -1188,8 +1242,8 @@ collect_esp32_artifacts() { local firmware_filename=$2 pio run -t mergebin -e "$env_name" || return $? - copy_build_output ".pio/build/${env_name}/firmware.bin" "out/${firmware_filename}.bin" || return $? - copy_build_output ".pio/build/${env_name}/firmware-merged.bin" "out/${firmware_filename}-merged.bin" || return $? + copy_build_output ".pio/build/${env_name}/firmware.bin" "${OUTPUT_DIR}/${firmware_filename}.bin" || return $? + copy_build_output ".pio/build/${env_name}/firmware-merged.bin" "${OUTPUT_DIR}/${firmware_filename}-merged.bin" || return $? } collect_nrf52_artifacts() { @@ -1197,9 +1251,9 @@ collect_nrf52_artifacts() { local firmware_filename=$2 python3 bin/uf2conv/uf2conv.py ".pio/build/${env_name}/firmware.hex" -c -o ".pio/build/${env_name}/firmware.uf2" -f 0xADA52840 || return $? - copy_build_output ".pio/build/${env_name}/firmware.uf2" "out/${firmware_filename}.uf2" || return $? + copy_build_output ".pio/build/${env_name}/firmware.uf2" "${OUTPUT_DIR}/${firmware_filename}.uf2" || return $? if [ -f ".pio/build/${env_name}/firmware.zip" ]; then - copy_build_output ".pio/build/${env_name}/firmware.zip" "out/${firmware_filename}.zip" || return $? + copy_build_output ".pio/build/${env_name}/firmware.zip" "${OUTPUT_DIR}/${firmware_filename}.zip" || return $? fi } @@ -1207,16 +1261,46 @@ collect_stm32_artifacts() { local env_name=$1 local firmware_filename=$2 - copy_build_output ".pio/build/${env_name}/firmware.bin" "out/${firmware_filename}.bin" || return $? - copy_build_output ".pio/build/${env_name}/firmware.hex" "out/${firmware_filename}.hex" || return $? + copy_build_output ".pio/build/${env_name}/firmware.bin" "${OUTPUT_DIR}/${firmware_filename}.bin" || return $? + copy_build_output ".pio/build/${env_name}/firmware.hex" "${OUTPUT_DIR}/${firmware_filename}.hex" || return $? } collect_rp2040_artifacts() { local env_name=$1 local firmware_filename=$2 - copy_build_output ".pio/build/${env_name}/firmware.bin" "out/${firmware_filename}.bin" || return $? - copy_build_output ".pio/build/${env_name}/firmware.uf2" "out/${firmware_filename}.uf2" || return $? + copy_build_output ".pio/build/${env_name}/firmware.bin" "${OUTPUT_DIR}/${firmware_filename}.bin" || return $? + copy_build_output ".pio/build/${env_name}/firmware.uf2" "${OUTPUT_DIR}/${firmware_filename}.uf2" || return $? +} + +output_artifact_exists() { + [ -s "${OUTPUT_DIR}/$1" ] +} + +build_artifacts_exist() { + local env_platform=$1 + local firmware_filename=$2 + + case "$env_platform" in + ESP32_PLATFORM) + output_artifact_exists "${firmware_filename}.bin" \ + && output_artifact_exists "${firmware_filename}-merged.bin" + ;; + NRF52_PLATFORM) + output_artifact_exists "${firmware_filename}.uf2" + ;; + STM32_PLATFORM) + output_artifact_exists "${firmware_filename}.bin" \ + && output_artifact_exists "${firmware_filename}.hex" + ;; + RP2040_PLATFORM) + output_artifact_exists "${firmware_filename}.bin" \ + && output_artifact_exists "${firmware_filename}.uf2" + ;; + *) + return 1 + ;; + esac } collect_build_artifacts() { @@ -1307,6 +1391,11 @@ build_firmware() { firmware_version_string="${firmware_version}-${commit_hash}" firmware_filename=$(get_firmware_filename "$env_name" "$firmware_version_string") + if [ "$RESUME_BUILD_OUTPUT" == "1" ] && build_artifacts_exist "$env_platform" "$firmware_filename"; then + echo "Skipping ${env_name}; existing artifacts found for ${firmware_filename}." + return 0 + fi + if [ "${PLATFORMIO_BUILD_FLAGS+x}" ]; then had_platformio_build_flags=1 original_platformio_build_flags=$PLATFORMIO_BUILD_FLAGS @@ -1462,6 +1551,12 @@ prepare_output_dir() { exit 1 fi + if [ "$RESUME_BUILD_OUTPUT" == "1" ]; then + mkdir -p -- "$output_dir" + echo "Resuming build output in ${output_dir}; existing artifacts will be skipped." + return 0 + fi + rm -rf -- "$output_dir" mkdir -p -- "$output_dir" } @@ -1625,6 +1720,11 @@ main() { fi prompt_for_resolved_firmware_version + if is_logging_matrix_command "$1"; then + prompt_for_logging_matrix_output_policy + else + RESUME_BUILD_OUTPUT=0 + fi prepare_output_dir run_command "$@" } From 2cb3a29fa86971fceab26ac72f229316b40b474b Mon Sep 17 00:00:00 2001 From: entr0p1 <1475255+entr0p1@users.noreply.github.com> Date: Tue, 30 Jun 2026 16:55:16 +1000 Subject: [PATCH 181/214] SX1262 LDO Support - Added build flag "SX126X_USE_REGULATOR_LDO" (set to 1 to enable) to use LDO for the SX1262 radio instead of DC-DC when the DCDC pin isn't wired up (e.g. t-echo lite) - Added additional debug mode diagnostic messages during SX1262 init to better highlight faults --- src/helpers/radiolib/CustomSX1262.h | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/helpers/radiolib/CustomSX1262.h b/src/helpers/radiolib/CustomSX1262.h index ad201229..ca62fc26 100644 --- a/src/helpers/radiolib/CustomSX1262.h +++ b/src/helpers/radiolib/CustomSX1262.h @@ -1,6 +1,7 @@ #pragma once #include +#include "MeshCore.h" #define SX126X_IRQ_HEADER_VALID 0b0000010000 // 4 4 valid LoRa header received #define SX126X_IRQ_PREAMBLE_DETECTED 0x04 @@ -27,6 +28,14 @@ class CustomSX1262 : public SX1262 { uint8_t cr = 5; #endif + #ifdef SX126X_USE_REGULATOR_LDO + constexpr bool useRegulatorLDO = SX126X_USE_REGULATOR_LDO; + #else + constexpr bool useRegulatorLDO = false; + #endif + + MESH_DEBUG_PRINTLN("SX1262 regulator requested: %s", useRegulatorLDO ? "LDO" : "DC-DC"); + #if defined(P_LORA_SCLK) #ifdef NRF52_PLATFORM if (spi) { spi->setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI); spi->begin(); } @@ -42,11 +51,12 @@ class CustomSX1262 : public SX1262 { if (spi) spi->begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); #endif #endif - int status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo); + int status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo, useRegulatorLDO); // if radio init fails with -707/-706, try again with tcxo voltage set to 0.0f if (status == RADIOLIB_ERR_SPI_CMD_FAILED || status == RADIOLIB_ERR_SPI_CMD_INVALID) { + MESH_DEBUG_PRINTLN("SX1262 init failed with error %d, retrying with TCXO at 0.0V", status); tcxo = 0.0f; - status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo); + status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo, useRegulatorLDO); } if (status != RADIOLIB_ERR_NONE) { Serial.print("ERROR: radio init failed: "); @@ -83,6 +93,8 @@ class CustomSX1262 : public SX1262 { writeRegister(0x8B5, &r_data, 1); #endif + MESH_DEBUG_PRINTLN("SX1262 status=0x%02X device_errors=0x%04X", getStatus(), getDeviceErrors()); + return true; // success } From 5ab52ca7c0e04db6b3627ea978e9d89bb5964bd2 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 30 Jun 2026 14:12:34 -0700 Subject: [PATCH 182/214] Add Mesh America provider catalogs --- mesh-america/README.md | 21 + .../generate-mesh-america-catalogs.ps1 | 275 + ...mind-cascade-logging-v1.16.0-provider.json | 8803 ++++++++++++ .../keymind-cascade-v1.16.0-provider.json | 11175 ++++++++++++++++ 4 files changed, 20274 insertions(+) create mode 100644 mesh-america/README.md create mode 100644 mesh-america/generate-mesh-america-catalogs.ps1 create mode 100644 mesh-america/keymind-cascade-logging-v1.16.0-provider.json create mode 100644 mesh-america/keymind-cascade-v1.16.0-provider.json diff --git a/mesh-america/README.md b/mesh-america/README.md new file mode 100644 index 00000000..e76b09f4 --- /dev/null +++ b/mesh-america/README.md @@ -0,0 +1,21 @@ +# Mesh America provider catalogs + +Generated provider catalogs for the Keymind Cascade MeshCore release assets. + +Send Mesh America these catalog URLs after this directory is committed and pushed to `keymindCascade`: + +```text +Provider name: Keymind Cascade +Catalog URL: https://raw.githubusercontent.com/mikecarper/MeshCore/keymindCascade/mesh-america/keymind-cascade-v1.16.0-provider.json +``` + +```text +Provider name: Keymind Cascade Logging +Catalog URL: https://raw.githubusercontent.com/mikecarper/MeshCore/keymindCascade/mesh-america/keymind-cascade-logging-v1.16.0-provider.json +``` + +Regenerate both catalogs from the release asset folders with: + +```powershell +powershell -ExecutionPolicy Bypass -File mesh-america\generate-mesh-america-catalogs.ps1 +``` diff --git a/mesh-america/generate-mesh-america-catalogs.ps1 b/mesh-america/generate-mesh-america-catalogs.ps1 new file mode 100644 index 00000000..b6b46f80 --- /dev/null +++ b/mesh-america/generate-mesh-america-catalogs.ps1 @@ -0,0 +1,275 @@ +$ErrorActionPreference = 'Stop' + +$Repo = 'mikecarper/MeshCore' +$OutputDir = $PSScriptRoot +$Root = Split-Path -Parent $PSScriptRoot + +$Catalogs = @( + @{ + Name = 'non-logging' + SourceDir = Join-Path $Root '.releases\non-logging' + Tag = 'v1.16.0-halo-keymind-dev-28ee5d2c' + Output = Join-Path $OutputDir 'keymind-cascade-v1.16.0-provider.json' + Description = 'Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.' + }, + @{ + Name = 'logging' + SourceDir = Join-Path $Root '.releases\logging' + Tag = 'logging-v1.16.0-halo-keymind-dev-28ee5d2c' + Output = Join-Path $OutputDir 'keymind-cascade-logging-v1.16.0-provider.json' + Description = 'Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.' + } +) + +$RoleDefinitions = [ordered]@{ + companionSerial = [ordered]@{ name = 'Companion Serial' } + companionWifi = [ordered]@{ name = 'Companion WiFi' } + repeaterBridgeEspnow = [ordered]@{ name = 'Repeater Bridge ESP-NOW' } + repeaterBridgeRs232 = [ordered]@{ name = 'Repeater Bridge RS232' } + sensor = [ordered]@{ name = 'Sensor' } + terminalChat = [ordered]@{ name = 'Terminal Chat' } +} + +$RolePatterns = @( + @{ Suffix = 'logging_repeater_bridge_espnow'; Role = 'repeaterBridgeEspnow'; Title = 'Repeater Bridge ESP-NOW'; SubTitle = 'Logging' }, + @{ Suffix = 'logging_repeater'; Role = 'repeater'; Title = 'Repeater'; SubTitle = 'Logging' }, + @{ Suffix = 'companion_radio_ble_femoff'; Role = 'companionBle'; Title = 'Companion BLE'; SubTitle = 'FEM off' }, + @{ Suffix = 'companion_radio_ble_femon'; Role = 'companionBle'; Title = 'Companion BLE'; SubTitle = 'FEM on' }, + @{ Suffix = 'companion_radio_ble_ps_femoff'; Role = 'companionBle'; Title = 'Companion BLE'; SubTitle = 'Power saving, FEM off' }, + @{ Suffix = 'companion_radio_ble_ps_femon'; Role = 'companionBle'; Title = 'Companion BLE'; SubTitle = 'Power saving, FEM on' }, + @{ Suffix = 'companion_radio_ble_ps'; Role = 'companionBle'; Title = 'Companion BLE'; SubTitle = 'Power saving' }, + @{ Suffix = 'companion_radio_ble_'; Role = 'companionBle'; Title = 'Companion BLE'; SubTitle = $null }, + @{ Suffix = 'companion_radio_ble'; Role = 'companionBle'; Title = 'Companion BLE'; SubTitle = $null }, + @{ Suffix = 'companion_ble'; Role = 'companionBle'; Title = 'Companion BLE'; SubTitle = $null }, + @{ Suffix = 'companion_radio_usb_'; Role = 'companionUsb'; Title = 'Companion USB'; SubTitle = $null }, + @{ Suffix = 'companion_radio_usb'; Role = 'companionUsb'; Title = 'Companion USB'; SubTitle = $null }, + @{ Suffix = 'companion_usb'; Role = 'companionUsb'; Title = 'Companion USB'; SubTitle = $null }, + @{ Suffix = 'comp_radio_usb'; Role = 'companionUsb'; Title = 'Companion USB'; SubTitle = $null }, + @{ Suffix = 'companion_radio_serial'; Role = 'companionSerial'; Title = 'Companion Serial'; SubTitle = $null }, + @{ Suffix = 'companion_radio_wifi'; Role = 'companionWifi'; Title = 'Companion WiFi'; SubTitle = $null }, + @{ Suffix = 'repeater_bridge_rs232_serial1'; Role = 'repeaterBridgeRs232'; Title = 'Repeater Bridge RS232'; SubTitle = 'Serial 1' }, + @{ Suffix = 'repeater_bridge_rs232_serial2'; Role = 'repeaterBridgeRs232'; Title = 'Repeater Bridge RS232'; SubTitle = 'Serial 2' }, + @{ Suffix = 'repeater_bridge_rs232'; Role = 'repeaterBridgeRs232'; Title = 'Repeater Bridge RS232'; SubTitle = $null }, + @{ Suffix = 'repeater_bridge_espnow_'; Role = 'repeaterBridgeEspnow'; Title = 'Repeater Bridge ESP-NOW'; SubTitle = $null }, + @{ Suffix = 'repeater_bridge_espnow'; Role = 'repeaterBridgeEspnow'; Title = 'Repeater Bridge ESP-NOW'; SubTitle = $null }, + @{ Suffix = 'Repeater'; Role = 'repeater'; Title = 'Repeater'; SubTitle = $null }, + @{ Suffix = 'repeater_'; Role = 'repeater'; Title = 'Repeater'; SubTitle = $null }, + @{ Suffix = 'repeatr'; Role = 'repeater'; Title = 'Repeater'; SubTitle = $null }, + @{ Suffix = 'repeater'; Role = 'repeater'; Title = 'Repeater'; SubTitle = $null }, + @{ Suffix = 'room_server_'; Role = 'roomServer'; Title = 'Room Server'; SubTitle = $null }, + @{ Suffix = 'room_server'; Role = 'roomServer'; Title = 'Room Server'; SubTitle = $null }, + @{ Suffix = 'room_svr'; Role = 'roomServer'; Title = 'Room Server'; SubTitle = $null }, + @{ Suffix = 'terminal_chat'; Role = 'terminalChat'; Title = 'Terminal Chat'; SubTitle = $null }, + @{ Suffix = 'sensor'; Role = 'sensor'; Title = 'Sensor'; SubTitle = $null } +) + +function ConvertTo-DeviceName { + param([string]$Prefix) + $name = $Prefix.Trim('_') + $name = $name -replace '_', ' ' + $name = $name -replace '\s+', ' ' + return $name.Trim() +} + +function Get-RoleInfo { + param([string]$DeviceRole) + + foreach ($pattern in $RolePatterns) { + $suffix = $pattern.Suffix + if ($DeviceRole.EndsWith("_$suffix", [StringComparison]::OrdinalIgnoreCase) -or + $DeviceRole.EndsWith($suffix, [StringComparison]::OrdinalIgnoreCase)) { + $prefixLength = $DeviceRole.Length - $suffix.Length + if ($prefixLength -gt 0 -and $DeviceRole[$prefixLength - 1] -eq '_') { + $prefixLength-- + } + + $devicePrefix = $DeviceRole.Substring(0, $prefixLength).Trim('_') + if (-not $devicePrefix) { + throw "Unable to parse device name from '$DeviceRole'" + } + + return [ordered]@{ + DeviceKey = $devicePrefix + DeviceName = ConvertTo-DeviceName $devicePrefix + Role = $pattern.Role + Title = $pattern.Title + SubTitle = $pattern.SubTitle + } + } + } + + throw "Unable to parse role from '$DeviceRole'" +} + +function Get-FileSortRank { + param([string]$Type, [string]$Name) + + switch ($Type) { + 'flash-wipe' { return 10 } + 'flash-update' { return 20 } + 'flash' { return 30 } + default { + if ($Name.EndsWith('.uf2', [StringComparison]::OrdinalIgnoreCase)) { return 40 } + if ($Name.EndsWith('.hex', [StringComparison]::OrdinalIgnoreCase)) { return 50 } + return 60 + } + } +} + +function Get-DeviceType { + param([array]$Files) + + $extensions = @($Files | ForEach-Object { $_.Extension.ToLowerInvariant() } | Sort-Object -Unique) + $hasMergedBin = @($Files | Where-Object { + $_.Extension -ieq '.bin' -and $_.BaseName.EndsWith('-merged', [StringComparison]::OrdinalIgnoreCase) + }).Count -gt 0 + + if ($hasMergedBin) { return 'esp32' } + if ($extensions -contains '.zip') { return 'nrf52' } + return 'noflash' +} + +function Get-FileType { + param( + [System.IO.FileInfo]$File, + [string]$DeviceType + ) + + $extension = $File.Extension.ToLowerInvariant() + $isMerged = $File.BaseName.EndsWith('-merged', [StringComparison]::OrdinalIgnoreCase) + + if ($DeviceType -eq 'esp32' -and $extension -eq '.bin' -and $isMerged) { return 'flash-wipe' } + if ($DeviceType -eq 'esp32' -and $extension -eq '.bin') { return 'flash-update' } + if ($DeviceType -eq 'nrf52' -and $extension -eq '.zip') { return 'flash' } + return 'download' +} + +function Get-FileTitle { + param([string]$Type, [string]$Name) + + switch ($Type) { + 'flash-wipe' { return 'Full install (bootloader + firmware)' } + 'flash-update' { return 'Update (app only)' } + 'flash' { return 'Serial DFU package' } + default { + if ($Name.EndsWith('.uf2', [StringComparison]::OrdinalIgnoreCase)) { return 'UF2 download' } + if ($Name.EndsWith('.hex', [StringComparison]::OrdinalIgnoreCase)) { return 'HEX download' } + return 'Download' + } + } +} + +function Get-DownloadUrl { + param( + [string]$Tag, + [string]$Name + ) + + $escapedName = [Uri]::EscapeDataString($Name).Replace('%2F', '/') + return "https://github.com/$Repo/releases/download/$Tag/$escapedName" +} + +function New-Catalog { + param([hashtable]$Definition) + + if (-not (Test-Path -LiteralPath $Definition.SourceDir)) { + throw "Source directory not found: $($Definition.SourceDir)" + } + + $sourceFiles = @(Get-ChildItem -LiteralPath $Definition.SourceDir -File | Sort-Object Name) + if (-not $sourceFiles.Count) { + throw "No release files found in $($Definition.SourceDir)" + } + + $parsedFiles = foreach ($file in $sourceFiles) { + $stem = $file.BaseName + if ($stem.EndsWith('-merged', [StringComparison]::OrdinalIgnoreCase)) { + $stem = $stem.Substring(0, $stem.Length - '-merged'.Length) + } + + $suffix = "-$($Definition.Tag)" + if (-not $stem.EndsWith($suffix, [StringComparison]::Ordinal)) { + throw "File '$($file.Name)' does not end with expected tag '$($Definition.Tag)'" + } + + $deviceRole = $stem.Substring(0, $stem.Length - $suffix.Length) + $roleInfo = Get-RoleInfo $deviceRole + + [pscustomobject]@{ + File = $file + DeviceKey = $roleInfo.DeviceKey + DeviceName = $roleInfo.DeviceName + Role = $roleInfo.Role + Title = $roleInfo.Title + SubTitle = $roleInfo.SubTitle + } + } + + $devices = New-Object System.Collections.ArrayList + foreach ($deviceGroup in @($parsedFiles | Group-Object DeviceKey | Sort-Object Name)) { + $deviceFiles = @($deviceGroup.Group | ForEach-Object { $_.File }) + $deviceType = Get-DeviceType $deviceFiles + $firmware = New-Object System.Collections.ArrayList + + foreach ($roleGroup in @($deviceGroup.Group | Group-Object Role,Title,SubTitle | Sort-Object Name)) { + $first = $roleGroup.Group[0] + $files = @( + $roleGroup.Group | + Sort-Object @{ Expression = { Get-FileSortRank (Get-FileType $_.File $deviceType) $_.File.Name } }, @{ Expression = { $_.File.Name } } | + ForEach-Object { + $type = Get-FileType $_.File $deviceType + [ordered]@{ + type = $type + name = $_.File.Name + url = Get-DownloadUrl $Definition.Tag $_.File.Name + title = Get-FileTitle $type $_.File.Name + } + } + ) + + $firmwareEntry = [ordered]@{ + role = $first.Role + title = $first.Title + } + + if ($first.SubTitle) { + $firmwareEntry.subTitle = $first.SubTitle + } + + $firmwareEntry.version = [ordered]@{ + $Definition.Tag = [ordered]@{ + notes = $Definition.Description + files = $files + } + } + + [void]$firmware.Add($firmwareEntry) + } + + [void]$devices.Add([ordered]@{ + maker = 'keymindCascade' + class = 'keymindCascade' + name = $deviceGroup.Group[0].DeviceName + type = $deviceType + firmware = @($firmware) + }) + } + + return [ordered]@{ + description = $Definition.Description + maker = [ordered]@{ + keymindCascade = [ordered]@{ name = 'Keymind Cascade' } + } + role = $RoleDefinitions + device = @($devices) + } +} + +New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null + +foreach ($catalogDef in $Catalogs) { + $catalog = New-Catalog $catalogDef + $json = $catalog | ConvertTo-Json -Depth 100 + [System.IO.File]::WriteAllText($catalogDef.Output, $json + [Environment]::NewLine, [System.Text.UTF8Encoding]::new($false)) + Write-Output ("Wrote {0}" -f $catalogDef.Output) +} diff --git a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json new file mode 100644 index 00000000..e0933aee --- /dev/null +++ b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json @@ -0,0 +1,8803 @@ +{ + "description": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "maker": { + "keymindCascade": { + "name": "Keymind Cascade" + } + }, + "role": { + "companionSerial": { + "name": "Companion Serial" + }, + "companionWifi": { + "name": "Companion WiFi" + }, + "repeaterBridgeEspnow": { + "name": "Repeater Bridge ESP-NOW" + }, + "repeaterBridgeRs232": { + "name": "Repeater Bridge RS232" + }, + "sensor": { + "name": "Sensor" + }, + "terminalChat": { + "name": "Terminal Chat" + } + }, + "device": [ + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Ebyte EoRa-S3", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Ebyte_EoRa-S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Ebyte_EoRa-S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Ebyte_EoRa-S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Ebyte_EoRa-S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Ebyte_EoRa-S3_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Ebyte_EoRa-S3_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Ebyte_EoRa-S3_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Ebyte_EoRa-S3_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Ebyte_EoRa-S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Ebyte_EoRa-S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Ebyte_EoRa-S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Ebyte_EoRa-S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Ebyte_EoRa-S3_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Ebyte_EoRa-S3_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Ebyte_EoRa-S3_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Ebyte_EoRa-S3_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "GAT562 30S Mesh Kit", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "GAT562_30S_Mesh_Kit_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_30S_Mesh_Kit_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_30S_Mesh_Kit_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_30S_Mesh_Kit_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "GAT562_30S_Mesh_Kit_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_30S_Mesh_Kit_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_30S_Mesh_Kit_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_30S_Mesh_Kit_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "GAT562_30S_Mesh_Kit_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_30S_Mesh_Kit_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_30S_Mesh_Kit_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_30S_Mesh_Kit_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "GAT562 Mesh EVB Pro", + "type": "nrf52", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "GAT562 Mesh Tracker Pro", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_Tracker_Pro_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Tracker_Pro_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_Tracker_Pro_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Tracker_Pro_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_Tracker_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Tracker_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_Tracker_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Tracker_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_Tracker_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Tracker_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_Tracker_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Tracker_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Generic E22 sx1262", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Generic_E22_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Generic_E22_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Generic_E22_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Generic_E22_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Generic E22 sx1268", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Generic_E22_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Generic_E22_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Generic_E22_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Generic_E22_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Generic ESPNOW", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Generic_ESPNOW_comp_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_ESPNOW_comp_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Generic_ESPNOW_comp_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_ESPNOW_comp_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Generic_ESPNOW_repeatr-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_ESPNOW_repeatr-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Generic_ESPNOW_repeatr-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_ESPNOW_repeatr-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Generic_ESPNOW_room_svr-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_ESPNOW_room_svr-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Generic_ESPNOW_room_svr-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_ESPNOW_room_svr-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Generic_ESPNOW_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_ESPNOW_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Generic_ESPNOW_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_ESPNOW_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec ct62", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_ct62_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_ct62_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_ct62_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_ct62_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_ct62_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_ct62_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_ct62_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_ct62_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec E213", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E213_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E213_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E213_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E213_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E213_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E213_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E213_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E213_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec E290", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E290_companion_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_companion_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E290_companion_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_companion_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E290_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E290_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E290_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E290_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E290_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E290_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec mesh solar", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_mesh_solar_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_mesh_solar_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_mesh_solar_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_mesh_solar_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_mesh_solar_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_mesh_solar_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec t096", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_t096_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t096_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_t096_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t096_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_t096_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t096_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_t096_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t096_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_t096_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t096_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec t1", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_t1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_t1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_t1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec t114", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec t114 without display", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_without_display_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_without_display_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_without_display_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec T190", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_T190_companion_radio_usb_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_companion_radio_usb_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_T190_companion_radio_usb_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_companion_radio_usb_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_T190_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_T190_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_T190_repeater_bridge_espnow_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_bridge_espnow_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_T190_repeater_bridge_espnow_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_bridge_espnow_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_T190_room_server_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_room_server_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_T190_room_server_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_room_server_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "heltec tracker v2", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec v2", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec v3", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v3_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v3_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v3_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v3_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "heltec v4", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "heltec v4 expansionkit", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_expansionkit_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_expansionkit_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_expansionkit_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_expansionkit_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "heltec v4 tft", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec Wireless Paper", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Paper_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Paper_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Paper_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Paper_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Paper_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Paper_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Paper_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Paper_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec Wireless Tracker", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Tracker_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Tracker_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Tracker_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Tracker_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Tracker_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Tracker_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Tracker_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Tracker_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec WSL3", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_WSL3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_WSL3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_WSL3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_WSL3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_WSL3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_WSL3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_WSL3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_WSL3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_WSL3_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_WSL3_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_WSL3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_WSL3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_WSL3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_WSL3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ikoka handheld nrf e22 30dbm", + "type": "nrf52", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_handheld_nrf_e22_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_handheld_nrf_e22_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_handheld_nrf_e22_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_handheld_nrf_e22_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ikoka handheld nrf e22 30dbm 096", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ikoka handheld nrf e22 30dbm 096 rotated", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ikoka nano nrf 22dbm", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_22dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_22dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ikoka nano nrf 30dbm", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_30dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_30dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ikoka nano nrf 33dbm", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_33dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_33dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_33dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_33dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_33dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_33dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_33dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_33dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_33dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_33dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_33dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_33dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ikoka stick nrf 22dbm", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_22dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_22dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ikoka stick nrf 30dbm", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_30dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_30dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ikoka stick nrf 33dbm", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_33dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_33dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_33dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_33dbm_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_33dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_33dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_33dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_33dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_33dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_33dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_33dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_33dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "KeepteenLT1", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "KeepteenLT1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/KeepteenLT1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "KeepteenLT1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/KeepteenLT1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "KeepteenLT1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/KeepteenLT1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "KeepteenLT1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/KeepteenLT1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "KeepteenLT1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/KeepteenLT1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "KeepteenLT1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/KeepteenLT1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T Impulse Plus", + "type": "nrf52", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T3S3 sx1262", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1262_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1262_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1262_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1262_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T3S3 sx1276", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1276_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1276_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1276_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1276_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1276_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1276_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1276_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1276_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1276_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1276_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo TBeam 1W", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo TDeck", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TDeck_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TDeck_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TDeck_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TDeck_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TDeck_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TDeck_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TDeck_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TDeck_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T-Echo", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T-Echo Card", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo_Card_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_Card_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo_Card_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_Card_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo_Card_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_Card_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo_Card_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_Card_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo_Card_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_Card_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo_Card_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_Card_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T-Echo-Lite", + "type": "nrf52", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo-Lite_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo-Lite_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo-Lite_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo-Lite_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T-Echo-Lite non shell", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo TETH Elite sx1262", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TETH_Elite_sx1262_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TETH_Elite_sx1262_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TETH_Elite_sx1262_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TETH_Elite_sx1262_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TETH_Elite_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TETH_Elite_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TETH_Elite_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TETH_Elite_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TETH_Elite_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TETH_Elite_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TETH_Elite_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TETH_Elite_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo Tlora C6", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_Tlora_C6_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_Tlora_C6_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_Tlora_C6_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_Tlora_C6_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_Tlora_C6_room_server_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_Tlora_C6_room_server_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_Tlora_C6_room_server_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_Tlora_C6_room_server_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo TLora V2 1 1 6", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "M5Stack Unit C6L", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "M5Stack_Unit_C6L_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/M5Stack_Unit_C6L_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "M5Stack_Unit_C6L_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/M5Stack_Unit_C6L_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "M5Stack_Unit_C6L_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/M5Stack_Unit_C6L_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "M5Stack_Unit_C6L_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/M5Stack_Unit_C6L_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "M5Stack_Unit_C6L_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/M5Stack_Unit_C6L_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "M5Stack_Unit_C6L_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/M5Stack_Unit_C6L_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Mesh pocket", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Mesh_pocket_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Mesh_pocket_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Mesh_pocket_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Mesh_pocket_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Mesh_pocket_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Mesh_pocket_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Meshadventurer sx1262", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Meshadventurer sx1268", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1268_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1268_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1268_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1268_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1268_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1268_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Meshimi", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshimi_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshimi_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshimi_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshimi_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Meshtiny", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Meshtiny_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshtiny_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Meshtiny_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshtiny_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Minewsemi me25ls01", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Minewsemi_me25ls01_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Minewsemi_me25ls01_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Minewsemi_me25ls01_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Minewsemi_me25ls01_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Minewsemi_me25ls01_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Minewsemi_me25ls01_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Minewsemi_me25ls01_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Minewsemi_me25ls01_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Minewsemi_me25ls01_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Minewsemi_me25ls01_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Minewsemi_me25ls01_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Minewsemi_me25ls01_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Minewsemi_me25ls01_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Minewsemi_me25ls01_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Minewsemi_me25ls01_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Minewsemi_me25ls01_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Nano G2 Ultra", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Nano_G2_Ultra_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Nano_G2_Ultra_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Nano_G2_Ultra_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Nano_G2_Ultra_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Nano_G2_Ultra_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Nano_G2_Ultra_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "nibble screen connect", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "nibble_screen_connect_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "nibble_screen_connect_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "nibble_screen_connect_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "nibble_screen_connect_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "nibble_screen_connect_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "nibble_screen_connect_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "nibble_screen_connect_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "nibble_screen_connect_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "nibble_screen_connect_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "nibble_screen_connect_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "nibble_screen_connect_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "nibble_screen_connect_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "PicoW", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "PicoW_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "PicoW_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "PicoW_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "PicoW_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "PicoW_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "PicoW_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "PicoW_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "PicoW_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ProMicro", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ProMicro_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ProMicro_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ProMicro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ProMicro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "subTitle": "Serial 1", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ProMicro_repeater_bridge_rs232_serial1-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_repeater_bridge_rs232_serial1-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ProMicro_repeater_bridge_rs232_serial1-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_repeater_bridge_rs232_serial1-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ProMicro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ProMicro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ProMicro_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ProMicro_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ProMicro_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ProMicro_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "R1Neo", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "R1Neo_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "R1Neo_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "R1Neo_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "R1Neo_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "R1Neo_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "R1Neo_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "R1Neo_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "R1Neo_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "R1Neo_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "R1Neo_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "RAK 11310", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "RAK_11310_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "RAK_11310_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "RAK_11310_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "RAK_11310_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "RAK_11310_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "RAK_11310_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "RAK_11310_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "RAK_11310_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "RAK_11310_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "RAK_11310_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "RAK 3112", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "RAK_3112_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "RAK_3112_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "RAK_3112_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "RAK_3112_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "RAK_3112_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "RAK_3112_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "RAK_3112_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "RAK_3112_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "RAK_3112_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "RAK_3112_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "RAK_3112_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "RAK_3112_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "RAK_3112_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "RAK_3112_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "RAK_3112_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "RAK_3112_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "RAK 3401", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_3401_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_3401_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_3401_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_3401_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_3401_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_3401_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_3401_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_3401_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_3401_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_3401_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "RAK 3x72", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "RAK_3x72_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "RAK_3x72_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "RAK_3x72_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "RAK_3x72_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "RAK_3x72_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "RAK_3x72_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "RAK 4631", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "subTitle": "Serial 1", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_repeater_bridge_rs232_serial1-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial1-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_repeater_bridge_rs232_serial1-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial1-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "subTitle": "Serial 2", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_repeater_bridge_rs232_serial2-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial2-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_repeater_bridge_rs232_serial2-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial2-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "RAK WisMesh Tag", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_WisMesh_Tag_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_WisMesh_Tag_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_WisMesh_Tag_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_WisMesh_Tag_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_WisMesh_Tag_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_WisMesh_Tag_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_WisMesh_Tag_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_WisMesh_Tag_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_WisMesh_Tag_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_WisMesh_Tag_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_WisMesh_Tag_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_WisMesh_Tag_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_WisMesh_Tag_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_WisMesh_Tag_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_WisMesh_Tag_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_WisMesh_Tag_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "SenseCap Solar", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "SenseCap_Solar_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/SenseCap_Solar_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "SenseCap_Solar_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/SenseCap_Solar_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "SenseCap_Solar_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/SenseCap_Solar_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "SenseCap_Solar_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/SenseCap_Solar_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "SenseCap_Solar_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/SenseCap_Solar_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "SenseCap_Solar_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/SenseCap_Solar_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "SenseCapIndicator-ESPNow", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "SenseCapIndicator-ESPNow_comp_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/SenseCapIndicator-ESPNow_comp_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "SenseCapIndicator-ESPNow_comp_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/SenseCapIndicator-ESPNow_comp_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Station G2", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "Logging", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G2_logging_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_logging_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G2_logging_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_logging_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "subTitle": "Logging", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G2_logging_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_logging_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G2_logging_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_logging_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Station G3 ESP32", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G3_ESP32_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G3_ESP32_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G3_ESP32_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G3_ESP32_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G3_ESP32_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G3_ESP32_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "Logging", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G3_ESP32_logging_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_logging_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G3_ESP32_logging_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_logging_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G3_ESP32_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G3_ESP32_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "T Beam S3 Supreme SX1262", + "type": "esp32", + "firmware": [ + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "t1000e", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "t1000e_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "t1000e_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "t1000e_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "t1000e_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "t1000e_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "t1000e_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Tbeam SX1262", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Tbeam SX1276", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1276_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1276_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1276_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1276_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1276_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1276_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Tenstar C3 sx1262", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Tenstar_C3_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tenstar_C3_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Tenstar C3 sx1268", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Tenstar_C3_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tenstar_C3_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Tenstar_C3_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tenstar_C3_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ThinkNode M1", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ThinkNode M2", + "type": "esp32", + "firmware": [ + { + "role": "companionSerial", + "title": "Companion Serial", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ThinkNode M3", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ThinkNode M5", + "type": "esp32", + "firmware": [ + { + "role": "companionSerial", + "title": "Companion Serial", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ThinkNode M6", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Tiny Relay", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Tiny_Relay_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "Tiny_Relay_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Tiny_Relay_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "Tiny_Relay_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Tiny_Relay_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "Tiny_Relay_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "waveshare rp2040 lora", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "WHY2025 badge", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "WHY2025_badge_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WHY2025_badge_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "WHY2025_badge_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WHY2025_badge_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "wio wm1110", + "type": "noflash", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "wio_wm1110_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio_wm1110_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "wio_wm1110_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio_wm1110_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "wio-e5", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "wio-e5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "wio-e5-", + "type": "noflash", + "firmware": [ + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "wio-e5-mini", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "wio-e5-mini_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5-mini_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "wio-e5-mini_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5-mini_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "wio-e5-mini_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5-mini_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "WioTrackerL1", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "WioTrackerL1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "WioTrackerL1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "WioTrackerL1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "WioTrackerL1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "WioTrackerL1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "WioTrackerL1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Xiao C3", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Xiao C6", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C6_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C6_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C6_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C6_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Xiao nrf52", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Xiao_nrf52_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Xiao_nrf52_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Xiao_nrf52_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Xiao_nrf52_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Xiao_nrf52_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Xiao_nrf52_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Xiao rp2040", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Xiao_rp2040_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Xiao_rp2040_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Xiao_rp2040_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Xiao_rp2040_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Xiao_rp2040_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Xiao_rp2040_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Xiao_rp2040_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Xiao_rp2040_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Xiao S3", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Xiao S3 WIO", + "type": "esp32", + "firmware": [ + { + "role": "companionSerial", + "title": "Companion Serial", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + } + ] +} diff --git a/mesh-america/keymind-cascade-v1.16.0-provider.json b/mesh-america/keymind-cascade-v1.16.0-provider.json new file mode 100644 index 00000000..359ec3f5 --- /dev/null +++ b/mesh-america/keymind-cascade-v1.16.0-provider.json @@ -0,0 +1,11175 @@ +{ + "description": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "maker": { + "keymindCascade": { + "name": "Keymind Cascade" + } + }, + "role": { + "companionSerial": { + "name": "Companion Serial" + }, + "companionWifi": { + "name": "Companion WiFi" + }, + "repeaterBridgeEspnow": { + "name": "Repeater Bridge ESP-NOW" + }, + "repeaterBridgeRs232": { + "name": "Repeater Bridge RS232" + }, + "sensor": { + "name": "Sensor" + }, + "terminalChat": { + "name": "Terminal Chat" + } + }, + "device": [ + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Ebyte EoRa-S3", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Ebyte_EoRa-S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Ebyte_EoRa-S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Ebyte_EoRa-S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Ebyte_EoRa-S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Ebyte_EoRa-S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Ebyte_EoRa-S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Ebyte_EoRa-S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Ebyte_EoRa-S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Ebyte_EoRa-S3_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Ebyte_EoRa-S3_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Ebyte_EoRa-S3_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Ebyte_EoRa-S3_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Ebyte_EoRa-S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Ebyte_EoRa-S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Ebyte_EoRa-S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Ebyte_EoRa-S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Ebyte_EoRa-S3_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Ebyte_EoRa-S3_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Ebyte_EoRa-S3_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Ebyte_EoRa-S3_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "GAT562 30S Mesh Kit", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "GAT562_30S_Mesh_Kit_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_30S_Mesh_Kit_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_30S_Mesh_Kit_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_30S_Mesh_Kit_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "GAT562_30S_Mesh_Kit_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_30S_Mesh_Kit_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_30S_Mesh_Kit_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_30S_Mesh_Kit_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "GAT562_30S_Mesh_Kit_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_30S_Mesh_Kit_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_30S_Mesh_Kit_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_30S_Mesh_Kit_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "GAT562_30S_Mesh_Kit_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_30S_Mesh_Kit_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_30S_Mesh_Kit_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_30S_Mesh_Kit_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "GAT562 Mesh EVB Pro", + "type": "nrf52", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "GAT562 Mesh Tracker Pro", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_Tracker_Pro_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Tracker_Pro_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_Tracker_Pro_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Tracker_Pro_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_Tracker_Pro_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Tracker_Pro_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_Tracker_Pro_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Tracker_Pro_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_Tracker_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Tracker_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_Tracker_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Tracker_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_Tracker_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Tracker_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_Tracker_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Tracker_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "GAT562 Mesh Watch13", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Generic E22 sx1262", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Generic_E22_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Generic_E22_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Generic_E22_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Generic_E22_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Generic E22 sx1268", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Generic_E22_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Generic_E22_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Generic_E22_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Generic_E22_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Generic ESPNOW", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Generic_ESPNOW_comp_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_ESPNOW_comp_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Generic_ESPNOW_comp_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_ESPNOW_comp_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Generic_ESPNOW_repeatr-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_ESPNOW_repeatr-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Generic_ESPNOW_repeatr-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_ESPNOW_repeatr-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Generic_ESPNOW_room_svr-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_ESPNOW_room_svr-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Generic_ESPNOW_room_svr-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_ESPNOW_room_svr-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Generic_ESPNOW_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_ESPNOW_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Generic_ESPNOW_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_ESPNOW_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec ct62", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_ct62_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_ct62_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_ct62_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_ct62_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_ct62_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_ct62_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_ct62_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_ct62_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_ct62_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_ct62_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_ct62_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_ct62_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_ct62_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec E213", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E213_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E213_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E213_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E213_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E213_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E213_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E213_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E213_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E213_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E213_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec E290", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E290_companion_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_companion_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E290_companion_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_companion_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E290_companion_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_companion_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E290_companion_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_companion_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E290_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E290_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E290_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E290_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E290_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E290_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec mesh solar", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_mesh_solar_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_mesh_solar_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_mesh_solar_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_mesh_solar_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_mesh_solar_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_mesh_solar_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_mesh_solar_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_mesh_solar_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec t096", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t096_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t096_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "FEM off", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t096_companion_radio_ble_femoff-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_companion_radio_ble_femoff-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t096_companion_radio_ble_femoff-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_companion_radio_ble_femoff-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "FEM on", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t096_companion_radio_ble_femon-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_companion_radio_ble_femon-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t096_companion_radio_ble_femon-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_companion_radio_ble_femon-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t096_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t096_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t096_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t096_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t096_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t096_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t096_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t096_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t096_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t096_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t096_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec t1", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec t114", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec t114 without display", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_without_display_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_without_display_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_without_display_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_without_display_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec T190", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_T190_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_T190_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_T190_companion_radio_usb_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_companion_radio_usb_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_T190_companion_radio_usb_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_companion_radio_usb_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_T190_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_T190_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_T190_repeater_bridge_espnow_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_bridge_espnow_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_T190_repeater_bridge_espnow_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_bridge_espnow_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_T190_room_server_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_room_server_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_T190_room_server_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_room_server_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "heltec tracker v2", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec v2", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v2_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v2_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec v3", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v3_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v3_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_v3_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_v3_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_v3_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "heltec v4", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving, FEM on", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_companion_radio_ble_ps_femon-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_ble_ps_femon-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_companion_radio_ble_ps_femon-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_ble_ps_femon-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "heltec v4 3", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving, FEM off", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_3_companion_radio_ble_ps_femoff-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_3_companion_radio_ble_ps_femoff-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_3_companion_radio_ble_ps_femoff-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_3_companion_radio_ble_ps_femoff-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "heltec v4 expansionkit", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_expansionkit_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_expansionkit_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_expansionkit_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_expansionkit_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "heltec v4 expansionkit tft", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_expansionkit_tft_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_expansionkit_tft_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_expansionkit_tft_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_expansionkit_tft_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "heltec v4 tft", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec Wireless Paper", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Paper_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Paper_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Paper_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Paper_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Paper_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Paper_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Paper_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Paper_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Paper_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Paper_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Paper_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Paper_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec Wireless Tracker", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Tracker_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Tracker_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Tracker_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Tracker_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Tracker_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Tracker_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Tracker_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Tracker_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Tracker_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Tracker_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Tracker_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Tracker_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Tracker_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec WSL3", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_WSL3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_WSL3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_WSL3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_WSL3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_WSL3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_WSL3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_WSL3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_WSL3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_WSL3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_WSL3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_WSL3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_WSL3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_WSL3_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_WSL3_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_WSL3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_WSL3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_WSL3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_WSL3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_WSL3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ikoka handheld nrf e22 30dbm", + "type": "nrf52", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_handheld_nrf_e22_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_handheld_nrf_e22_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_handheld_nrf_e22_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_handheld_nrf_e22_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ikoka handheld nrf e22 30dbm 096", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_handheld_nrf_e22_30dbm_096_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_handheld_nrf_e22_30dbm_096_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ikoka handheld nrf e22 30dbm 096 rotated", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ikoka nano nrf 22dbm", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_22dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_22dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ikoka nano nrf 30dbm", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_30dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_30dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ikoka nano nrf 33dbm", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_33dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_33dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_33dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_33dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_33dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_33dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_33dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_33dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_33dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_33dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_33dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_33dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_33dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_33dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_33dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_33dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ikoka stick nrf 22dbm", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_22dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_22dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ikoka stick nrf 30dbm", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_30dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_30dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ikoka stick nrf 33dbm", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_33dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_33dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_33dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_33dbm_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_33dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_33dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_33dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_33dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_33dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_33dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_33dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_33dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_33dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_33dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_33dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_33dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "KeepteenLT1", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "KeepteenLT1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/KeepteenLT1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "KeepteenLT1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/KeepteenLT1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "KeepteenLT1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/KeepteenLT1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "KeepteenLT1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/KeepteenLT1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "KeepteenLT1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/KeepteenLT1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "KeepteenLT1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/KeepteenLT1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "KeepteenLT1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/KeepteenLT1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "KeepteenLT1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/KeepteenLT1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T Impulse Plus", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T3S3 sx1262", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1262_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1262_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1262_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T3S3 sx1276", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1276_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1276_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1276_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1276_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1276_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1276_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1276_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1276_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1276_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1276_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_T3S3_sx1276_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_T3S3_sx1276_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T3S3_sx1276_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo TBeam 1W", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo TDeck", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TDeck_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TDeck_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TDeck_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TDeck_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TDeck_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TDeck_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TDeck_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TDeck_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TDeck_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TDeck_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TDeck_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TDeck_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T-Echo", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T-Echo Card", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo_Card_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_Card_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo_Card_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_Card_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo_Card_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_Card_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo_Card_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_Card_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo_Card_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_Card_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo_Card_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_Card_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo_Card_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_Card_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo_Card_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo_Card_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T-Echo-Lite", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo-Lite_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo-Lite_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo-Lite_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo-Lite_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo-Lite_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo-Lite_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T-Echo-Lite non shell", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo-Lite_non_shell_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_non_shell_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo-Lite_non_shell_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_non_shell_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo TETH Elite sx1262", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TETH_Elite_sx1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TETH_Elite_sx1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TETH_Elite_sx1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TETH_Elite_sx1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TETH_Elite_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TETH_Elite_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TETH_Elite_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TETH_Elite_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TETH_Elite_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TETH_Elite_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TETH_Elite_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TETH_Elite_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TETH_Elite_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TETH_Elite_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TETH_Elite_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TETH_Elite_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo Tlora C6", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_Tlora_C6_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_Tlora_C6_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_Tlora_C6_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_Tlora_C6_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_Tlora_C6_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_Tlora_C6_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_Tlora_C6_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_Tlora_C6_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_Tlora_C6_room_server_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_Tlora_C6_room_server_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_Tlora_C6_room_server_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_Tlora_C6_room_server_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo TLora V2 1 1 6", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "M5Stack Unit C6L", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "M5Stack_Unit_C6L_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/M5Stack_Unit_C6L_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "M5Stack_Unit_C6L_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/M5Stack_Unit_C6L_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "M5Stack_Unit_C6L_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/M5Stack_Unit_C6L_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "M5Stack_Unit_C6L_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/M5Stack_Unit_C6L_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "M5Stack_Unit_C6L_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/M5Stack_Unit_C6L_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "M5Stack_Unit_C6L_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/M5Stack_Unit_C6L_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "M5Stack_Unit_C6L_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/M5Stack_Unit_C6L_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "M5Stack_Unit_C6L_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/M5Stack_Unit_C6L_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Mesh pocket", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Mesh_pocket_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Mesh_pocket_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Mesh_pocket_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Mesh_pocket_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Mesh_pocket_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Mesh_pocket_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Mesh_pocket_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Mesh_pocket_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Meshadventurer sx1262", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Meshadventurer sx1268", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1268_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1268_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1268_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1268_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1268_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1268_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1268_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1268_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1268_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Meshimi", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshimi_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshimi_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshimi_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshimi_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshimi_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshimi_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshimi_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshimi_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Meshtiny", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Meshtiny_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshtiny_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Meshtiny_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshtiny_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Meshtiny_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshtiny_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Meshtiny_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshtiny_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Minewsemi me25ls01", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Minewsemi_me25ls01_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Minewsemi_me25ls01_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Minewsemi_me25ls01_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Minewsemi_me25ls01_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Minewsemi_me25ls01_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Minewsemi_me25ls01_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Minewsemi_me25ls01_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Minewsemi_me25ls01_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Minewsemi_me25ls01_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Minewsemi_me25ls01_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Minewsemi_me25ls01_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Minewsemi_me25ls01_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Minewsemi_me25ls01_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Minewsemi_me25ls01_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Minewsemi_me25ls01_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Minewsemi_me25ls01_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Minewsemi_me25ls01_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Minewsemi_me25ls01_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Minewsemi_me25ls01_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Minewsemi_me25ls01_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Nano G2 Ultra", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Nano_G2_Ultra_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Nano_G2_Ultra_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Nano_G2_Ultra_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Nano_G2_Ultra_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Nano_G2_Ultra_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Nano_G2_Ultra_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Nano_G2_Ultra_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Nano_G2_Ultra_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "nibble screen connect", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "nibble_screen_connect_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "nibble_screen_connect_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "nibble_screen_connect_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "nibble_screen_connect_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "nibble_screen_connect_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "nibble_screen_connect_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "nibble_screen_connect_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "nibble_screen_connect_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "nibble_screen_connect_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "nibble_screen_connect_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "nibble_screen_connect_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "nibble_screen_connect_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "nibble_screen_connect_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "nibble_screen_connect_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/nibble_screen_connect_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "PicoW", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "PicoW_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "PicoW_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "PicoW_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "PicoW_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "PicoW_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "PicoW_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "PicoW_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "PicoW_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ProMicro", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ProMicro_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ProMicro_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ProMicro_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ProMicro_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ProMicro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ProMicro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "subTitle": "Serial 1", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ProMicro_repeater_bridge_rs232_serial1-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_repeater_bridge_rs232_serial1-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ProMicro_repeater_bridge_rs232_serial1-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_repeater_bridge_rs232_serial1-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ProMicro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ProMicro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ProMicro_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ProMicro_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ProMicro_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ProMicro_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ProMicro_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "R1Neo", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "R1Neo_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "R1Neo_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "R1Neo_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "R1Neo_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "R1Neo_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "R1Neo_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "R1Neo_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "R1Neo_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "R1Neo_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "R1Neo_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "R1Neo_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "R1Neo_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "RAK 11310", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "RAK_11310_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "RAK_11310_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "RAK_11310_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "RAK_11310_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "RAK_11310_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "RAK_11310_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "RAK_11310_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "RAK_11310_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "RAK_11310_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "RAK_11310_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_11310_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "RAK 3112", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "RAK_3112_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "RAK_3112_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "RAK_3112_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "RAK_3112_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "RAK_3112_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "RAK_3112_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "RAK_3112_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "RAK_3112_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "RAK_3112_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "RAK_3112_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "RAK_3112_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "RAK_3112_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "RAK_3112_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "RAK_3112_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "RAK_3112_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "RAK_3112_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "RAK_3112_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "RAK_3112_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3112_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "RAK 3401", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_3401_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_3401_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_3401_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_3401_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_3401_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_3401_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_3401_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_3401_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_3401_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_3401_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_3401_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_3401_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3401_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "RAK 3x72", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "RAK_3x72_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "RAK_3x72_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "RAK_3x72_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "RAK_3x72_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "RAK_3x72_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "RAK_3x72_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "RAK 4631", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "subTitle": "Serial 1", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_repeater_bridge_rs232_serial1-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial1-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_repeater_bridge_rs232_serial1-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial1-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "subTitle": "Serial 2", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_repeater_bridge_rs232_serial2-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial2-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_repeater_bridge_rs232_serial2-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial2-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "RAK WisMesh Tag", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_WisMesh_Tag_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_WisMesh_Tag_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_WisMesh_Tag_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_WisMesh_Tag_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_WisMesh_Tag_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_WisMesh_Tag_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_WisMesh_Tag_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_WisMesh_Tag_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_WisMesh_Tag_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_WisMesh_Tag_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_WisMesh_Tag_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_WisMesh_Tag_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_WisMesh_Tag_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_WisMesh_Tag_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_WisMesh_Tag_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_WisMesh_Tag_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_WisMesh_Tag_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_WisMesh_Tag_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_WisMesh_Tag_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_WisMesh_Tag_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "SenseCap Solar", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "SenseCap_Solar_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/SenseCap_Solar_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "SenseCap_Solar_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/SenseCap_Solar_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "SenseCap_Solar_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/SenseCap_Solar_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "SenseCap_Solar_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/SenseCap_Solar_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "SenseCap_Solar_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/SenseCap_Solar_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "SenseCap_Solar_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/SenseCap_Solar_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "SenseCap_Solar_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/SenseCap_Solar_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "SenseCap_Solar_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/SenseCap_Solar_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "SenseCapIndicator-ESPNow", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "SenseCapIndicator-ESPNow_comp_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/SenseCapIndicator-ESPNow_comp_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "SenseCapIndicator-ESPNow_comp_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/SenseCapIndicator-ESPNow_comp_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Station G2", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "Logging", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G2_logging_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_logging_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G2_logging_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_logging_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "subTitle": "Logging", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G2_logging_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_logging_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G2_logging_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_logging_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Station G3 ESP32", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G3_ESP32_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G3_ESP32_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G3_ESP32_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G3_ESP32_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G3_ESP32_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G3_ESP32_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G3_ESP32_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G3_ESP32_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "Logging", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G3_ESP32_logging_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_logging_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G3_ESP32_logging_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_logging_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G3_ESP32_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G3_ESP32_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G3_ESP32_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "T Beam S3 Supreme SX1262", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "t1000e", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "t1000e_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "t1000e_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "t1000e_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "t1000e_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "t1000e_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "t1000e_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "t1000e_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "t1000e_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Tbeam SX1262", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Tbeam SX1276", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1276_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1276_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1276_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1276_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1276_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1276_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1276_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1276_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1276_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1276_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Tenstar C3 sx1262", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tenstar_C3_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tenstar_C3_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Tenstar C3 sx1268", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tenstar_C3_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tenstar_C3_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tenstar_C3_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tenstar_C3_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ThinkNode M1", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ThinkNode M2", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionSerial", + "title": "Companion Serial", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ThinkNode M3", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ThinkNode M5", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionSerial", + "title": "Companion Serial", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "ThinkNode M6", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Tiny Relay", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Tiny_Relay_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "Tiny_Relay_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Tiny_Relay_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "Tiny_Relay_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Tiny_Relay_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "Tiny_Relay_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "waveshare rp2040 lora", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "WHY2025 badge", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "WHY2025_badge_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WHY2025_badge_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "WHY2025_badge_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WHY2025_badge_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "WHY2025_badge_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WHY2025_badge_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "WHY2025_badge_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WHY2025_badge_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "wio wm1110", + "type": "noflash", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio_wm1110_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio_wm1110_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio_wm1110_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio_wm1110_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio_wm1110_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio_wm1110_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "wio-e5", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio-e5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "wio-e5-", + "type": "noflash", + "firmware": [ + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "wio-e5-mini", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio-e5-mini_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5-mini_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio-e5-mini_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5-mini_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio-e5-mini_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5-mini_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "WioTrackerL1", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "WioTrackerL1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "WioTrackerL1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "WioTrackerL1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "WioTrackerL1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "WioTrackerL1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "WioTrackerL1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "WioTrackerL1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "WioTrackerL1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "WioTrackerL1Eink", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "WioTrackerL1Eink_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1Eink_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "WioTrackerL1Eink_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1Eink_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Xiao C3", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Xiao C6", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C6_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C6_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C6_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C6_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C6_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C6_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C6_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C6_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Xiao nrf52", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Xiao_nrf52_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Xiao_nrf52_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Xiao_nrf52_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Xiao_nrf52_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Xiao_nrf52_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Xiao_nrf52_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Xiao_nrf52_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Xiao_nrf52_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Xiao rp2040", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Xiao_rp2040_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Xiao_rp2040_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Xiao_rp2040_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Xiao_rp2040_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Xiao_rp2040_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Xiao_rp2040_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Xiao_rp2040_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Xiao_rp2040_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Xiao S3", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Xiao S3 WIO", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionSerial", + "title": "Companion Serial", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + } + ] +} From bcb75839dd1f72edf2977ba458c9aa5064f2c49a Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 30 Jun 2026 14:18:23 -0700 Subject: [PATCH 183/214] Improve Mesh America device name matching --- .../generate-mesh-america-catalogs.ps1 | 105 +- ...mind-cascade-logging-v1.16.0-provider.json | 6506 +++++++------ .../keymind-cascade-v1.16.0-provider.json | 8421 ++++++++--------- 3 files changed, 7405 insertions(+), 7627 deletions(-) diff --git a/mesh-america/generate-mesh-america-catalogs.ps1 b/mesh-america/generate-mesh-america-catalogs.ps1 index b6b46f80..9a7ad540 100644 --- a/mesh-america/generate-mesh-america-catalogs.ps1 +++ b/mesh-america/generate-mesh-america-catalogs.ps1 @@ -63,14 +63,113 @@ $RolePatterns = @( @{ Suffix = 'sensor'; Role = 'sensor'; Title = 'Sensor'; SubTitle = $null } ) +$DeviceNameOverrides = @{ + 'GAT562_30S_Mesh_Kit' = 'GAT-IoT GAT562 30s' + 'GAT562_Mesh_Tracker_Pro' = 'GAT-IoT GAT562 Tracker' + 'Heltec_E213' = 'Heltec Vision Master E213' + 'Heltec_E290' = 'Heltec Vision Master E290' + 'Heltec_mesh_solar' = 'Heltec MeshSolar / MeshTower' + 'Heltec_t096' = 'Heltec Mesh Node T096' + 'Heltec_t1' = 'Heltec Mesh Node T1' + 'Heltec_t114' = 'Heltec T114' + 'Heltec_t114_without_display' = 'Heltec T114' + 'Heltec_T190' = 'Heltec T114' + 'heltec_tracker_v2' = 'Heltec Wireless Tracker v2' + 'heltec_v4' = 'Heltec v4' + 'heltec_v4_3' = 'Heltec v4' + 'heltec_v4_expansionkit' = 'Heltec v4 + Expansion Kit (Touch)' + 'heltec_v4_expansionkit_tft' = 'Heltec v4 + Expansion Kit (Touch)' + 'heltec_v4_tft' = 'Heltec v4' + 'Heltec_Wireless_Paper' = 'Heltec Heltec Wireless Paper' + 'ikoka_nano_nrf_22dbm' = 'Ikoka Nano' + 'ikoka_nano_nrf_30dbm' = 'Ikoka Nano' + 'ikoka_nano_nrf_33dbm' = 'Ikoka Nano' + 'ikoka_stick_nrf_22dbm' = 'Ikoka Stick' + 'ikoka_stick_nrf_30dbm' = 'Ikoka Stick' + 'ikoka_stick_nrf_33dbm' = 'Ikoka Stick' + 'KeepteenLT1' = 'Keepteen LT1' + 'LilyGo_T3S3_sx1262' = 'LilyGo T3 S3 (SX126x)' + 'LilyGo_T3S3_sx1276' = 'LilyGo T3 S3 (SX127x)' + 'LilyGo_TBeam_1W' = 'LilyGo T-Beam (SX1262)' + 'LilyGo_TDeck' = 'LilyGo T-Deck' + 'LilyGo_T-Echo-Lite' = 'LilyGo T-Echo Lite' + 'LilyGo_TLora_V2_1_1_6' = 'LilyGo LoRa32 V2.1_1.6' + 'Mesh_pocket' = 'Heltec MeshPocket' + 'Nano_G2_Ultra' = 'UnitEng Nano G2 Ultra' + 'ProMicro' = 'ProMicro nrf52 (faketec)' + 'R1Neo' = 'Muzi Works R1 Neo' + 'RAK_3112' = 'RAK WisBlock 3112' + 'RAK_3401' = 'RAK WisMesh 1W Booster (3401 + 13302)' + 'RAK_4631' = 'RAK WisBlock / WisMesh (RAK 4631)' + 'SenseCap_Solar' = 'Seeed Studio SenseCAP Solar' + 'Station_G2' = 'UnitEng Station G2' + 'Station_G3_ESP32' = 'UnitEng/BQ Voyage Station G3' + 'T_Beam_S3_Supreme_SX1262' = 'LilyGo T-Beam Supreme (SX1262)' + 't1000e' = 'Seeed Studio SenseCAP T1000-E' + 'Tbeam_SX1262' = 'LilyGo T-Beam (SX1262)' + 'Tbeam_SX1276' = 'LilyGo T-Beam 1.2 (SX1276)' + 'ThinkNode_M1' = 'Elecrow ThinkNode M1' + 'ThinkNode_M2' = 'Elecrow ThinkNode M2' + 'ThinkNode_M3' = 'Elecrow ThinkNode M3' + 'ThinkNode_M5' = 'Elecrow ThinkNode M5' + 'ThinkNode_M6' = 'Elecrow ThinkNode M6' + 'waveshare_rp2040_lora' = 'RPI Pico 2040 + WaveShare SX1262' + 'WioTrackerL1' = 'Seeed Studio Wio Tracker L1 Pro' + 'WioTrackerL1Eink' = 'Seeed Studio Wio Tracker L1 EINK' + 'Xiao_C3' = 'Seeed Studio Xiao C3' + 'Xiao_nrf52' = 'Seeed Studio Xiao nRF52 WIO' + 'Xiao_S3' = 'Seeed Studio Xiao S3 WIO' + 'Xiao_S3_WIO' = 'Seeed Studio Xiao S3 WIO' +} + +$DeviceSubTitleOverrides = @{ + 'Heltec_t114_without_display' = 'Without display' + 'Heltec_T190' = 'T190' + 'heltec_v4_3' = 'v4.3' + 'heltec_v4_tft' = 'TFT' + 'heltec_v4_expansionkit' = 'Expansion Kit' + 'heltec_v4_expansionkit_tft' = 'Expansion Kit TFT' + 'ikoka_nano_nrf_22dbm' = '22 dBm' + 'ikoka_nano_nrf_30dbm' = '30 dBm' + 'ikoka_nano_nrf_33dbm' = '33 dBm' + 'ikoka_stick_nrf_22dbm' = '22 dBm' + 'ikoka_stick_nrf_30dbm' = '30 dBm' + 'ikoka_stick_nrf_33dbm' = '33 dBm' +} + function ConvertTo-DeviceName { param([string]$Prefix) + if ($DeviceNameOverrides.ContainsKey($Prefix)) { + return $DeviceNameOverrides[$Prefix] + } + $name = $Prefix.Trim('_') $name = $name -replace '_', ' ' $name = $name -replace '\s+', ' ' return $name.Trim() } +function Join-SubTitle { + param( + [string]$DeviceKey, + [string]$RoleSubTitle + ) + + $parts = @() + if ($DeviceSubTitleOverrides.ContainsKey($DeviceKey)) { + $parts += $DeviceSubTitleOverrides[$DeviceKey] + } + if ($RoleSubTitle) { + $parts += $RoleSubTitle + } + + if (-not $parts.Count) { + return $null + } + + return ($parts -join ', ') +} + function Get-RoleInfo { param([string]$DeviceRole) @@ -79,7 +178,7 @@ function Get-RoleInfo { if ($DeviceRole.EndsWith("_$suffix", [StringComparison]::OrdinalIgnoreCase) -or $DeviceRole.EndsWith($suffix, [StringComparison]::OrdinalIgnoreCase)) { $prefixLength = $DeviceRole.Length - $suffix.Length - if ($prefixLength -gt 0 -and $DeviceRole[$prefixLength - 1] -eq '_') { + if ($prefixLength -gt 0 -and ($DeviceRole[$prefixLength - 1] -eq '_' -or $DeviceRole[$prefixLength - 1] -eq '-')) { $prefixLength-- } @@ -201,12 +300,12 @@ function New-Catalog { DeviceName = $roleInfo.DeviceName Role = $roleInfo.Role Title = $roleInfo.Title - SubTitle = $roleInfo.SubTitle + SubTitle = Join-SubTitle $roleInfo.DeviceKey $roleInfo.SubTitle } } $devices = New-Object System.Collections.ArrayList - foreach ($deviceGroup in @($parsedFiles | Group-Object DeviceKey | Sort-Object Name)) { + foreach ($deviceGroup in @($parsedFiles | Group-Object DeviceName | Sort-Object Name)) { $deviceFiles = @($deviceGroup.Group | ForEach-Object { $_.File }) $deviceType = Get-DeviceType $deviceFiles $firmware = New-Object System.Collections.ArrayList diff --git a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json index e0933aee..17db5ec9 100644 --- a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json @@ -129,7 +129,630 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "GAT562 30S Mesh Kit", + "name": "Elecrow ThinkNode M1", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Elecrow ThinkNode M2", + "type": "esp32", + "firmware": [ + { + "role": "companionSerial", + "title": "Companion Serial", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Elecrow ThinkNode M3", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Elecrow ThinkNode M5", + "type": "esp32", + "firmware": [ + { + "role": "companionSerial", + "title": "Companion Serial", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Elecrow ThinkNode M6", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "GAT562 Mesh EVB Pro", + "type": "nrf52", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "GAT-IoT GAT562 30s", "type": "nrf52", "firmware": [ { @@ -206,61 +829,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "GAT562 Mesh EVB Pro", - "type": "nrf52", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "GAT562 Mesh Tracker Pro", + "name": "GAT-IoT GAT562 Tracker", "type": "nrf52", "firmware": [ { @@ -645,7 +1214,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Heltec E213", + "name": "Heltec Heltec Wireless Paper", "type": "esp32", "firmware": [ { @@ -657,14 +1226,14 @@ "files": [ { "type": "flash-wipe", - "name": "Heltec_E213_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "name": "Heltec_Wireless_Paper_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", - "name": "Heltec_E213_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "name": "Heltec_Wireless_Paper_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" } ] @@ -680,14 +1249,14 @@ "files": [ { "type": "flash-wipe", - "name": "Heltec_E213_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "name": "Heltec_Wireless_Paper_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", - "name": "Heltec_E213_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "name": "Heltec_Wireless_Paper_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" } ] @@ -703,14 +1272,14 @@ "files": [ { "type": "flash-wipe", - "name": "Heltec_E213_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "name": "Heltec_Wireless_Paper_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", - "name": "Heltec_E213_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "name": "Heltec_Wireless_Paper_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" } ] @@ -726,14 +1295,14 @@ "files": [ { "type": "flash-wipe", - "name": "Heltec_E213_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "name": "Heltec_Wireless_Paper_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", - "name": "Heltec_E213_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "name": "Heltec_Wireless_Paper_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" } ] @@ -745,184 +1314,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Heltec E290", - "type": "esp32", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_E290_companion_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_companion_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_E290_companion_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_companion_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_E290_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_E290_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_E290_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_E290_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_E290_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_E290_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Heltec mesh solar", - "type": "nrf52", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "Heltec_mesh_solar_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Heltec_mesh_solar_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "Heltec_mesh_solar_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Heltec_mesh_solar_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "Heltec_mesh_solar_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Heltec_mesh_solar_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Heltec t096", + "name": "Heltec Mesh Node T096", "type": "nrf52", "firmware": [ { @@ -1045,7 +1437,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Heltec t1", + "name": "Heltec Mesh Node T1", "type": "nrf52", "firmware": [ { @@ -1122,7 +1514,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Heltec t114", + "name": "Heltec MeshPocket", "type": "nrf52", "firmware": [ { @@ -1134,14 +1526,14 @@ "files": [ { "type": "flash", - "name": "Heltec_t114_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "Mesh_pocket_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Heltec_t114_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "Mesh_pocket_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -1157,37 +1549,14 @@ "files": [ { "type": "flash", - "name": "Heltec_t114_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "Mesh_pocket_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Heltec_t114_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeaterBridgeRs232", - "title": "Repeater Bridge RS232", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "Heltec_t114_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Heltec_t114_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "Mesh_pocket_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -1203,14 +1572,14 @@ "files": [ { "type": "flash", - "name": "Heltec_t114_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "Mesh_pocket_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Heltec_t114_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "Mesh_pocket_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -1222,7 +1591,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Heltec t114 without display", + "name": "Heltec MeshSolar / MeshTower", "type": "nrf52", "firmware": [ { @@ -1234,14 +1603,14 @@ "files": [ { "type": "flash", - "name": "Heltec_t114_without_display_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "Heltec_mesh_solar_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Heltec_t114_without_display_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "Heltec_mesh_solar_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -1257,37 +1626,14 @@ "files": [ { "type": "flash", - "name": "Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "Heltec_mesh_solar_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeaterBridgeRs232", - "title": "Repeater Bridge RS232", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "Heltec_t114_without_display_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Heltec_t114_without_display_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "Heltec_mesh_solar_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -1303,14 +1649,14 @@ "files": [ { "type": "flash", - "name": "Heltec_t114_without_display_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "Heltec_mesh_solar_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Heltec_t114_without_display_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "Heltec_mesh_solar_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -1322,12 +1668,36 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Heltec T190", + "name": "Heltec T114", "type": "esp32", "firmware": [ { "role": "companionUsb", "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Heltec_t114_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Heltec_t114_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "T190", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -1348,9 +1718,57 @@ } } }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "Without display", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Heltec_t114_without_display_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Heltec_t114_without_display_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Download" + } + ] + } + } + }, { "role": "repeater", "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Heltec_t114_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Heltec_t114_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "T190", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -1371,9 +1789,34 @@ } } }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "Without display", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Download" + } + ] + } + } + }, { "role": "repeaterBridgeEspnow", "title": "Repeater Bridge ESP-NOW", + "subTitle": "T190", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -1394,9 +1837,80 @@ } } }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Heltec_t114_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Heltec_t114_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "subTitle": "Without display", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Heltec_t114_without_display_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Heltec_t114_without_display_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Download" + } + ] + } + } + }, { "role": "roomServer", "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Heltec_t114_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Heltec_t114_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "subTitle": "T190", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -1416,171 +1930,26 @@ ] } } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "heltec tracker v2", - "type": "esp32", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_tracker_v2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_tracker_v2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_tracker_v2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_tracker_v2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_tracker_v2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_tracker_v2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_tracker_v2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_tracker_v2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } }, { "role": "roomServer", "title": "Room Server", + "subTitle": "Without display", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", "files": [ { - "type": "flash-wipe", - "name": "heltec_tracker_v2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" + "type": "download", + "name": "Heltec_t114_without_display_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" }, { - "type": "flash-update", - "name": "heltec_tracker_v2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "sensor", - "title": "Sensor", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_tracker_v2_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_tracker_v2_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_tracker_v2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_tracker_v2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" + "type": "download", + "name": "Heltec_t114_without_display_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Download" } ] } @@ -1929,7 +2298,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "heltec v4", + "name": "Heltec v4", "type": "esp32", "firmware": [ { @@ -1955,6 +2324,30 @@ } } }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "TFT", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "companionWifi", "title": "Companion WiFi", @@ -1978,6 +2371,30 @@ } } }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "subTitle": "TFT", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "repeater", "title": "Repeater", @@ -2001,6 +2418,30 @@ } } }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "TFT", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "repeaterBridgeEspnow", "title": "Repeater Bridge ESP-NOW", @@ -2024,6 +2465,30 @@ } } }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "subTitle": "TFT", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "roomServer", "title": "Room Server", @@ -2047,6 +2512,30 @@ } } }, + { + "role": "roomServer", + "title": "Room Server", + "subTitle": "TFT", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "sensor", "title": "Sensor", @@ -2070,186 +2559,10 @@ } } }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "heltec v4 expansionkit", - "type": "esp32", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_expansionkit_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_expansionkit_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_expansionkit_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_expansionkit_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "heltec v4 tft", - "type": "esp32", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_tft_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_tft_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_tft_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_tft_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_tft_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_tft_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_tft_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_tft_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, { "role": "sensor", "title": "Sensor", + "subTitle": "TFT", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -2273,6 +2586,30 @@ { "role": "terminalChat", "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "subTitle": "TFT", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -2298,7 +2635,39 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Heltec Wireless Paper", + "name": "Heltec v4 + Expansion Kit (Touch)", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "subTitle": "Expansion Kit", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_expansionkit_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_expansionkit_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_expansionkit_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_expansionkit_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec Vision Master E213", "type": "esp32", "firmware": [ { @@ -2310,14 +2679,14 @@ "files": [ { "type": "flash-wipe", - "name": "Heltec_Wireless_Paper_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "name": "Heltec_E213_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", - "name": "Heltec_Wireless_Paper_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "name": "Heltec_E213_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" } ] @@ -2333,14 +2702,14 @@ "files": [ { "type": "flash-wipe", - "name": "Heltec_Wireless_Paper_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "name": "Heltec_E213_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", - "name": "Heltec_Wireless_Paper_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "name": "Heltec_E213_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" } ] @@ -2356,14 +2725,14 @@ "files": [ { "type": "flash-wipe", - "name": "Heltec_Wireless_Paper_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "name": "Heltec_E213_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", - "name": "Heltec_Wireless_Paper_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "name": "Heltec_E213_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" } ] @@ -2379,14 +2748,114 @@ "files": [ { "type": "flash-wipe", - "name": "Heltec_Wireless_Paper_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "name": "Heltec_E213_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", - "name": "Heltec_Wireless_Paper_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "name": "Heltec_E213_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec Vision Master E290", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E290_companion_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_companion_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E290_companion_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_companion_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E290_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E290_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E290_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E290_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E290_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E290_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" } ] @@ -2495,6 +2964,175 @@ } ] }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec Wireless Tracker v2", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -2783,12 +3421,13 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "ikoka nano nrf 22dbm", + "name": "Ikoka Nano", "type": "nrf52", "firmware": [ { "role": "companionUsb", "title": "Companion USB", + "subTitle": "22 dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -2809,63 +3448,10 @@ } } }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ikoka nano nrf 30dbm", - "type": "nrf52", - "firmware": [ { "role": "companionUsb", "title": "Companion USB", + "subTitle": "30 dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -2886,63 +3472,10 @@ } } }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ikoka nano nrf 33dbm", - "type": "nrf52", - "firmware": [ { "role": "companionUsb", "title": "Companion USB", + "subTitle": "33 dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -2966,6 +3499,55 @@ { "role": "repeater", "title": "Repeater", + "subTitle": "22 dBm", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "30 dBm", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "33 dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -2989,6 +3571,55 @@ { "role": "roomServer", "title": "Room Server", + "subTitle": "22 dBm", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "subTitle": "30 dBm", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "subTitle": "33 dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3014,12 +3645,13 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "ikoka stick nrf 22dbm", + "name": "Ikoka Stick", "type": "nrf52", "firmware": [ { "role": "companionUsb", "title": "Companion USB", + "subTitle": "22 dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3040,63 +3672,10 @@ } } }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ikoka stick nrf 30dbm", - "type": "nrf52", - "firmware": [ { "role": "companionUsb", "title": "Companion USB", + "subTitle": "30 dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3117,63 +3696,10 @@ } } }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ikoka stick nrf 33dbm", - "type": "nrf52", - "firmware": [ { "role": "companionUsb", "title": "Companion USB", + "subTitle": "33 dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3197,6 +3723,55 @@ { "role": "repeater", "title": "Repeater", + "subTitle": "22 dBm", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "30 dBm", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "33 dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3220,6 +3795,55 @@ { "role": "roomServer", "title": "Room Server", + "subTitle": "22 dBm", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "subTitle": "30 dBm", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "subTitle": "33 dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3245,7 +3869,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "KeepteenLT1", + "name": "Keepteen LT1", "type": "nrf52", "firmware": [ { @@ -3319,6 +3943,175 @@ } ] }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo LoRa32 V2.1_1.6", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -3353,7 +4146,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "LilyGo T3S3 sx1262", + "name": "LilyGo T3 S3 (SX126x)", "type": "esp32", "firmware": [ { @@ -3476,7 +4269,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "LilyGo T3S3 sx1276", + "name": "LilyGo T3 S3 (SX127x)", "type": "esp32", "firmware": [ { @@ -3599,7 +4392,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "LilyGo TBeam 1W", + "name": "LilyGo T-Beam (SX1262)", "type": "esp32", "firmware": [ { @@ -3661,11 +4454,23 @@ "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, + { + "type": "flash-wipe", + "name": "Tbeam_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, { "type": "flash-update", "name": "LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" } ] } @@ -3684,11 +4489,23 @@ "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, + { + "type": "flash-wipe", + "name": "Tbeam_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, { "type": "flash-update", "name": "LilyGo_TBeam_1W_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" } ] } @@ -3707,11 +4524,23 @@ "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, + { + "type": "flash-wipe", + "name": "Tbeam_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, { "type": "flash-update", "name": "LilyGo_TBeam_1W_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" } ] } @@ -3722,7 +4551,184 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "LilyGo TDeck", + "name": "LilyGo T-Beam 1.2 (SX1276)", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1276_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1276_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1276_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1276_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1276_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1276_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T-Beam Supreme (SX1262)", + "type": "esp32", + "firmware": [ + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T-Deck", "type": "esp32", "firmware": [ { @@ -3930,7 +4936,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "LilyGo T-Echo-Lite", + "name": "LilyGo T-Echo Lite", "type": "nrf52", "firmware": [ { @@ -4143,175 +5149,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "LilyGo TLora V2 1 1 6", - "type": "esp32", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TLora_V2_1_1_6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TLora_V2_1_1_6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TLora_V2_1_1_6_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TLora_V2_1_1_6_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TLora_V2_1_1_6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TLora_V2_1_1_6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeRs232", - "title": "Repeater Bridge RS232", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TLora_V2_1_1_6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TLora_V2_1_1_6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TLora_V2_1_1_6_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TLora_V2_1_1_6_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -4389,83 +5226,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Mesh pocket", - "type": "nrf52", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "Mesh_pocket_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Mesh_pocket_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "Mesh_pocket_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Mesh_pocket_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "Mesh_pocket_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Mesh_pocket_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -4877,7 +5637,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Nano G2 Ultra", + "name": "Muzi Works R1 Neo", "type": "nrf52", "firmware": [ { @@ -4889,14 +5649,14 @@ "files": [ { "type": "flash", - "name": "Nano_G2_Ultra_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "R1Neo_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Nano_G2_Ultra_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "R1Neo_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -4912,14 +5672,14 @@ "files": [ { "type": "flash", - "name": "Nano_G2_Ultra_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "R1Neo_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Nano_G2_Ultra_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "R1Neo_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -4935,14 +5695,60 @@ "files": [ { "type": "flash", - "name": "Nano_G2_Ultra_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "R1Neo_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Nano_G2_Ultra_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "R1Neo_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "R1Neo_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "R1Neo_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "R1Neo_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "R1Neo_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -5200,7 +6006,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "ProMicro", + "name": "ProMicro nrf52 (faketec)", "type": "nrf52", "firmware": [ { @@ -5344,129 +6150,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "R1Neo", - "type": "nrf52", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "R1Neo_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "R1Neo_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "R1Neo_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "R1Neo_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "R1Neo_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "R1Neo_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "sensor", - "title": "Sensor", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "R1Neo_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "R1Neo_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "R1Neo_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "R1Neo_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -5593,7 +6276,255 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "RAK 3112", + "name": "RAK 3x72", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "RAK_3x72_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "RAK_3x72_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "RAK_3x72_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "RAK_3x72_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "RAK_3x72_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "RAK_3x72_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "RAK WisBlock / WisMesh (RAK 4631)", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "subTitle": "Serial 1", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_repeater_bridge_rs232_serial1-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial1-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_repeater_bridge_rs232_serial1-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial1-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "subTitle": "Serial 2", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_repeater_bridge_rs232_serial2-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial2-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_repeater_bridge_rs232_serial2-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial2-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "RAK WisBlock 3112", "type": "esp32", "firmware": [ { @@ -5785,7 +6716,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "RAK 3401", + "name": "RAK WisMesh 1W Booster (3401 + 13302)", "type": "nrf52", "firmware": [ { @@ -5905,254 +6836,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "RAK 3x72", - "type": "noflash", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "RAK_3x72_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "RAK_3x72_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "RAK_3x72_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "RAK_3x72_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "sensor", - "title": "Sensor", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "RAK_3x72_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "RAK_3x72_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "RAK 4631", - "type": "nrf52", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "RAK_4631_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "RAK_4631_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "RAK_4631_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "RAK_4631_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeaterBridgeRs232", - "title": "Repeater Bridge RS232", - "subTitle": "Serial 1", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "RAK_4631_repeater_bridge_rs232_serial1-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial1-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "RAK_4631_repeater_bridge_rs232_serial1-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial1-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeaterBridgeRs232", - "title": "Repeater Bridge RS232", - "subTitle": "Serial 2", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "RAK_4631_repeater_bridge_rs232_serial2-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial2-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "RAK_4631_repeater_bridge_rs232_serial2-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial2-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "RAK_4631_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "RAK_4631_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "sensor", - "title": "Sensor", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "RAK_4631_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "RAK_4631_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "RAK_4631_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "RAK_4631_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -6256,7 +6939,130 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "SenseCap Solar", + "name": "RPI Pico 2040 + WaveShare SX1262", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Seeed Studio SenseCAP Solar", "type": "nrf52", "firmware": [ { @@ -6330,6 +7136,589 @@ } ] }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Seeed Studio SenseCAP T1000-E", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "t1000e_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "t1000e_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "t1000e_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "t1000e_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "t1000e_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "t1000e_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Seeed Studio Wio Tracker L1 Pro", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "WioTrackerL1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "WioTrackerL1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "WioTrackerL1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "WioTrackerL1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "WioTrackerL1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "WioTrackerL1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Seeed Studio Xiao C3", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Seeed Studio Xiao nRF52 WIO", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Xiao_nrf52_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Xiao_nrf52_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Xiao_nrf52_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Xiao_nrf52_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Xiao_nrf52_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Xiao_nrf52_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Seeed Studio Xiao S3 WIO", + "type": "esp32", + "firmware": [ + { + "role": "companionSerial", + "title": "Companion Serial", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -6364,7 +7753,269 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Station G2", + "name": "Tenstar C3 sx1262", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Tenstar_C3_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tenstar_C3_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Tenstar C3 sx1268", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Tenstar_C3_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tenstar_C3_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Tenstar_C3_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tenstar_C3_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Tiny Relay", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Tiny_Relay_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "Tiny_Relay_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Tiny_Relay_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "Tiny_Relay_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Tiny_Relay_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "Tiny_Relay_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "UnitEng Nano G2 Ultra", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Nano_G2_Ultra_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Nano_G2_Ultra_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Nano_G2_Ultra_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Nano_G2_Ultra_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Nano_G2_Ultra_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Nano_G2_Ultra_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "UnitEng Station G2", "type": "esp32", "firmware": [ { @@ -6535,7 +8186,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Station G3 ESP32", + "name": "UnitEng/BQ Voyage Station G3", "type": "esp32", "firmware": [ { @@ -6656,1214 +8307,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "T Beam S3 Supreme SX1262", - "type": "esp32", - "firmware": [ - { - "role": "companionWifi", - "title": "Companion WiFi", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "T_Beam_S3_Supreme_SX1262_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "T_Beam_S3_Supreme_SX1262_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "T_Beam_S3_Supreme_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "T_Beam_S3_Supreme_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "T_Beam_S3_Supreme_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "T_Beam_S3_Supreme_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "t1000e", - "type": "nrf52", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "t1000e_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "t1000e_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "t1000e_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "t1000e_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "t1000e_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "t1000e_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Tbeam SX1262", - "type": "esp32", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Tbeam_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tbeam_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Tbeam_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tbeam_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Tbeam_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tbeam_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Tbeam SX1276", - "type": "esp32", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Tbeam_SX1276_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tbeam_SX1276_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Tbeam_SX1276_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tbeam_SX1276_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Tbeam_SX1276_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tbeam_SX1276_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Tenstar C3 sx1262", - "type": "esp32", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Tenstar_C3_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tenstar_C3_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Tenstar C3 sx1268", - "type": "esp32", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Tenstar_C3_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tenstar_C3_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Tenstar_C3_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tenstar_C3_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ThinkNode M1", - "type": "nrf52", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ThinkNode M2", - "type": "esp32", - "firmware": [ - { - "role": "companionSerial", - "title": "Companion Serial", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M2_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M2_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M2_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M2_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ThinkNode M3", - "type": "nrf52", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ThinkNode M5", - "type": "esp32", - "firmware": [ - { - "role": "companionSerial", - "title": "Companion Serial", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M5_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M5_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M5_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M5_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M5_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M5_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M5_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M5_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M5_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M5_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ThinkNode M6", - "type": "nrf52", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Tiny Relay", - "type": "noflash", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "Tiny_Relay_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "Tiny_Relay_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "Tiny_Relay_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "Tiny_Relay_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "sensor", - "title": "Sensor", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "Tiny_Relay_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "Tiny_Relay_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "waveshare rp2040 lora", - "type": "noflash", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "waveshare_rp2040_lora_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "waveshare_rp2040_lora_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "waveshare_rp2040_lora_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "waveshare_rp2040_lora_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeaterBridgeRs232", - "title": "Repeater Bridge RS232", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "waveshare_rp2040_lora_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "waveshare_rp2040_lora_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "waveshare_rp2040_lora_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "waveshare_rp2040_lora_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "waveshare_rp2040_lora_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "waveshare_rp2040_lora_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -7988,15 +8431,7 @@ ] } } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "wio-e5-", - "type": "noflash", - "firmware": [ + }, { "role": "repeaterBridgeRs232", "title": "Repeater Bridge RS232", @@ -8099,183 +8534,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "WioTrackerL1", - "type": "nrf52", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "WioTrackerL1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "WioTrackerL1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "WioTrackerL1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "WioTrackerL1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "WioTrackerL1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "WioTrackerL1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Xiao C3", - "type": "esp32", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_C3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_C3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_C3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_C3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_C3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_C3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_C3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_C3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -8307,83 +8565,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Xiao nrf52", - "type": "nrf52", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "Xiao_nrf52_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Xiao_nrf52_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "Xiao_nrf52_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Xiao_nrf52_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "Xiao_nrf52_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Xiao_nrf52_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -8483,321 +8664,6 @@ } } ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Xiao S3", - "type": "esp32", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "sensor", - "title": "Sensor", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Xiao S3 WIO", - "type": "esp32", - "firmware": [ - { - "role": "companionSerial", - "title": "Companion Serial", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "sensor", - "title": "Sensor", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] } ] } diff --git a/mesh-america/keymind-cascade-v1.16.0-provider.json b/mesh-america/keymind-cascade-v1.16.0-provider.json index 359ec3f5..896ffc4e 100644 --- a/mesh-america/keymind-cascade-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-v1.16.0-provider.json @@ -152,7 +152,800 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "GAT562 30S Mesh Kit", + "name": "Elecrow ThinkNode M1", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Elecrow ThinkNode M2", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionSerial", + "title": "Companion Serial", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Elecrow ThinkNode M3", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Elecrow ThinkNode M5", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionSerial", + "title": "Companion Serial", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Elecrow ThinkNode M6", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ThinkNode_M6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ThinkNode_M6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "GAT562 Mesh EVB Pro", + "type": "nrf52", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "GAT562 Mesh Watch13", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "GAT-IoT GAT562 30s", "type": "nrf52", "firmware": [ { @@ -252,61 +1045,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "GAT562 Mesh EVB Pro", - "type": "nrf52", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "GAT562 Mesh Tracker Pro", + "name": "GAT-IoT GAT562 Tracker", "type": "nrf52", "firmware": [ { @@ -403,37 +1142,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "GAT562 Mesh Watch13", - "type": "nrf52", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -792,7 +1500,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Heltec E213", + "name": "Heltec Heltec Wireless Paper", "type": "esp32", "firmware": [ { @@ -804,14 +1512,38 @@ "files": [ { "type": "flash-wipe", - "name": "Heltec_E213_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "name": "Heltec_Wireless_Paper_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", - "name": "Heltec_E213_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "name": "Heltec_Wireless_Paper_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_Wireless_Paper_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_Wireless_Paper_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" } ] @@ -827,14 +1559,14 @@ "files": [ { "type": "flash-wipe", - "name": "Heltec_E213_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "name": "Heltec_Wireless_Paper_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", - "name": "Heltec_E213_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "name": "Heltec_Wireless_Paper_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" } ] @@ -850,14 +1582,14 @@ "files": [ { "type": "flash-wipe", - "name": "Heltec_E213_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "name": "Heltec_Wireless_Paper_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", - "name": "Heltec_E213_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "name": "Heltec_Wireless_Paper_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" } ] @@ -873,14 +1605,14 @@ "files": [ { "type": "flash-wipe", - "name": "Heltec_E213_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "name": "Heltec_Wireless_Paper_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", - "name": "Heltec_E213_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "name": "Heltec_Wireless_Paper_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" } ] @@ -896,14 +1628,14 @@ "files": [ { "type": "flash-wipe", - "name": "Heltec_E213_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "name": "Heltec_Wireless_Paper_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", - "name": "Heltec_E213_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "name": "Heltec_Wireless_Paper_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" } ] @@ -915,230 +1647,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Heltec E290", - "type": "esp32", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_E290_companion_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_companion_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_E290_companion_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_companion_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_E290_companion_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_companion_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_E290_companion_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_companion_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_E290_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_E290_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_E290_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_E290_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_E290_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_E290_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Heltec mesh solar", - "type": "nrf52", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "Heltec_mesh_solar_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Heltec_mesh_solar_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "Heltec_mesh_solar_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Heltec_mesh_solar_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "Heltec_mesh_solar_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Heltec_mesh_solar_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "Heltec_mesh_solar_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Heltec_mesh_solar_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Heltec t096", + "name": "Heltec Mesh Node T096", "type": "nrf52", "firmware": [ { @@ -1332,7 +1841,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Heltec t1", + "name": "Heltec Mesh Node T1", "type": "nrf52", "firmware": [ { @@ -1432,7 +1941,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Heltec t114", + "name": "Heltec MeshPocket", "type": "nrf52", "firmware": [ { @@ -1444,14 +1953,14 @@ "files": [ { "type": "flash", - "name": "Heltec_t114_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "Mesh_pocket_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Heltec_t114_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "Mesh_pocket_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -1467,14 +1976,14 @@ "files": [ { "type": "flash", - "name": "Heltec_t114_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "Mesh_pocket_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Heltec_t114_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "Mesh_pocket_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -1490,37 +1999,14 @@ "files": [ { "type": "flash", - "name": "Heltec_t114_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "Mesh_pocket_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Heltec_t114_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeaterBridgeRs232", - "title": "Repeater Bridge RS232", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "Heltec_t114_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Heltec_t114_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "Mesh_pocket_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -1536,14 +2022,14 @@ "files": [ { "type": "flash", - "name": "Heltec_t114_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "Mesh_pocket_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Heltec_t114_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "Mesh_pocket_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -1555,7 +2041,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Heltec t114 without display", + "name": "Heltec MeshSolar / MeshTower", "type": "nrf52", "firmware": [ { @@ -1567,14 +2053,14 @@ "files": [ { "type": "flash", - "name": "Heltec_t114_without_display_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "Heltec_mesh_solar_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Heltec_t114_without_display_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "Heltec_mesh_solar_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -1590,14 +2076,14 @@ "files": [ { "type": "flash", - "name": "Heltec_t114_without_display_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "Heltec_mesh_solar_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Heltec_t114_without_display_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "Heltec_mesh_solar_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -1613,37 +2099,14 @@ "files": [ { "type": "flash", - "name": "Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "Heltec_mesh_solar_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeaterBridgeRs232", - "title": "Repeater Bridge RS232", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "Heltec_t114_without_display_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Heltec_t114_without_display_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "Heltec_mesh_solar_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -1659,14 +2122,14 @@ "files": [ { "type": "flash", - "name": "Heltec_t114_without_display_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "Heltec_mesh_solar_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Heltec_t114_without_display_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "Heltec_mesh_solar_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_mesh_solar_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -1678,12 +2141,36 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Heltec T190", + "name": "Heltec T114", "type": "esp32", "firmware": [ { "role": "companionBle", "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Heltec_t114_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Heltec_t114_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Download" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "T190", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -1704,9 +2191,57 @@ } } }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Without display", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Heltec_t114_without_display_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Heltec_t114_without_display_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Download" + } + ] + } + } + }, { "role": "companionUsb", "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Heltec_t114_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Heltec_t114_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "T190", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -1727,9 +2262,57 @@ } } }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "Without display", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Heltec_t114_without_display_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Heltec_t114_without_display_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Download" + } + ] + } + } + }, { "role": "repeater", "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Heltec_t114_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Heltec_t114_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "T190", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -1750,9 +2333,34 @@ } } }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "Without display", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Download" + } + ] + } + } + }, { "role": "repeaterBridgeEspnow", "title": "Repeater Bridge ESP-NOW", + "subTitle": "T190", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -1773,9 +2381,80 @@ } } }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Heltec_t114_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Heltec_t114_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "subTitle": "Without display", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Heltec_t114_without_display_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Heltec_t114_without_display_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Download" + } + ] + } + } + }, { "role": "roomServer", "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Heltec_t114_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Heltec_t114_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "subTitle": "T190", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -1795,218 +2474,26 @@ ] } } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "heltec tracker v2", - "type": "esp32", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_tracker_v2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_tracker_v2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionBle", - "title": "Companion BLE", - "subTitle": "Power saving", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_tracker_v2_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_tracker_v2_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_tracker_v2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_tracker_v2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_tracker_v2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_tracker_v2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_tracker_v2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_tracker_v2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_tracker_v2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_tracker_v2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } }, { "role": "roomServer", "title": "Room Server", + "subTitle": "Without display", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ { - "type": "flash-wipe", - "name": "heltec_tracker_v2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" + "type": "download", + "name": "Heltec_t114_without_display_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" }, { - "type": "flash-update", - "name": "heltec_tracker_v2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "sensor", - "title": "Sensor", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_tracker_v2_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_tracker_v2_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_tracker_v2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_tracker_v2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" + "type": "download", + "name": "Heltec_t114_without_display_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Download" } ] } @@ -2449,7 +2936,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "heltec v4", + "name": "Heltec v4", "type": "esp32", "firmware": [ { @@ -2523,6 +3010,54 @@ } } }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "TFT", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "v4.3, Power saving, FEM off", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_3_companion_radio_ble_ps_femoff-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_3_companion_radio_ble_ps_femoff-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_3_companion_radio_ble_ps_femoff-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_3_companion_radio_ble_ps_femoff-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "companionUsb", "title": "Companion USB", @@ -2546,6 +3081,30 @@ } } }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "TFT", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "companionWifi", "title": "Companion WiFi", @@ -2569,6 +3128,30 @@ } } }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "subTitle": "TFT", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "repeater", "title": "Repeater", @@ -2592,6 +3175,30 @@ } } }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "TFT", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "repeaterBridgeEspnow", "title": "Repeater Bridge ESP-NOW", @@ -2615,6 +3222,30 @@ } } }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "subTitle": "TFT", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "roomServer", "title": "Room Server", @@ -2638,6 +3269,30 @@ } } }, + { + "role": "roomServer", + "title": "Room Server", + "subTitle": "TFT", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "sensor", "title": "Sensor", @@ -2661,273 +3316,10 @@ } } }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "heltec v4 3", - "type": "esp32", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "subTitle": "Power saving, FEM off", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_3_companion_radio_ble_ps_femoff-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_3_companion_radio_ble_ps_femoff-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_3_companion_radio_ble_ps_femoff-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_3_companion_radio_ble_ps_femoff-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "heltec v4 expansionkit", - "type": "esp32", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_expansionkit_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_expansionkit_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_expansionkit_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_expansionkit_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "heltec v4 expansionkit tft", - "type": "esp32", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "subTitle": "Power saving", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_expansionkit_tft_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_expansionkit_tft_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_expansionkit_tft_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_expansionkit_tft_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "heltec v4 tft", - "type": "esp32", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_tft_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_tft_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_tft_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_tft_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_tft_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_tft_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_tft_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_tft_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_tft_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_tft_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, { "role": "sensor", "title": "Sensor", + "subTitle": "TFT", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -2951,6 +3343,30 @@ { "role": "terminalChat", "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "subTitle": "TFT", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -2976,7 +3392,63 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Heltec Wireless Paper", + "name": "Heltec v4 + Expansion Kit (Touch)", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Expansion Kit TFT, Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_expansionkit_tft_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_expansionkit_tft_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_expansionkit_tft_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_expansionkit_tft_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "Expansion Kit", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_expansionkit_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_expansionkit_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_expansionkit_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_expansionkit_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec Vision Master E213", "type": "esp32", "firmware": [ { @@ -2988,38 +3460,14 @@ "files": [ { "type": "flash-wipe", - "name": "Heltec_Wireless_Paper_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "name": "Heltec_E213_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", - "name": "Heltec_Wireless_Paper_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionBle", - "title": "Companion BLE", - "subTitle": "Power saving", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_Wireless_Paper_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_Wireless_Paper_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "name": "Heltec_E213_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" } ] @@ -3035,14 +3483,14 @@ "files": [ { "type": "flash-wipe", - "name": "Heltec_Wireless_Paper_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "name": "Heltec_E213_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", - "name": "Heltec_Wireless_Paper_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "name": "Heltec_E213_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" } ] @@ -3058,14 +3506,14 @@ "files": [ { "type": "flash-wipe", - "name": "Heltec_Wireless_Paper_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "name": "Heltec_E213_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", - "name": "Heltec_Wireless_Paper_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "name": "Heltec_E213_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" } ] @@ -3081,14 +3529,14 @@ "files": [ { "type": "flash-wipe", - "name": "Heltec_Wireless_Paper_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "name": "Heltec_E213_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", - "name": "Heltec_Wireless_Paper_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "name": "Heltec_E213_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" } ] @@ -3104,14 +3552,137 @@ "files": [ { "type": "flash-wipe", - "name": "Heltec_Wireless_Paper_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "name": "Heltec_E213_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", - "name": "Heltec_Wireless_Paper_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_Wireless_Paper_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "name": "Heltec_E213_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E213_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec Vision Master E290", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E290_companion_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_companion_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E290_companion_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_companion_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E290_companion_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_companion_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E290_companion_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_companion_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E290_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E290_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E290_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E290_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_E290_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_E290_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_E290_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" } ] @@ -3267,6 +3838,222 @@ } ] }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Heltec Wireless Tracker v2", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_tracker_v2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_tracker_v2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_tracker_v2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -3648,12 +4435,13 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "ikoka nano nrf 22dbm", + "name": "Ikoka Nano", "type": "nrf52", "firmware": [ { "role": "companionBle", "title": "Companion BLE", + "subTitle": "22 dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -3674,86 +4462,10 @@ } } }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ikoka nano nrf 30dbm", - "type": "nrf52", - "firmware": [ { "role": "companionBle", "title": "Companion BLE", + "subTitle": "30 dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -3774,86 +4486,10 @@ } } }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ikoka nano nrf 33dbm", - "type": "nrf52", - "firmware": [ { "role": "companionBle", "title": "Companion BLE", + "subTitle": "33 dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -3877,6 +4513,55 @@ { "role": "companionUsb", "title": "Companion USB", + "subTitle": "22 dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "30 dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "33 dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -3900,6 +4585,55 @@ { "role": "repeater", "title": "Repeater", + "subTitle": "22 dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "30 dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "33 dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -3923,6 +4657,55 @@ { "role": "roomServer", "title": "Room Server", + "subTitle": "22 dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "subTitle": "30 dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "subTitle": "33 dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -3948,12 +4731,13 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "ikoka stick nrf 22dbm", + "name": "Ikoka Stick", "type": "nrf52", "firmware": [ { "role": "companionBle", "title": "Companion BLE", + "subTitle": "22 dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -3974,86 +4758,10 @@ } } }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ikoka stick nrf 30dbm", - "type": "nrf52", - "firmware": [ { "role": "companionBle", "title": "Companion BLE", + "subTitle": "30 dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4074,86 +4782,10 @@ } } }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ikoka stick nrf 33dbm", - "type": "nrf52", - "firmware": [ { "role": "companionBle", "title": "Companion BLE", + "subTitle": "33 dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4177,6 +4809,55 @@ { "role": "companionUsb", "title": "Companion USB", + "subTitle": "22 dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "30 dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "33 dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4200,6 +4881,55 @@ { "role": "repeater", "title": "Repeater", + "subTitle": "22 dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "30 dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "33 dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4223,6 +4953,55 @@ { "role": "roomServer", "title": "Room Server", + "subTitle": "22 dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "subTitle": "30 dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "subTitle": "33 dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4248,7 +5027,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "KeepteenLT1", + "name": "Keepteen LT1", "type": "nrf52", "firmware": [ { @@ -4345,6 +5124,222 @@ } ] }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo LoRa32 V2.1_1.6", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TLora_V2_1_1_6_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TLora_V2_1_1_6_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -4402,7 +5397,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "LilyGo T3S3 sx1262", + "name": "LilyGo T3 S3 (SX126x)", "type": "esp32", "firmware": [ { @@ -4572,7 +5567,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "LilyGo T3S3 sx1276", + "name": "LilyGo T3 S3 (SX127x)", "type": "esp32", "firmware": [ { @@ -4718,7 +5713,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "LilyGo TBeam 1W", + "name": "LilyGo T-Beam (SX1262)", "type": "esp32", "firmware": [ { @@ -4734,11 +5729,23 @@ "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, + { + "type": "flash-wipe", + "name": "Tbeam_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, { "type": "flash-update", "name": "LilyGo_TBeam_1W_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" } ] } @@ -4758,11 +5765,23 @@ "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, + { + "type": "flash-wipe", + "name": "Tbeam_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, { "type": "flash-update", "name": "LilyGo_TBeam_1W_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" } ] } @@ -4827,11 +5846,23 @@ "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, + { + "type": "flash-wipe", + "name": "Tbeam_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, { "type": "flash-update", "name": "LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" } ] } @@ -4850,11 +5881,23 @@ "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, + { + "type": "flash-wipe", + "name": "Tbeam_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, { "type": "flash-update", "name": "LilyGo_TBeam_1W_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" } ] } @@ -4873,11 +5916,23 @@ "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, + { + "type": "flash-wipe", + "name": "Tbeam_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, { "type": "flash-update", "name": "LilyGo_TBeam_1W_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" } ] } @@ -4888,7 +5943,278 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "LilyGo TDeck", + "name": "LilyGo T-Beam 1.2 (SX1276)", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1276_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1276_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1276_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1276_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1276_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1276_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1276_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1276_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tbeam_SX1276_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tbeam_SX1276_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T-Beam Supreme (SX1262)", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "T_Beam_S3_Supreme_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "T_Beam_S3_Supreme_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T-Deck", "type": "esp32", "firmware": [ { @@ -5165,7 +6491,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "LilyGo T-Echo-Lite", + "name": "LilyGo T-Echo Lite", "type": "nrf52", "firmware": [ { @@ -5470,222 +6796,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "LilyGo TLora V2 1 1 6", - "type": "esp32", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TLora_V2_1_1_6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TLora_V2_1_1_6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionBle", - "title": "Companion BLE", - "subTitle": "Power saving", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TLora_V2_1_1_6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TLora_V2_1_1_6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TLora_V2_1_1_6_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TLora_V2_1_1_6_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TLora_V2_1_1_6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TLora_V2_1_1_6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeRs232", - "title": "Repeater Bridge RS232", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TLora_V2_1_1_6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TLora_V2_1_1_6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TLora_V2_1_1_6_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TLora_V2_1_1_6_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TLora_V2_1_1_6_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -5786,106 +6896,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Mesh pocket", - "type": "nrf52", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "Mesh_pocket_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Mesh_pocket_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "Mesh_pocket_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Mesh_pocket_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "Mesh_pocket_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Mesh_pocket_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "Mesh_pocket_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Mesh_pocket_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Mesh_pocket_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -6412,7 +7422,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Nano G2 Ultra", + "name": "Muzi Works R1 Neo", "type": "nrf52", "firmware": [ { @@ -6424,14 +7434,14 @@ "files": [ { "type": "flash", - "name": "Nano_G2_Ultra_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "R1Neo_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Nano_G2_Ultra_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "R1Neo_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -6447,14 +7457,14 @@ "files": [ { "type": "flash", - "name": "Nano_G2_Ultra_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "R1Neo_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Nano_G2_Ultra_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "R1Neo_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -6470,14 +7480,14 @@ "files": [ { "type": "flash", - "name": "Nano_G2_Ultra_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "R1Neo_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Nano_G2_Ultra_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "R1Neo_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -6493,14 +7503,60 @@ "files": [ { "type": "flash", - "name": "Nano_G2_Ultra_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "name": "R1Neo_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", "title": "Serial DFU package" }, { "type": "download", - "name": "Nano_G2_Ultra_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "name": "R1Neo_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "R1Neo_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "R1Neo_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "R1Neo_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "R1Neo_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" } ] @@ -6781,7 +7837,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "ProMicro", + "name": "ProMicro nrf52 (faketec)", "type": "nrf52", "firmware": [ { @@ -6948,152 +8004,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "R1Neo", - "type": "nrf52", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "R1Neo_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "R1Neo_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "R1Neo_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "R1Neo_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "R1Neo_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "R1Neo_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "R1Neo_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "R1Neo_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "sensor", - "title": "Sensor", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "R1Neo_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "R1Neo_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "R1Neo_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "R1Neo_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/R1Neo_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -7220,7 +8130,278 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "RAK 3112", + "name": "RAK 3x72", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "RAK_3x72_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "RAK_3x72_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "RAK_3x72_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "RAK_3x72_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "RAK_3x72_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "RAK_3x72_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "RAK WisBlock / WisMesh (RAK 4631)", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "subTitle": "Serial 1", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_repeater_bridge_rs232_serial1-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial1-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_repeater_bridge_rs232_serial1-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial1-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "subTitle": "Serial 2", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_repeater_bridge_rs232_serial2-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial2-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_repeater_bridge_rs232_serial2-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial2-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "RAK_4631_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "RAK_4631_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "RAK WisBlock 3112", "type": "esp32", "firmware": [ { @@ -7435,7 +8616,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "RAK 3401", + "name": "RAK WisMesh 1W Booster (3401 + 13302)", "type": "nrf52", "firmware": [ { @@ -7578,277 +8759,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "RAK 3x72", - "type": "noflash", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "RAK_3x72_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "RAK_3x72_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "RAK_3x72_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "RAK_3x72_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "sensor", - "title": "Sensor", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "RAK_3x72_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "RAK_3x72_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_3x72_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "RAK 4631", - "type": "nrf52", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "RAK_4631_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "RAK_4631_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "RAK_4631_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "RAK_4631_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "RAK_4631_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "RAK_4631_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeaterBridgeRs232", - "title": "Repeater Bridge RS232", - "subTitle": "Serial 1", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "RAK_4631_repeater_bridge_rs232_serial1-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial1-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "RAK_4631_repeater_bridge_rs232_serial1-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial1-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeaterBridgeRs232", - "title": "Repeater Bridge RS232", - "subTitle": "Serial 2", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "RAK_4631_repeater_bridge_rs232_serial2-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial2-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "RAK_4631_repeater_bridge_rs232_serial2-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_repeater_bridge_rs232_serial2-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "RAK_4631_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "RAK_4631_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "sensor", - "title": "Sensor", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "RAK_4631_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "RAK_4631_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "RAK_4631_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "RAK_4631_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/RAK_4631_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -7975,7 +8885,130 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "SenseCap Solar", + "name": "RPI Pico 2040 + WaveShare SX1262", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "waveshare_rp2040_lora_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "waveshare_rp2040_lora_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Seeed Studio SenseCAP Solar", "type": "nrf52", "firmware": [ { @@ -8072,6 +9105,807 @@ } ] }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Seeed Studio SenseCAP T1000-E", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "t1000e_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "t1000e_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "t1000e_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "t1000e_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "t1000e_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "t1000e_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "t1000e_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "t1000e_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Seeed Studio Wio Tracker L1 EINK", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "WioTrackerL1Eink_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1Eink_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "WioTrackerL1Eink_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1Eink_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Seeed Studio Wio Tracker L1 Pro", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "WioTrackerL1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "WioTrackerL1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "WioTrackerL1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "WioTrackerL1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "WioTrackerL1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "WioTrackerL1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "WioTrackerL1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "WioTrackerL1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Seeed Studio Xiao C3", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Seeed Studio Xiao nRF52 WIO", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Xiao_nrf52_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Xiao_nrf52_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Xiao_nrf52_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Xiao_nrf52_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Xiao_nrf52_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Xiao_nrf52_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Xiao_nrf52_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Xiao_nrf52_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Seeed Studio Xiao S3 WIO", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionSerial", + "title": "Companion Serial", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -8106,7 +9940,292 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Station G2", + "name": "Tenstar C3 sx1262", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tenstar_C3_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tenstar_C3_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Tenstar C3 sx1268", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tenstar_C3_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tenstar_C3_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tenstar_C3_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tenstar_C3_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "Tiny Relay", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Tiny_Relay_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "Tiny_Relay_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Tiny_Relay_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "Tiny_Relay_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Tiny_Relay_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "Tiny_Relay_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "UnitEng Nano G2 Ultra", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Nano_G2_Ultra_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Nano_G2_Ultra_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Nano_G2_Ultra_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Nano_G2_Ultra_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Nano_G2_Ultra_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Nano_G2_Ultra_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Nano_G2_Ultra_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Nano_G2_Ultra_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Nano_G2_Ultra_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "UnitEng Station G2", "type": "esp32", "firmware": [ { @@ -8300,7 +10419,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "Station G3 ESP32", + "name": "UnitEng/BQ Voyage Station G3", "type": "esp32", "firmware": [ { @@ -8444,1517 +10563,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "T Beam S3 Supreme SX1262", - "type": "esp32", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "T_Beam_S3_Supreme_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "T_Beam_S3_Supreme_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionBle", - "title": "Companion BLE", - "subTitle": "Power saving", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "T_Beam_S3_Supreme_SX1262_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "T_Beam_S3_Supreme_SX1262_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "T_Beam_S3_Supreme_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "T_Beam_S3_Supreme_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "T_Beam_S3_Supreme_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "T_Beam_S3_Supreme_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/T_Beam_S3_Supreme_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "t1000e", - "type": "nrf52", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "t1000e_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "t1000e_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "t1000e_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "t1000e_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "t1000e_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "t1000e_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "t1000e_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "t1000e_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/t1000e_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Tbeam SX1262", - "type": "esp32", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Tbeam_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tbeam_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionBle", - "title": "Companion BLE", - "subTitle": "Power saving", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Tbeam_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tbeam_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Tbeam_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tbeam_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Tbeam_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tbeam_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Tbeam_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tbeam_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Tbeam SX1276", - "type": "esp32", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Tbeam_SX1276_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tbeam_SX1276_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionBle", - "title": "Companion BLE", - "subTitle": "Power saving", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Tbeam_SX1276_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tbeam_SX1276_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Tbeam_SX1276_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tbeam_SX1276_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Tbeam_SX1276_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tbeam_SX1276_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Tbeam_SX1276_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tbeam_SX1276_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1276_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Tenstar C3 sx1262", - "type": "esp32", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Tenstar_C3_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tenstar_C3_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Tenstar C3 sx1268", - "type": "esp32", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Tenstar_C3_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tenstar_C3_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Tenstar_C3_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tenstar_C3_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1268_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ThinkNode M1", - "type": "nrf52", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ThinkNode M2", - "type": "esp32", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionSerial", - "title": "Companion Serial", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M2_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M2_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M2_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M2_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ThinkNode M3", - "type": "nrf52", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ThinkNode M5", - "type": "esp32", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M5_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M5_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionBle", - "title": "Companion BLE", - "subTitle": "Power saving", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M5_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M5_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionSerial", - "title": "Companion Serial", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M5_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M5_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M5_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M5_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M5_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M5_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_Repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M5_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M5_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M5_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M5_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ThinkNode M6", - "type": "nrf52", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ThinkNode_M6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ThinkNode_M6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M6_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Tiny Relay", - "type": "noflash", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "Tiny_Relay_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "Tiny_Relay_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "Tiny_Relay_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "Tiny_Relay_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "sensor", - "title": "Sensor", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "Tiny_Relay_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "Tiny_Relay_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tiny_Relay_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "waveshare rp2040 lora", - "type": "noflash", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "waveshare_rp2040_lora_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "waveshare_rp2040_lora_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "waveshare_rp2040_lora_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "waveshare_rp2040_lora_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeaterBridgeRs232", - "title": "Repeater Bridge RS232", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "waveshare_rp2040_lora_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "waveshare_rp2040_lora_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "waveshare_rp2040_lora_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "waveshare_rp2040_lora_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "waveshare_rp2040_lora_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "waveshare_rp2040_lora_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/waveshare_rp2040_lora_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -10119,15 +10727,7 @@ ] } } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "wio-e5-", - "type": "noflash", - "firmware": [ + }, { "role": "repeaterBridgeRs232", "title": "Repeater Bridge RS232", @@ -10230,284 +10830,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "WioTrackerL1", - "type": "nrf52", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "WioTrackerL1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "WioTrackerL1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "WioTrackerL1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "WioTrackerL1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "WioTrackerL1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "WioTrackerL1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "WioTrackerL1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "WioTrackerL1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "WioTrackerL1Eink", - "type": "nrf52", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "WioTrackerL1Eink_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1Eink_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "WioTrackerL1Eink_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/WioTrackerL1Eink_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Xiao C3", - "type": "esp32", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_C3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_C3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionBle", - "title": "Companion BLE", - "subTitle": "Power saving", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_C3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_C3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_C3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_C3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_C3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_C3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_C3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_C3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_C3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_C3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -10562,106 +10884,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Xiao nrf52", - "type": "nrf52", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "Xiao_nrf52_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Xiao_nrf52_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "Xiao_nrf52_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Xiao_nrf52_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "Xiao_nrf52_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Xiao_nrf52_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "Xiao_nrf52_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Xiao_nrf52_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_nrf52_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -10761,415 +10983,6 @@ } } ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Xiao S3", - "type": "esp32", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionBle", - "title": "Companion BLE", - "subTitle": "Power saving", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "sensor", - "title": "Sensor", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Xiao S3 WIO", - "type": "esp32", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionBle", - "title": "Companion BLE", - "subTitle": "Power saving", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionSerial", - "title": "Companion Serial", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "sensor", - "title": "Sensor", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] } ] } From da7b90a1aca9277d846f3f04c8845f77a88c2004 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 30 Jun 2026 14:21:57 -0700 Subject: [PATCH 184/214] Fold additional Mesh America device names --- .../generate-mesh-america-catalogs.ps1 | 4 + ...mind-cascade-logging-v1.16.0-provider.json | 119 +++++----- .../keymind-cascade-v1.16.0-provider.json | 212 +++++++++--------- 3 files changed, 163 insertions(+), 172 deletions(-) diff --git a/mesh-america/generate-mesh-america-catalogs.ps1 b/mesh-america/generate-mesh-america-catalogs.ps1 index 9a7ad540..d076b2f2 100644 --- a/mesh-america/generate-mesh-america-catalogs.ps1 +++ b/mesh-america/generate-mesh-america-catalogs.ps1 @@ -90,9 +90,12 @@ $DeviceNameOverrides = @{ 'KeepteenLT1' = 'Keepteen LT1' 'LilyGo_T3S3_sx1262' = 'LilyGo T3 S3 (SX126x)' 'LilyGo_T3S3_sx1276' = 'LilyGo T3 S3 (SX127x)' + 'LilyGo_T_Impulse_Plus' = 'LilyGo T-Watch S3 Plus' 'LilyGo_TBeam_1W' = 'LilyGo T-Beam (SX1262)' 'LilyGo_TDeck' = 'LilyGo T-Deck' 'LilyGo_T-Echo-Lite' = 'LilyGo T-Echo Lite' + 'LilyGo_T-Echo-Lite_non_shell' = 'LilyGo T-Echo Lite' + 'LilyGo_Tlora_C6' = 'LilyGo T-Lora Pager' 'LilyGo_TLora_V2_1_1_6' = 'LilyGo LoRa32 V2.1_1.6' 'Mesh_pocket' = 'Heltec MeshPocket' 'Nano_G2_Ultra' = 'UnitEng Nano G2 Ultra' @@ -129,6 +132,7 @@ $DeviceSubTitleOverrides = @{ 'heltec_v4_tft' = 'TFT' 'heltec_v4_expansionkit' = 'Expansion Kit' 'heltec_v4_expansionkit_tft' = 'Expansion Kit TFT' + 'LilyGo_T-Echo-Lite_non_shell' = 'Non shell' 'ikoka_nano_nrf_22dbm' = '22 dBm' 'ikoka_nano_nrf_30dbm' = '30 dBm' 'ikoka_nano_nrf_33dbm' = '33 dBm' diff --git a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json index 17db5ec9..91516c64 100644 --- a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json @@ -4112,37 +4112,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "LilyGo T Impulse Plus", - "type": "nrf52", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -4939,6 +4908,30 @@ "name": "LilyGo T-Echo Lite", "type": "nrf52", "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "Non shell", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, { "role": "repeater", "title": "Repeater", @@ -4987,37 +4980,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "LilyGo T-Echo-Lite non shell", - "type": "nrf52", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -5098,7 +5060,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "LilyGo Tlora C6", + "name": "LilyGo T-Lora Pager", "type": "esp32", "firmware": [ { @@ -5149,6 +5111,37 @@ } ] }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T-Watch S3 Plus", + "type": "nrf52", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, { "maker": "keymindCascade", "class": "keymindCascade", diff --git a/mesh-america/keymind-cascade-v1.16.0-provider.json b/mesh-america/keymind-cascade-v1.16.0-provider.json index 896ffc4e..6b3db2d1 100644 --- a/mesh-america/keymind-cascade-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-v1.16.0-provider.json @@ -5340,60 +5340,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "LilyGo T Impulse Plus", - "type": "nrf52", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -6517,6 +6463,54 @@ } } }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Non shell", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo-Lite_non_shell_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_non_shell_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo-Lite_non_shell_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_non_shell_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "Non shell", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, { "role": "repeater", "title": "Repeater", @@ -6565,60 +6559,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "LilyGo T-Echo-Lite non shell", - "type": "nrf52", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "LilyGo_T-Echo-Lite_non_shell_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_non_shell_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "LilyGo_T-Echo-Lite_non_shell_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_non_shell_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T-Echo-Lite_non_shell_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -6722,7 +6662,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "LilyGo Tlora C6", + "name": "LilyGo T-Lora Pager", "type": "esp32", "firmware": [ { @@ -6796,6 +6736,60 @@ } ] }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T-Watch S3 Plus", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, { "maker": "keymindCascade", "class": "keymindCascade", From 53a86491ac09666d3e64332ada3ced793c707504 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 30 Jun 2026 14:25:20 -0700 Subject: [PATCH 185/214] Keep distinct LilyGo hardware entries --- .../generate-mesh-america-catalogs.ps1 | 2 - ...mind-cascade-logging-v1.16.0-provider.json | 64 +++++----- .../keymind-cascade-v1.16.0-provider.json | 110 +++++++++--------- 3 files changed, 87 insertions(+), 89 deletions(-) diff --git a/mesh-america/generate-mesh-america-catalogs.ps1 b/mesh-america/generate-mesh-america-catalogs.ps1 index d076b2f2..a4f65d96 100644 --- a/mesh-america/generate-mesh-america-catalogs.ps1 +++ b/mesh-america/generate-mesh-america-catalogs.ps1 @@ -90,12 +90,10 @@ $DeviceNameOverrides = @{ 'KeepteenLT1' = 'Keepteen LT1' 'LilyGo_T3S3_sx1262' = 'LilyGo T3 S3 (SX126x)' 'LilyGo_T3S3_sx1276' = 'LilyGo T3 S3 (SX127x)' - 'LilyGo_T_Impulse_Plus' = 'LilyGo T-Watch S3 Plus' 'LilyGo_TBeam_1W' = 'LilyGo T-Beam (SX1262)' 'LilyGo_TDeck' = 'LilyGo T-Deck' 'LilyGo_T-Echo-Lite' = 'LilyGo T-Echo Lite' 'LilyGo_T-Echo-Lite_non_shell' = 'LilyGo T-Echo Lite' - 'LilyGo_Tlora_C6' = 'LilyGo T-Lora Pager' 'LilyGo_TLora_V2_1_1_6' = 'LilyGo LoRa32 V2.1_1.6' 'Mesh_pocket' = 'Heltec MeshPocket' 'Nano_G2_Ultra' = 'UnitEng Nano G2 Ultra' diff --git a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json index 91516c64..cce18516 100644 --- a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json @@ -4112,6 +4112,37 @@ } ] }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T Impulse Plus", + "type": "nrf52", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -5060,7 +5091,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "LilyGo T-Lora Pager", + "name": "LilyGo Tlora C6", "type": "esp32", "firmware": [ { @@ -5111,37 +5142,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "LilyGo T-Watch S3 Plus", - "type": "nrf52", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", diff --git a/mesh-america/keymind-cascade-v1.16.0-provider.json b/mesh-america/keymind-cascade-v1.16.0-provider.json index 6b3db2d1..d7dc6d18 100644 --- a/mesh-america/keymind-cascade-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-v1.16.0-provider.json @@ -5340,6 +5340,60 @@ } ] }, + { + "maker": "keymindCascade", + "class": "keymindCascade", + "name": "LilyGo T Impulse Plus", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, { "maker": "keymindCascade", "class": "keymindCascade", @@ -6662,7 +6716,7 @@ { "maker": "keymindCascade", "class": "keymindCascade", - "name": "LilyGo T-Lora Pager", + "name": "LilyGo Tlora C6", "type": "esp32", "firmware": [ { @@ -6736,60 +6790,6 @@ } ] }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "LilyGo T-Watch S3 Plus", - "type": "nrf52", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, { "maker": "keymindCascade", "class": "keymindCascade", From bd3decc864beef9f9567976cae2eaa36c2ed9d57 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 30 Jun 2026 23:34:38 -0700 Subject: [PATCH 186/214] Refine Mesh America catalog metadata --- .../generate-mesh-america-catalogs.ps1 | 149 +- ...mind-cascade-logging-v1.16.0-provider.json | 1818 +++++++------- .../keymind-cascade-v1.16.0-provider.json | 2101 ++++++++--------- 3 files changed, 2082 insertions(+), 1986 deletions(-) diff --git a/mesh-america/generate-mesh-america-catalogs.ps1 b/mesh-america/generate-mesh-america-catalogs.ps1 index a4f65d96..8429dbff 100644 --- a/mesh-america/generate-mesh-america-catalogs.ps1 +++ b/mesh-america/generate-mesh-america-catalogs.ps1 @@ -22,7 +22,6 @@ $Catalogs = @( ) $RoleDefinitions = [ordered]@{ - companionSerial = [ordered]@{ name = 'Companion Serial' } companionWifi = [ordered]@{ name = 'Companion WiFi' } repeaterBridgeEspnow = [ordered]@{ name = 'Repeater Bridge ESP-NOW' } repeaterBridgeRs232 = [ordered]@{ name = 'Repeater Bridge RS232' } @@ -45,7 +44,7 @@ $RolePatterns = @( @{ Suffix = 'companion_radio_usb'; Role = 'companionUsb'; Title = 'Companion USB'; SubTitle = $null }, @{ Suffix = 'companion_usb'; Role = 'companionUsb'; Title = 'Companion USB'; SubTitle = $null }, @{ Suffix = 'comp_radio_usb'; Role = 'companionUsb'; Title = 'Companion USB'; SubTitle = $null }, - @{ Suffix = 'companion_radio_serial'; Role = 'companionSerial'; Title = 'Companion Serial'; SubTitle = $null }, + @{ Suffix = 'companion_radio_serial'; Role = 'companionUsb'; Title = 'Companion USB'; SubTitle = 'Serial' }, @{ Suffix = 'companion_radio_wifi'; Role = 'companionWifi'; Title = 'Companion WiFi'; SubTitle = $null }, @{ Suffix = 'repeater_bridge_rs232_serial1'; Role = 'repeaterBridgeRs232'; Title = 'Repeater Bridge RS232'; SubTitle = 'Serial 1' }, @{ Suffix = 'repeater_bridge_rs232_serial2'; Role = 'repeaterBridgeRs232'; Title = 'Repeater Bridge RS232'; SubTitle = 'Serial 2' }, @@ -63,9 +62,85 @@ $RolePatterns = @( @{ Suffix = 'sensor'; Role = 'sensor'; Title = 'Sensor'; SubTitle = $null } ) +$MakerDefinitions = [ordered]@{ + elecrow = [ordered]@{ name = 'Elecrow' } + ebyte = [ordered]@{ name = 'Ebyte' } + 'gat-iot' = [ordered]@{ name = 'GAT-IoT' } + generic = [ordered]@{ name = 'Generic' } + heltec = [ordered]@{ name = 'Heltec' } + Ikoka = [ordered]@{ name = 'Ikoka' } + keepteen = [ordered]@{ name = 'Keepteen' } + lilygo = [ordered]@{ name = 'LilyGo' } + m5stack = [ordered]@{ name = 'M5Stack' } + meshadventurer = [ordered]@{ name = 'Meshadventurer' } + meshimi = [ordered]@{ name = 'Meshimi' } + meshtiny = [ordered]@{ name = 'Meshtiny' } + minewsemi = [ordered]@{ name = 'Minewsemi' } + muziworks = [ordered]@{ name = 'Muzi Works' } + nibble = [ordered]@{ name = 'Nibble' } + promicro = [ordered]@{ name = 'ProMicro' } + rak = [ordered]@{ name = 'RAK Wireless' } + raspberry = [ordered]@{ name = 'Raspberry Pi' } + seeed = [ordered]@{ name = 'Seeed Studio' } + tenstar = [ordered]@{ name = 'Tenstar' } + tinyrelay = [ordered]@{ name = 'Tiny Relay' } + uniteng = [ordered]@{ name = 'UnitEng' } + why2025 = [ordered]@{ name = 'WHY2025' } +} + +$DeviceMakerOverrides = @{ + 'KeepteenLT1' = 'keepteen' + 'M5Stack_Unit_C6L' = 'm5stack' + 'Mesh_pocket' = 'heltec' + 'Meshimi' = 'meshimi' + 'Meshtiny' = 'meshtiny' + 'Minewsemi_me25ls01' = 'minewsemi' + 'Nano_G2_Ultra' = 'uniteng' + 'nibble_screen_connect' = 'nibble' + 'PicoW' = 'raspberry' + 'ProMicro' = 'promicro' + 'R1Neo' = 'muziworks' + 'SenseCapIndicator-ESPNow' = 'seeed' + 'SenseCap_Solar' = 'seeed' + 'T_Beam_S3_Supreme_SX1262' = 'lilygo' + 't1000e' = 'seeed' + 'Tiny_Relay' = 'tinyrelay' + 'waveshare_rp2040_lora' = 'raspberry' + 'WHY2025_badge' = 'why2025' + 'wio-e5' = 'seeed' + 'wio-e5-mini' = 'seeed' + 'wio_wm1110' = 'seeed' + 'WioTrackerL1' = 'seeed' + 'WioTrackerL1Eink' = 'seeed' +} + +$DeviceMakerPatterns = @( + @{ Prefix = 'Ebyte_'; Maker = 'ebyte' }, + @{ Prefix = 'GAT562_'; Maker = 'gat-iot' }, + @{ Prefix = 'Generic_'; Maker = 'generic' }, + @{ Prefix = 'Heltec_'; Maker = 'heltec' }, + @{ Prefix = 'heltec_'; Maker = 'heltec' }, + @{ Prefix = 'ikoka_'; Maker = 'Ikoka' }, + @{ Prefix = 'LilyGo_'; Maker = 'lilygo' }, + @{ Prefix = 'Meshadventurer_'; Maker = 'meshadventurer' }, + @{ Prefix = 'RAK_'; Maker = 'rak' }, + @{ Prefix = 'Station_'; Maker = 'uniteng' }, + @{ Prefix = 'Tbeam_'; Maker = 'lilygo' }, + @{ Prefix = 'Tenstar_'; Maker = 'tenstar' }, + @{ Prefix = 'ThinkNode_'; Maker = 'elecrow' }, + @{ Prefix = 'Xiao_'; Maker = 'seeed' } +) + $DeviceNameOverrides = @{ + 'Ebyte_EoRa-S3' = 'Ebyte EoRa-S3' 'GAT562_30S_Mesh_Kit' = 'GAT-IoT GAT562 30s' + 'GAT562_Mesh_EVB_Pro' = 'GAT-IoT GAT562 EVB Pro' 'GAT562_Mesh_Tracker_Pro' = 'GAT-IoT GAT562 Tracker' + 'GAT562_Mesh_Watch13' = 'GAT-IoT GAT562 Watch13' + 'Generic_E22_sx1262' = 'Generic E22 SX1262' + 'Generic_E22_sx1268' = 'Generic E22 SX1268' + 'Generic_ESPNOW' = 'Generic ESP-NOW' + 'Heltec_ct62' = 'Heltec CT62' 'Heltec_E213' = 'Heltec Vision Master E213' 'Heltec_E290' = 'Heltec Vision Master E290' 'Heltec_mesh_solar' = 'Heltec MeshSolar / MeshTower' @@ -73,7 +148,7 @@ $DeviceNameOverrides = @{ 'Heltec_t1' = 'Heltec Mesh Node T1' 'Heltec_t114' = 'Heltec T114' 'Heltec_t114_without_display' = 'Heltec T114' - 'Heltec_T190' = 'Heltec T114' + 'Heltec_T190' = 'Heltec Vision Master T190' 'heltec_tracker_v2' = 'Heltec Wireless Tracker v2' 'heltec_v4' = 'Heltec v4' 'heltec_v4_3' = 'Heltec v4' @@ -84,24 +159,41 @@ $DeviceNameOverrides = @{ 'ikoka_nano_nrf_22dbm' = 'Ikoka Nano' 'ikoka_nano_nrf_30dbm' = 'Ikoka Nano' 'ikoka_nano_nrf_33dbm' = 'Ikoka Nano' + 'ikoka_handheld_nrf_e22_30dbm' = 'Ikoka Handheld nRF E22 30 dBm' + 'ikoka_handheld_nrf_e22_30dbm_096' = 'Ikoka Handheld nRF E22 30 dBm 0.96' + 'ikoka_handheld_nrf_e22_30dbm_096_rotated' = 'Ikoka Handheld nRF E22 30 dBm 0.96 Rotated' 'ikoka_stick_nrf_22dbm' = 'Ikoka Stick' 'ikoka_stick_nrf_30dbm' = 'Ikoka Stick' 'ikoka_stick_nrf_33dbm' = 'Ikoka Stick' 'KeepteenLT1' = 'Keepteen LT1' + 'LilyGo_T_Impulse_Plus' = 'LilyGo T-Impulse Plus nRF52840' 'LilyGo_T3S3_sx1262' = 'LilyGo T3 S3 (SX126x)' 'LilyGo_T3S3_sx1276' = 'LilyGo T3 S3 (SX127x)' 'LilyGo_TBeam_1W' = 'LilyGo T-Beam (SX1262)' 'LilyGo_TDeck' = 'LilyGo T-Deck' 'LilyGo_T-Echo-Lite' = 'LilyGo T-Echo Lite' 'LilyGo_T-Echo-Lite_non_shell' = 'LilyGo T-Echo Lite' + 'LilyGo_TETH_Elite_sx1262' = 'LilyGo T-ETH Elite (SX1262)' 'LilyGo_TLora_V2_1_1_6' = 'LilyGo LoRa32 V2.1_1.6' + 'LilyGo_Tlora_C6' = 'LilyGo T-LoRa C6' + 'M5Stack_Unit_C6L' = 'M5Stack Unit C6L' + 'Meshadventurer_sx1262' = 'Meshadventurer SX1262' + 'Meshadventurer_sx1268' = 'Meshadventurer SX1268' + 'Meshimi' = 'Meshimi' + 'Meshtiny' = 'Meshtiny' 'Mesh_pocket' = 'Heltec MeshPocket' + 'Minewsemi_me25ls01' = 'Minewsemi ME25LS01' 'Nano_G2_Ultra' = 'UnitEng Nano G2 Ultra' + 'nibble_screen_connect' = 'Nibble Screen Connect' + 'PicoW' = 'Raspberry Pi Pico W' 'ProMicro' = 'ProMicro nrf52 (faketec)' 'R1Neo' = 'Muzi Works R1 Neo' + 'RAK_11310' = 'RAK 11310' 'RAK_3112' = 'RAK WisBlock 3112' 'RAK_3401' = 'RAK WisMesh 1W Booster (3401 + 13302)' + 'RAK_3x72' = 'RAK 3x72' 'RAK_4631' = 'RAK WisBlock / WisMesh (RAK 4631)' + 'SenseCapIndicator-ESPNow' = 'Seeed Studio SenseCAP Indicator ESP-NOW' 'SenseCap_Solar' = 'Seeed Studio SenseCAP Solar' 'Station_G2' = 'UnitEng Station G2' 'Station_G3_ESP32' = 'UnitEng/BQ Voyage Station G3' @@ -109,23 +201,31 @@ $DeviceNameOverrides = @{ 't1000e' = 'Seeed Studio SenseCAP T1000-E' 'Tbeam_SX1262' = 'LilyGo T-Beam (SX1262)' 'Tbeam_SX1276' = 'LilyGo T-Beam 1.2 (SX1276)' + 'Tenstar_C3_sx1262' = 'Tenstar C3 SX1262' + 'Tenstar_C3_sx1268' = 'Tenstar C3 SX1268' 'ThinkNode_M1' = 'Elecrow ThinkNode M1' 'ThinkNode_M2' = 'Elecrow ThinkNode M2' 'ThinkNode_M3' = 'Elecrow ThinkNode M3' 'ThinkNode_M5' = 'Elecrow ThinkNode M5' 'ThinkNode_M6' = 'Elecrow ThinkNode M6' + 'Tiny_Relay' = 'Tiny Relay' 'waveshare_rp2040_lora' = 'RPI Pico 2040 + WaveShare SX1262' + 'WHY2025_badge' = 'WHY2025 Badge' + 'wio_wm1110' = 'Seeed Studio Wio WM1110' + 'wio-e5' = 'Seeed Studio Wio-E5' + 'wio-e5-mini' = 'Seeed Studio Wio-E5 mini' 'WioTrackerL1' = 'Seeed Studio Wio Tracker L1 Pro' 'WioTrackerL1Eink' = 'Seeed Studio Wio Tracker L1 EINK' 'Xiao_C3' = 'Seeed Studio Xiao C3' + 'Xiao_C6' = 'Seeed Studio Xiao C6' 'Xiao_nrf52' = 'Seeed Studio Xiao nRF52 WIO' + 'Xiao_rp2040' = 'Seeed Studio Xiao RP2040' 'Xiao_S3' = 'Seeed Studio Xiao S3 WIO' 'Xiao_S3_WIO' = 'Seeed Studio Xiao S3 WIO' } $DeviceSubTitleOverrides = @{ 'Heltec_t114_without_display' = 'Without display' - 'Heltec_T190' = 'T190' 'heltec_v4_3' = 'v4.3' 'heltec_v4_tft' = 'TFT' 'heltec_v4_expansionkit' = 'Expansion Kit' @@ -151,6 +251,22 @@ function ConvertTo-DeviceName { return $name.Trim() } +function Get-DeviceMakerKey { + param([string]$DeviceKey) + + if ($DeviceMakerOverrides.ContainsKey($DeviceKey)) { + return $DeviceMakerOverrides[$DeviceKey] + } + + foreach ($pattern in $DeviceMakerPatterns) { + if ($DeviceKey.StartsWith($pattern.Prefix, [StringComparison]::OrdinalIgnoreCase)) { + return $pattern.Maker + } + } + + return 'generic' +} + function Join-SubTitle { param( [string]$DeviceKey, @@ -299,6 +415,7 @@ function New-Catalog { [pscustomobject]@{ File = $file DeviceKey = $roleInfo.DeviceKey + MakerKey = Get-DeviceMakerKey $roleInfo.DeviceKey DeviceName = $roleInfo.DeviceName Role = $roleInfo.Role Title = $roleInfo.Title @@ -309,6 +426,11 @@ function New-Catalog { $devices = New-Object System.Collections.ArrayList foreach ($deviceGroup in @($parsedFiles | Group-Object DeviceName | Sort-Object Name)) { $deviceFiles = @($deviceGroup.Group | ForEach-Object { $_.File }) + $deviceMakerKeys = @($deviceGroup.Group | ForEach-Object { $_.MakerKey } | Sort-Object -Unique) + if ($deviceMakerKeys.Count -ne 1) { + throw "Device '$($deviceGroup.Name)' maps to multiple makers: $($deviceMakerKeys -join ', ')" + } + $deviceType = Get-DeviceType $deviceFiles $firmware = New-Object System.Collections.ArrayList @@ -348,19 +470,24 @@ function New-Catalog { } [void]$devices.Add([ordered]@{ - maker = 'keymindCascade' - class = 'keymindCascade' + maker = $deviceMakerKeys[0] name = $deviceGroup.Group[0].DeviceName type = $deviceType firmware = @($firmware) }) } + $makerMap = [ordered]@{} + foreach ($makerKey in @($devices | ForEach-Object { $_['maker'] } | Sort-Object -Unique)) { + if (-not $MakerDefinitions.Contains($makerKey)) { + throw "No maker definition for '$makerKey'" + } + $makerMap[$makerKey] = $MakerDefinitions[$makerKey] + } + return [ordered]@{ description = $Definition.Description - maker = [ordered]@{ - keymindCascade = [ordered]@{ name = 'Keymind Cascade' } - } + maker = $makerMap role = $RoleDefinitions device = @($devices) } @@ -370,7 +497,7 @@ New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null foreach ($catalogDef in $Catalogs) { $catalog = New-Catalog $catalogDef - $json = $catalog | ConvertTo-Json -Depth 100 - [System.IO.File]::WriteAllText($catalogDef.Output, $json + [Environment]::NewLine, [System.Text.UTF8Encoding]::new($false)) + $json = ($catalog | ConvertTo-Json -Depth 100) -replace "`r`n", "`n" + [System.IO.File]::WriteAllText($catalogDef.Output, $json + "`n", [System.Text.UTF8Encoding]::new($false)) Write-Output ("Wrote {0}" -f $catalogDef.Output) } diff --git a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json index cce18516..09e70989 100644 --- a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json @@ -1,14 +1,77 @@ { "description": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", "maker": { - "keymindCascade": { - "name": "Keymind Cascade" - } + "ebyte": { + "name": "Ebyte" + }, + "elecrow": { + "name": "Elecrow" + }, + "gat-iot": { + "name": "GAT-IoT" + }, + "generic": { + "name": "Generic" + }, + "heltec": { + "name": "Heltec" + }, + "Ikoka": { + "name": "Ikoka" + }, + "keepteen": { + "name": "Keepteen" + }, + "lilygo": { + "name": "LilyGo" + }, + "m5stack": { + "name": "M5Stack" + }, + "meshadventurer": { + "name": "Meshadventurer" + }, + "meshimi": { + "name": "Meshimi" + }, + "meshtiny": { + "name": "Meshtiny" + }, + "minewsemi": { + "name": "Minewsemi" + }, + "muziworks": { + "name": "Muzi Works" + }, + "nibble": { + "name": "Nibble" + }, + "promicro": { + "name": "ProMicro" + }, + "rak": { + "name": "RAK Wireless" + }, + "raspberry": { + "name": "Raspberry Pi" + }, + "seeed": { + "name": "Seeed Studio" + }, + "tenstar": { + "name": "Tenstar" + }, + "tinyrelay": { + "name": "Tiny Relay" + }, + "uniteng": { + "name": "UnitEng" + }, + "why2025": { + "name": "WHY2025" + } }, "role": { - "companionSerial": { - "name": "Companion Serial" - }, "companionWifi": { "name": "Companion WiFi" }, @@ -27,8 +90,7 @@ }, "device": [ { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "ebyte", "name": "Ebyte EoRa-S3", "type": "esp32", "firmware": [ @@ -127,8 +189,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "elecrow", "name": "Elecrow ThinkNode M1", "type": "nrf52", "firmware": [ @@ -204,34 +265,10 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "elecrow", "name": "Elecrow ThinkNode M2", "type": "esp32", "firmware": [ - { - "role": "companionSerial", - "title": "Companion Serial", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, { "role": "companionUsb", "title": "Companion USB", @@ -255,6 +292,30 @@ } } }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "Serial", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "companionWifi", "title": "Companion WiFi", @@ -373,8 +434,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "elecrow", "name": "Elecrow ThinkNode M3", "type": "nrf52", "firmware": [ @@ -450,34 +510,10 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "elecrow", "name": "Elecrow ThinkNode M5", "type": "esp32", "firmware": [ - { - "role": "companionSerial", - "title": "Companion Serial", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, { "role": "companionUsb", "title": "Companion USB", @@ -501,6 +537,30 @@ } } }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "Serial", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "companionWifi", "title": "Companion WiFi", @@ -619,8 +679,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "elecrow", "name": "Elecrow ThinkNode M6", "type": "nrf52", "firmware": [ @@ -696,62 +755,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "GAT562 Mesh EVB Pro", - "type": "nrf52", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "gat-iot", "name": "GAT-IoT GAT562 30s", "type": "nrf52", "firmware": [ @@ -827,8 +831,60 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "gat-iot", + "name": "GAT-IoT GAT562 EVB Pro", + "type": "nrf52", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "gat-iot", "name": "GAT-IoT GAT562 Tracker", "type": "nrf52", "firmware": [ @@ -904,9 +960,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Generic E22 sx1262", + "maker": "generic", + "name": "Generic E22 SX1262", "type": "esp32", "firmware": [ { @@ -958,9 +1013,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Generic E22 sx1268", + "maker": "generic", + "name": "Generic E22 SX1268", "type": "esp32", "firmware": [ { @@ -1012,9 +1066,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Generic ESPNOW", + "maker": "generic", + "name": "Generic ESP-NOW", "type": "esp32", "firmware": [ { @@ -1112,9 +1165,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Heltec ct62", + "maker": "heltec", + "name": "Heltec CT62", "type": "esp32", "firmware": [ { @@ -1212,8 +1264,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec Heltec Wireless Paper", "type": "esp32", "firmware": [ @@ -1312,8 +1363,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec Mesh Node T096", "type": "nrf52", "firmware": [ @@ -1435,8 +1485,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec Mesh Node T1", "type": "nrf52", "firmware": [ @@ -1512,8 +1561,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec MeshPocket", "type": "nrf52", "firmware": [ @@ -1589,8 +1637,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec MeshSolar / MeshTower", "type": "nrf52", "firmware": [ @@ -1666,10 +1713,9 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec T114", - "type": "esp32", + "type": "nrf52", "firmware": [ { "role": "companionUsb", @@ -1678,41 +1724,17 @@ "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", "files": [ + { + "type": "flash", + "name": "Heltec_t114_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, { "type": "download", "name": "Heltec_t114_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" - }, - { - "type": "download", - "name": "Heltec_t114_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Download" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "subTitle": "T190", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_T190_companion_radio_usb_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_companion_radio_usb_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_T190_companion_radio_usb_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_companion_radio_usb_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" } ] } @@ -1726,17 +1748,17 @@ "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, { "type": "download", "name": "Heltec_t114_without_display_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" - }, - { - "type": "download", - "name": "Heltec_t114_without_display_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Download" } ] } @@ -1749,41 +1771,17 @@ "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", "files": [ + { + "type": "flash", + "name": "Heltec_t114_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, { "type": "download", "name": "Heltec_t114_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" - }, - { - "type": "download", - "name": "Heltec_t114_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "subTitle": "T190", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_T190_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_T190_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" } ] } @@ -1797,41 +1795,17 @@ "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, { "type": "download", "name": "Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" - }, - { - "type": "download", - "name": "Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "subTitle": "T190", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_T190_repeater_bridge_espnow_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_bridge_espnow_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_T190_repeater_bridge_espnow_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_bridge_espnow_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" } ] } @@ -1844,17 +1818,17 @@ "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", "files": [ + { + "type": "flash", + "name": "Heltec_t114_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, { "type": "download", "name": "Heltec_t114_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" - }, - { - "type": "download", - "name": "Heltec_t114_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Download" } ] } @@ -1868,17 +1842,17 @@ "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, { "type": "download", "name": "Heltec_t114_without_display_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" - }, - { - "type": "download", - "name": "Heltec_t114_without_display_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Download" } ] } @@ -1891,41 +1865,17 @@ "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", "files": [ + { + "type": "flash", + "name": "Heltec_t114_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, { "type": "download", "name": "Heltec_t114_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" - }, - { - "type": "download", - "name": "Heltec_t114_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "subTitle": "T190", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_T190_room_server_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_room_server_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_T190_room_server_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_room_server_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" } ] } @@ -1939,17 +1889,17 @@ "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, { "type": "download", "name": "Heltec_t114_without_display_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" - }, - { - "type": "download", - "name": "Heltec_t114_without_display_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Download" } ] } @@ -1958,8 +1908,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec v2", "type": "esp32", "firmware": [ @@ -2104,8 +2053,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec v3", "type": "esp32", "firmware": [ @@ -2296,8 +2244,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec v4", "type": "esp32", "firmware": [ @@ -2633,8 +2580,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec v4 + Expansion Kit (Touch)", "type": "esp32", "firmware": [ @@ -2665,8 +2611,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec Vision Master E213", "type": "esp32", "firmware": [ @@ -2765,8 +2710,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec Vision Master E290", "type": "esp32", "firmware": [ @@ -2865,8 +2809,106 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", + "name": "Heltec Vision Master T190", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_T190_companion_radio_usb_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_companion_radio_usb_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_T190_companion_radio_usb_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_companion_radio_usb_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_T190_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_T190_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_T190_repeater_bridge_espnow_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_bridge_espnow_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_T190_repeater_bridge_espnow_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_bridge_espnow_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_T190_room_server_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_room_server_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_T190_room_server_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_room_server_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "heltec", "name": "Heltec Wireless Tracker", "type": "esp32", "firmware": [ @@ -2965,8 +3007,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec Wireless Tracker v2", "type": "esp32", "firmware": [ @@ -3134,8 +3175,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec WSL3", "type": "esp32", "firmware": [ @@ -3303,9 +3343,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ikoka handheld nrf e22 30dbm", + "maker": "Ikoka", + "name": "Ikoka Handheld nRF E22 30 dBm", "type": "nrf52", "firmware": [ { @@ -3357,9 +3396,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ikoka handheld nrf e22 30dbm 096", + "maker": "Ikoka", + "name": "Ikoka Handheld nRF E22 30 dBm 0.96", "type": "nrf52", "firmware": [ { @@ -3388,9 +3426,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ikoka handheld nrf e22 30dbm 096 rotated", + "maker": "Ikoka", + "name": "Ikoka Handheld nRF E22 30 dBm 0.96 Rotated", "type": "nrf52", "firmware": [ { @@ -3419,8 +3456,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "Ikoka", "name": "Ikoka Nano", "type": "nrf52", "firmware": [ @@ -3643,8 +3679,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "Ikoka", "name": "Ikoka Stick", "type": "nrf52", "firmware": [ @@ -3867,8 +3902,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "keepteen", "name": "Keepteen LT1", "type": "nrf52", "firmware": [ @@ -3944,8 +3978,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo LoRa32 V2.1_1.6", "type": "esp32", "firmware": [ @@ -4113,39 +4146,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "LilyGo T Impulse Plus", - "type": "nrf52", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo T3 S3 (SX126x)", "type": "esp32", "firmware": [ @@ -4267,8 +4268,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo T3 S3 (SX127x)", "type": "esp32", "firmware": [ @@ -4390,8 +4390,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo T-Beam (SX1262)", "type": "esp32", "firmware": [ @@ -4549,8 +4548,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo T-Beam 1.2 (SX1276)", "type": "esp32", "firmware": [ @@ -4626,8 +4624,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo T-Beam Supreme (SX1262)", "type": "esp32", "firmware": [ @@ -4726,8 +4723,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo T-Deck", "type": "esp32", "firmware": [ @@ -4780,8 +4776,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo T-Echo", "type": "nrf52", "firmware": [ @@ -4857,8 +4852,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo T-Echo Card", "type": "nrf52", "firmware": [ @@ -4934,8 +4928,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo T-Echo Lite", "type": "nrf52", "firmware": [ @@ -5012,9 +5005,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "LilyGo TETH Elite sx1262", + "maker": "lilygo", + "name": "LilyGo T-ETH Elite (SX1262)", "type": "esp32", "firmware": [ { @@ -5089,9 +5081,38 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "LilyGo Tlora C6", + "maker": "lilygo", + "name": "LilyGo T-Impulse Plus nRF52840", + "type": "nrf52", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "lilygo", + "name": "LilyGo T-LoRa C6", "type": "esp32", "firmware": [ { @@ -5143,8 +5164,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "m5stack", "name": "M5Stack Unit C6L", "type": "esp32", "firmware": [ @@ -5220,9 +5240,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Meshadventurer sx1262", + "maker": "meshadventurer", + "name": "Meshadventurer SX1262", "type": "esp32", "firmware": [ { @@ -5343,9 +5362,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Meshadventurer sx1268", + "maker": "meshadventurer", + "name": "Meshadventurer SX1268", "type": "esp32", "firmware": [ { @@ -5466,8 +5484,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "meshimi", "name": "Meshimi", "type": "esp32", "firmware": [ @@ -5497,8 +5514,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "meshtiny", "name": "Meshtiny", "type": "nrf52", "firmware": [ @@ -5528,9 +5544,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Minewsemi me25ls01", + "maker": "minewsemi", + "name": "Minewsemi ME25LS01", "type": "nrf52", "firmware": [ { @@ -5628,8 +5643,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "muziworks", "name": "Muzi Works R1 Neo", "type": "nrf52", "firmware": [ @@ -5751,9 +5765,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "nibble screen connect", + "maker": "nibble", + "name": "Nibble Screen Connect", "type": "esp32", "firmware": [ { @@ -5897,108 +5910,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "PicoW", - "type": "noflash", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "PicoW_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "PicoW_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "PicoW_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "PicoW_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "PicoW_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "PicoW_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "PicoW_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "PicoW_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "promicro", "name": "ProMicro nrf52 (faketec)", "type": "nrf52", "firmware": [ @@ -6144,8 +6056,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "rak", "name": "RAK 11310", "type": "noflash", "firmware": [ @@ -6267,8 +6178,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "rak", "name": "RAK 3x72", "type": "noflash", "firmware": [ @@ -6344,8 +6254,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "rak", "name": "RAK WisBlock / WisMesh (RAK 4631)", "type": "nrf52", "firmware": [ @@ -6515,8 +6424,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "rak", "name": "RAK WisBlock 3112", "type": "esp32", "firmware": [ @@ -6707,8 +6615,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "rak", "name": "RAK WisMesh 1W Booster (3401 + 13302)", "type": "nrf52", "firmware": [ @@ -6830,8 +6737,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "rak", "name": "RAK WisMesh Tag", "type": "nrf52", "firmware": [ @@ -6930,8 +6836,106 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "raspberry", + "name": "Raspberry Pi Pico W", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "PicoW_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "PicoW_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "PicoW_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "PicoW_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "PicoW_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "PicoW_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "PicoW_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "PicoW_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "raspberry", "name": "RPI Pico 2040 + WaveShare SX1262", "type": "noflash", "firmware": [ @@ -7053,8 +7057,37 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "seeed", + "name": "Seeed Studio SenseCAP Indicator ESP-NOW", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "SenseCapIndicator-ESPNow_comp_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/SenseCapIndicator-ESPNow_comp_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "SenseCapIndicator-ESPNow_comp_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/SenseCapIndicator-ESPNow_comp_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "seeed", "name": "Seeed Studio SenseCAP Solar", "type": "nrf52", "firmware": [ @@ -7130,8 +7163,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "seeed", "name": "Seeed Studio SenseCAP T1000-E", "type": "nrf52", "firmware": [ @@ -7207,8 +7239,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "seeed", "name": "Seeed Studio Wio Tracker L1 Pro", "type": "nrf52", "firmware": [ @@ -7284,8 +7315,200 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "seeed", + "name": "Seeed Studio Wio WM1110", + "type": "noflash", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "wio_wm1110_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio_wm1110_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "wio_wm1110_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio_wm1110_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "seeed", + "name": "Seeed Studio Wio-E5", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "wio-e5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "seeed", + "name": "Seeed Studio Wio-E5 mini", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "wio-e5-mini_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5-mini_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "wio-e5-mini_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5-mini_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "wio-e5-mini_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5-mini_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "seeed", "name": "Seeed Studio Xiao C3", "type": "esp32", "firmware": [ @@ -7384,8 +7607,37 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "seeed", + "name": "Seeed Studio Xiao C6", + "type": "esp32", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C6_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C6_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C6_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C6_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "seeed", "name": "Seeed Studio Xiao nRF52 WIO", "type": "nrf52", "firmware": [ @@ -7461,34 +7713,109 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Seeed Studio Xiao S3 WIO", - "type": "esp32", + "maker": "seeed", + "name": "Seeed Studio Xiao RP2040", + "type": "noflash", "firmware": [ { - "role": "companionSerial", - "title": "Companion Serial", + "role": "companionUsb", + "title": "Companion USB", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", "files": [ { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" + "type": "download", + "name": "Xiao_rp2040_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" }, { - "type": "flash-update", - "name": "Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" + "type": "download", + "name": "Xiao_rp2040_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" } ] } } }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Xiao_rp2040_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Xiao_rp2040_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Xiao_rp2040_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Xiao_rp2040_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "Xiao_rp2040_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Xiao_rp2040_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "seeed", + "name": "Seeed Studio Xiao S3 WIO", + "type": "esp32", + "firmware": [ { "role": "companionUsb", "title": "Companion USB", @@ -7524,6 +7851,30 @@ } } }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "Serial", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "companionWifi", "title": "Companion WiFi", @@ -7713,40 +8064,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "SenseCapIndicator-ESPNow", - "type": "esp32", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "SenseCapIndicator-ESPNow_comp_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/SenseCapIndicator-ESPNow_comp_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "SenseCapIndicator-ESPNow_comp_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/SenseCapIndicator-ESPNow_comp_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Tenstar C3 sx1262", + "maker": "tenstar", + "name": "Tenstar C3 SX1262", "type": "esp32", "firmware": [ { @@ -7798,9 +8117,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Tenstar C3 sx1268", + "maker": "tenstar", + "name": "Tenstar C3 SX1268", "type": "esp32", "firmware": [ { @@ -7852,8 +8170,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "tinyrelay", "name": "Tiny Relay", "type": "noflash", "firmware": [ @@ -7929,8 +8246,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "uniteng", "name": "UnitEng Nano G2 Ultra", "type": "nrf52", "firmware": [ @@ -8006,8 +8322,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "uniteng", "name": "UnitEng Station G2", "type": "esp32", "firmware": [ @@ -8177,8 +8492,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "uniteng", "name": "UnitEng/BQ Voyage Station G3", "type": "esp32", "firmware": [ @@ -8301,9 +8615,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "WHY2025 badge", + "maker": "why2025", + "name": "WHY2025 Badge", "type": "esp32", "firmware": [ { @@ -8330,333 +8643,6 @@ } } ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "wio wm1110", - "type": "noflash", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "wio_wm1110_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio_wm1110_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "wio_wm1110_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio_wm1110_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "wio-e5", - "type": "noflash", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "wio-e5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "wio-e5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeaterBridgeRs232", - "title": "Repeater Bridge RS232", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "wio-e5-mini", - "type": "noflash", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "wio-e5-mini_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "wio-e5-mini_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "wio-e5-mini_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "wio-e5-mini_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "sensor", - "title": "Sensor", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "wio-e5-mini_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "wio-e5-mini_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Xiao C6", - "type": "esp32", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_C6_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C6_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_C6_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C6_repeater_-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Xiao rp2040", - "type": "noflash", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "Xiao_rp2040_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "Xiao_rp2040_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "Xiao_rp2040_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "Xiao_rp2040_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "Xiao_rp2040_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "Xiao_rp2040_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "Xiao_rp2040_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "Xiao_rp2040_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - } - ] } ] } diff --git a/mesh-america/keymind-cascade-v1.16.0-provider.json b/mesh-america/keymind-cascade-v1.16.0-provider.json index d7dc6d18..a5d062d3 100644 --- a/mesh-america/keymind-cascade-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-v1.16.0-provider.json @@ -1,14 +1,77 @@ { "description": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "maker": { - "keymindCascade": { - "name": "Keymind Cascade" - } + "ebyte": { + "name": "Ebyte" + }, + "elecrow": { + "name": "Elecrow" + }, + "gat-iot": { + "name": "GAT-IoT" + }, + "generic": { + "name": "Generic" + }, + "heltec": { + "name": "Heltec" + }, + "Ikoka": { + "name": "Ikoka" + }, + "keepteen": { + "name": "Keepteen" + }, + "lilygo": { + "name": "LilyGo" + }, + "m5stack": { + "name": "M5Stack" + }, + "meshadventurer": { + "name": "Meshadventurer" + }, + "meshimi": { + "name": "Meshimi" + }, + "meshtiny": { + "name": "Meshtiny" + }, + "minewsemi": { + "name": "Minewsemi" + }, + "muziworks": { + "name": "Muzi Works" + }, + "nibble": { + "name": "Nibble" + }, + "promicro": { + "name": "ProMicro" + }, + "rak": { + "name": "RAK Wireless" + }, + "raspberry": { + "name": "Raspberry Pi" + }, + "seeed": { + "name": "Seeed Studio" + }, + "tenstar": { + "name": "Tenstar" + }, + "tinyrelay": { + "name": "Tiny Relay" + }, + "uniteng": { + "name": "UnitEng" + }, + "why2025": { + "name": "WHY2025" + } }, "role": { - "companionSerial": { - "name": "Companion Serial" - }, "companionWifi": { "name": "Companion WiFi" }, @@ -27,8 +90,7 @@ }, "device": [ { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "ebyte", "name": "Ebyte EoRa-S3", "type": "esp32", "firmware": [ @@ -150,8 +212,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "elecrow", "name": "Elecrow ThinkNode M1", "type": "nrf52", "firmware": [ @@ -250,8 +311,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "elecrow", "name": "Elecrow ThinkNode M2", "type": "esp32", "firmware": [ @@ -278,29 +338,6 @@ } } }, - { - "role": "companionSerial", - "title": "Companion Serial", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, { "role": "companionUsb", "title": "Companion USB", @@ -324,6 +361,30 @@ } } }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "Serial", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M2_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "companionWifi", "title": "Companion WiFi", @@ -442,8 +503,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "elecrow", "name": "Elecrow ThinkNode M3", "type": "nrf52", "firmware": [ @@ -542,8 +602,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "elecrow", "name": "Elecrow ThinkNode M5", "type": "esp32", "firmware": [ @@ -594,29 +653,6 @@ } } }, - { - "role": "companionSerial", - "title": "Companion Serial", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, { "role": "companionUsb", "title": "Companion USB", @@ -640,6 +676,30 @@ } } }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "Serial", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ThinkNode_M5_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "companionWifi", "title": "Companion WiFi", @@ -758,8 +818,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "elecrow", "name": "Elecrow ThinkNode M6", "type": "nrf52", "firmware": [ @@ -858,93 +917,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "GAT562 Mesh EVB Pro", - "type": "nrf52", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "GAT562 Mesh Watch13", - "type": "nrf52", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "gat-iot", "name": "GAT-IoT GAT562 30s", "type": "nrf52", "firmware": [ @@ -1043,8 +1016,60 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "gat-iot", + "name": "GAT-IoT GAT562 EVB Pro", + "type": "nrf52", + "firmware": [ + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_EVB_Pro_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "gat-iot", "name": "GAT-IoT GAT562 Tracker", "type": "nrf52", "firmware": [ @@ -1143,9 +1168,38 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Generic E22 sx1262", + "maker": "gat-iot", + "name": "GAT-IoT GAT562 Watch13", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/GAT562_Mesh_Watch13_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "generic", + "name": "Generic E22 SX1262", "type": "esp32", "firmware": [ { @@ -1197,9 +1251,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Generic E22 sx1268", + "maker": "generic", + "name": "Generic E22 SX1268", "type": "esp32", "firmware": [ { @@ -1251,9 +1304,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Generic ESPNOW", + "maker": "generic", + "name": "Generic ESP-NOW", "type": "esp32", "firmware": [ { @@ -1351,9 +1403,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Heltec ct62", + "maker": "heltec", + "name": "Heltec CT62", "type": "esp32", "firmware": [ { @@ -1498,8 +1549,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec Heltec Wireless Paper", "type": "esp32", "firmware": [ @@ -1645,8 +1695,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec Mesh Node T096", "type": "nrf52", "firmware": [ @@ -1839,8 +1888,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec Mesh Node T1", "type": "nrf52", "firmware": [ @@ -1939,8 +1987,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec MeshPocket", "type": "nrf52", "firmware": [ @@ -2039,8 +2086,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec MeshSolar / MeshTower", "type": "nrf52", "firmware": [ @@ -2139,10 +2185,9 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec T114", - "type": "esp32", + "type": "nrf52", "firmware": [ { "role": "companionBle", @@ -2151,41 +2196,17 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ + { + "type": "flash", + "name": "Heltec_t114_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, { "type": "download", "name": "Heltec_t114_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" - }, - { - "type": "download", - "name": "Heltec_t114_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Download" - } - ] - } - } - }, - { - "role": "companionBle", - "title": "Companion BLE", - "subTitle": "T190", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_T190_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_T190_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" } ] } @@ -2199,17 +2220,17 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, { "type": "download", "name": "Heltec_t114_without_display_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" - }, - { - "type": "download", - "name": "Heltec_t114_without_display_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Download" } ] } @@ -2222,41 +2243,17 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ + { + "type": "flash", + "name": "Heltec_t114_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, { "type": "download", "name": "Heltec_t114_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" - }, - { - "type": "download", - "name": "Heltec_t114_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Download" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion USB", - "subTitle": "T190", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_T190_companion_radio_usb_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_companion_radio_usb_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_T190_companion_radio_usb_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_companion_radio_usb_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" } ] } @@ -2270,17 +2267,17 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, { "type": "download", "name": "Heltec_t114_without_display_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" - }, - { - "type": "download", - "name": "Heltec_t114_without_display_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Download" } ] } @@ -2293,41 +2290,17 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ + { + "type": "flash", + "name": "Heltec_t114_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, { "type": "download", "name": "Heltec_t114_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" - }, - { - "type": "download", - "name": "Heltec_t114_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "subTitle": "T190", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_T190_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_T190_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" } ] } @@ -2341,41 +2314,17 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, { "type": "download", "name": "Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" - }, - { - "type": "download", - "name": "Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", - "title": "Repeater Bridge ESP-NOW", - "subTitle": "T190", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_T190_repeater_bridge_espnow_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_bridge_espnow_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_T190_repeater_bridge_espnow_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_bridge_espnow_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" } ] } @@ -2388,17 +2337,17 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ + { + "type": "flash", + "name": "Heltec_t114_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, { "type": "download", "name": "Heltec_t114_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" - }, - { - "type": "download", - "name": "Heltec_t114_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Download" } ] } @@ -2412,17 +2361,17 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, { "type": "download", "name": "Heltec_t114_without_display_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" - }, - { - "type": "download", - "name": "Heltec_t114_without_display_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Download" } ] } @@ -2435,41 +2384,17 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ + { + "type": "flash", + "name": "Heltec_t114_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, { "type": "download", "name": "Heltec_t114_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" - }, - { - "type": "download", - "name": "Heltec_t114_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "subTitle": "T190", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Heltec_T190_room_server_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_room_server_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Heltec_T190_room_server_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_room_server_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" } ] } @@ -2483,17 +2408,17 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, { "type": "download", "name": "Heltec_t114_without_display_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", "title": "UF2 download" - }, - { - "type": "download", - "name": "Heltec_t114_without_display_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Download" } ] } @@ -2502,8 +2427,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec v2", "type": "esp32", "firmware": [ @@ -2695,8 +2619,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec v3", "type": "esp32", "firmware": [ @@ -2934,8 +2857,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec v4", "type": "esp32", "firmware": [ @@ -3390,8 +3312,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec v4 + Expansion Kit (Touch)", "type": "esp32", "firmware": [ @@ -3446,8 +3367,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec Vision Master E213", "type": "esp32", "firmware": [ @@ -3569,8 +3489,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec Vision Master E290", "type": "esp32", "firmware": [ @@ -3692,8 +3611,129 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", + "name": "Heltec Vision Master T190", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_T190_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_T190_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_T190_companion_radio_usb_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_companion_radio_usb_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_T190_companion_radio_usb_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_companion_radio_usb_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_T190_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_T190_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_T190_repeater_bridge_espnow_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_bridge_espnow_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_T190_repeater_bridge_espnow_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_repeater_bridge_espnow_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Heltec_T190_room_server_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_room_server_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Heltec_T190_room_server_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_T190_room_server_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "heltec", "name": "Heltec Wireless Tracker", "type": "esp32", "firmware": [ @@ -3839,8 +3879,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec Wireless Tracker v2", "type": "esp32", "firmware": [ @@ -4055,8 +4094,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "heltec", "name": "Heltec WSL3", "type": "esp32", "firmware": [ @@ -4271,9 +4309,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ikoka handheld nrf e22 30dbm", + "maker": "Ikoka", + "name": "Ikoka Handheld nRF E22 30 dBm", "type": "nrf52", "firmware": [ { @@ -4325,9 +4362,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ikoka handheld nrf e22 30dbm 096", + "maker": "Ikoka", + "name": "Ikoka Handheld nRF E22 30 dBm 0.96", "type": "nrf52", "firmware": [ { @@ -4379,9 +4415,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "ikoka handheld nrf e22 30dbm 096 rotated", + "maker": "Ikoka", + "name": "Ikoka Handheld nRF E22 30 dBm 0.96 Rotated", "type": "nrf52", "firmware": [ { @@ -4433,8 +4468,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "Ikoka", "name": "Ikoka Nano", "type": "nrf52", "firmware": [ @@ -4729,8 +4763,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "Ikoka", "name": "Ikoka Stick", "type": "nrf52", "firmware": [ @@ -5025,8 +5058,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "keepteen", "name": "Keepteen LT1", "type": "nrf52", "firmware": [ @@ -5125,8 +5157,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo LoRa32 V2.1_1.6", "type": "esp32", "firmware": [ @@ -5341,62 +5372,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "LilyGo T Impulse Plus", - "type": "nrf52", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo T3 S3 (SX126x)", "type": "esp32", "firmware": [ @@ -5565,8 +5541,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo T3 S3 (SX127x)", "type": "esp32", "firmware": [ @@ -5711,8 +5686,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo T-Beam (SX1262)", "type": "esp32", "firmware": [ @@ -5941,8 +5915,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo T-Beam 1.2 (SX1276)", "type": "esp32", "firmware": [ @@ -6065,8 +6038,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo T-Beam Supreme (SX1262)", "type": "esp32", "firmware": [ @@ -6212,8 +6184,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo T-Deck", "type": "esp32", "firmware": [ @@ -6289,8 +6260,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo T-Echo", "type": "nrf52", "firmware": [ @@ -6389,8 +6359,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo T-Echo Card", "type": "nrf52", "firmware": [ @@ -6489,8 +6458,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "lilygo", "name": "LilyGo T-Echo Lite", "type": "nrf52", "firmware": [ @@ -6614,9 +6582,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "LilyGo TETH Elite sx1262", + "maker": "lilygo", + "name": "LilyGo T-ETH Elite (SX1262)", "type": "esp32", "firmware": [ { @@ -6714,9 +6681,61 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "LilyGo Tlora C6", + "maker": "lilygo", + "name": "LilyGo T-Impulse Plus nRF52840", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_T_Impulse_Plus_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "lilygo", + "name": "LilyGo T-LoRa C6", "type": "esp32", "firmware": [ { @@ -6791,8 +6810,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "m5stack", "name": "M5Stack Unit C6L", "type": "esp32", "firmware": [ @@ -6891,9 +6909,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Meshadventurer sx1262", + "maker": "meshadventurer", + "name": "Meshadventurer SX1262", "type": "esp32", "firmware": [ { @@ -7037,9 +7054,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Meshadventurer sx1268", + "maker": "meshadventurer", + "name": "Meshadventurer SX1268", "type": "esp32", "firmware": [ { @@ -7183,8 +7199,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "meshimi", "name": "Meshimi", "type": "esp32", "firmware": [ @@ -7237,8 +7252,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "meshtiny", "name": "Meshtiny", "type": "nrf52", "firmware": [ @@ -7291,9 +7305,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Minewsemi me25ls01", + "maker": "minewsemi", + "name": "Minewsemi ME25LS01", "type": "nrf52", "firmware": [ { @@ -7414,8 +7427,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "muziworks", "name": "Muzi Works R1 Neo", "type": "nrf52", "firmware": [ @@ -7560,9 +7572,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "nibble screen connect", + "maker": "nibble", + "name": "Nibble Screen Connect", "type": "esp32", "firmware": [ { @@ -7729,108 +7740,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "PicoW", - "type": "noflash", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "PicoW_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "PicoW_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "PicoW_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "PicoW_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "PicoW_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "PicoW_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "PicoW_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "PicoW_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "promicro", "name": "ProMicro nrf52 (faketec)", "type": "nrf52", "firmware": [ @@ -7999,8 +7909,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "rak", "name": "RAK 11310", "type": "noflash", "firmware": [ @@ -8122,8 +8031,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "rak", "name": "RAK 3x72", "type": "noflash", "firmware": [ @@ -8199,8 +8107,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "rak", "name": "RAK WisBlock / WisMesh (RAK 4631)", "type": "nrf52", "firmware": [ @@ -8393,8 +8300,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "rak", "name": "RAK WisBlock 3112", "type": "esp32", "firmware": [ @@ -8608,8 +8514,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "rak", "name": "RAK WisMesh 1W Booster (3401 + 13302)", "type": "nrf52", "firmware": [ @@ -8754,8 +8659,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "rak", "name": "RAK WisMesh Tag", "type": "nrf52", "firmware": [ @@ -8877,8 +8781,106 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "raspberry", + "name": "Raspberry Pi Pico W", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "PicoW_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "PicoW_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "PicoW_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "PicoW_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "PicoW_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "PicoW_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "PicoW_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "PicoW_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/PicoW_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "raspberry", "name": "RPI Pico 2040 + WaveShare SX1262", "type": "noflash", "firmware": [ @@ -9000,8 +9002,37 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "seeed", + "name": "Seeed Studio SenseCAP Indicator ESP-NOW", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "SenseCapIndicator-ESPNow_comp_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/SenseCapIndicator-ESPNow_comp_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "SenseCapIndicator-ESPNow_comp_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/SenseCapIndicator-ESPNow_comp_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "seeed", "name": "Seeed Studio SenseCAP Solar", "type": "nrf52", "firmware": [ @@ -9100,8 +9131,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "seeed", "name": "Seeed Studio SenseCAP T1000-E", "type": "nrf52", "firmware": [ @@ -9200,8 +9230,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "seeed", "name": "Seeed Studio Wio Tracker L1 EINK", "type": "nrf52", "firmware": [ @@ -9231,8 +9260,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "seeed", "name": "Seeed Studio Wio Tracker L1 Pro", "type": "nrf52", "firmware": [ @@ -9331,8 +9359,217 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "seeed", + "name": "Seeed Studio Wio WM1110", + "type": "noflash", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio_wm1110_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio_wm1110_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio_wm1110_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio_wm1110_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio_wm1110_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio_wm1110_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + } + ] + }, + { + "maker": "seeed", + "name": "Seeed Studio Wio-E5", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio-e5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeaterBridgeRs232", + "title": "Repeater Bridge RS232", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "seeed", + "name": "Seeed Studio Wio-E5 mini", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio-e5-mini_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5-mini_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio-e5-mini_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5-mini_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio-e5-mini_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5-mini_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "seeed", "name": "Seeed Studio Xiao C3", "type": "esp32", "firmware": [ @@ -9478,8 +9715,60 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "seeed", + "name": "Seeed Studio Xiao C6", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C6_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C6_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C6_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C6_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_C6_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C6_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_C6_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C6_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "seeed", "name": "Seeed Studio Xiao nRF52 WIO", "type": "nrf52", "firmware": [ @@ -9578,8 +9867,106 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "seeed", + "name": "Seeed Studio Xiao RP2040", + "type": "noflash", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Xiao_rp2040_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Xiao_rp2040_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Xiao_rp2040_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Xiao_rp2040_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Xiao_rp2040_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Xiao_rp2040_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "Xiao_rp2040_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + }, + { + "type": "download", + "name": "Xiao_rp2040_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + } + ] + }, + { + "maker": "seeed", "name": "Seeed Studio Xiao S3 WIO", "type": "esp32", "firmware": [ @@ -9654,29 +10041,6 @@ } } }, - { - "role": "companionSerial", - "title": "Companion Serial", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, { "role": "companionUsb", "title": "Companion USB", @@ -9712,6 +10076,30 @@ } } }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "Serial", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_serial-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "companionWifi", "title": "Companion WiFi", @@ -9901,40 +10289,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "SenseCapIndicator-ESPNow", - "type": "esp32", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "SenseCapIndicator-ESPNow_comp_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/SenseCapIndicator-ESPNow_comp_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "SenseCapIndicator-ESPNow_comp_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/SenseCapIndicator-ESPNow_comp_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Tenstar C3 sx1262", + "maker": "tenstar", + "name": "Tenstar C3 SX1262", "type": "esp32", "firmware": [ { @@ -9986,9 +10342,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Tenstar C3 sx1268", + "maker": "tenstar", + "name": "Tenstar C3 SX1268", "type": "esp32", "firmware": [ { @@ -10040,8 +10395,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "tinyrelay", "name": "Tiny Relay", "type": "noflash", "firmware": [ @@ -10117,8 +10471,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "uniteng", "name": "UnitEng Nano G2 Ultra", "type": "nrf52", "firmware": [ @@ -10217,8 +10570,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "uniteng", "name": "UnitEng Station G2", "type": "esp32", "firmware": [ @@ -10411,8 +10763,7 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", + "maker": "uniteng", "name": "UnitEng/BQ Voyage Station G3", "type": "esp32", "firmware": [ @@ -10558,9 +10909,8 @@ ] }, { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "WHY2025 badge", + "maker": "why2025", + "name": "WHY2025 Badge", "type": "esp32", "firmware": [ { @@ -10610,373 +10960,6 @@ } } ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "wio wm1110", - "type": "noflash", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "wio_wm1110_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio_wm1110_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "wio_wm1110_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio_wm1110_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "wio_wm1110_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio_wm1110_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "wio-e5", - "type": "noflash", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "wio-e5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "wio-e5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeaterBridgeRs232", - "title": "Repeater Bridge RS232", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "wio-e5-mini", - "type": "noflash", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "wio-e5-mini_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "wio-e5-mini_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "wio-e5-mini_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "wio-e5-mini_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "sensor", - "title": "Sensor", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "wio-e5-mini_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "wio-e5-mini_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-mini_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Xiao C6", - "type": "esp32", - "firmware": [ - { - "role": "companionBle", - "title": "Companion BLE", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_C6_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C6_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_C6_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C6_companion_radio_ble_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_C6_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C6_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_C6_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_C6_repeater_-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "keymindCascade", - "class": "keymindCascade", - "name": "Xiao rp2040", - "type": "noflash", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "Xiao_rp2040_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "Xiao_rp2040_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "Xiao_rp2040_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "Xiao_rp2040_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "Xiao_rp2040_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "Xiao_rp2040_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "Xiao_rp2040_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - }, - { - "type": "download", - "name": "Xiao_rp2040_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_rp2040_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - } - ] } ] } From 33269aef533183d24a09a04f464e88d9cb70395e Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 30 Jun 2026 23:40:37 -0700 Subject: [PATCH 187/214] Split unsafe Mesh America folds --- .../generate-mesh-america-catalogs.ps1 | 4 +- ...mind-cascade-logging-v1.16.0-provider.json | 376 ++++++++----- .../keymind-cascade-v1.16.0-provider.json | 518 +++++++++++------- 3 files changed, 574 insertions(+), 324 deletions(-) diff --git a/mesh-america/generate-mesh-america-catalogs.ps1 b/mesh-america/generate-mesh-america-catalogs.ps1 index 8429dbff..f4b5bed6 100644 --- a/mesh-america/generate-mesh-america-catalogs.ps1 +++ b/mesh-america/generate-mesh-america-catalogs.ps1 @@ -169,7 +169,7 @@ $DeviceNameOverrides = @{ 'LilyGo_T_Impulse_Plus' = 'LilyGo T-Impulse Plus nRF52840' 'LilyGo_T3S3_sx1262' = 'LilyGo T3 S3 (SX126x)' 'LilyGo_T3S3_sx1276' = 'LilyGo T3 S3 (SX127x)' - 'LilyGo_TBeam_1W' = 'LilyGo T-Beam (SX1262)' + 'LilyGo_TBeam_1W' = 'LilyGo T-Beam 1W' 'LilyGo_TDeck' = 'LilyGo T-Deck' 'LilyGo_T-Echo-Lite' = 'LilyGo T-Echo Lite' 'LilyGo_T-Echo-Lite_non_shell' = 'LilyGo T-Echo Lite' @@ -220,7 +220,7 @@ $DeviceNameOverrides = @{ 'Xiao_C6' = 'Seeed Studio Xiao C6' 'Xiao_nrf52' = 'Seeed Studio Xiao nRF52 WIO' 'Xiao_rp2040' = 'Seeed Studio Xiao RP2040' - 'Xiao_S3' = 'Seeed Studio Xiao S3 WIO' + 'Xiao_S3' = 'Seeed Studio Xiao S3' 'Xiao_S3_WIO' = 'Seeed Studio Xiao S3 WIO' } diff --git a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json index 09e70989..cfb9e9c8 100644 --- a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json @@ -4394,52 +4394,6 @@ "name": "LilyGo T-Beam (SX1262)", "type": "esp32", "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TBeam_1W_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TBeam_1W_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TBeam_1W_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TBeam_1W_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, { "role": "repeater", "title": "Repeater", @@ -4447,24 +4401,12 @@ "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-wipe", "name": "Tbeam_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-update", - "name": "LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - }, { "type": "flash-update", "name": "Tbeam_SX1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", @@ -4482,24 +4424,12 @@ "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TBeam_1W_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-wipe", "name": "Tbeam_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-update", - "name": "LilyGo_TBeam_1W_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - }, { "type": "flash-update", "name": "Tbeam_SX1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", @@ -4517,24 +4447,12 @@ "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TBeam_1W_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-wipe", "name": "Tbeam_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-update", - "name": "LilyGo_TBeam_1W_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - }, { "type": "flash-update", "name": "Tbeam_SX1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", @@ -4623,6 +4541,128 @@ } ] }, + { + "maker": "lilygo", + "name": "LilyGo T-Beam 1W", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, { "maker": "lilygo", "name": "LilyGo T-Beam Supreme (SX1262)", @@ -7813,7 +7853,7 @@ }, { "maker": "seeed", - "name": "Seeed Studio Xiao S3 WIO", + "name": "Seeed Studio Xiao S3", "type": "esp32", "firmware": [ { @@ -7829,17 +7869,127 @@ "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-update", "name": "Xiao_S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "seeed", + "name": "Seeed Studio Xiao S3 WIO", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", @@ -7905,24 +8055,12 @@ "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-wipe", "name": "Xiao_S3_WIO_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-update", - "name": "Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - }, { "type": "flash-update", "name": "Xiao_S3_WIO_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", @@ -7940,24 +8078,12 @@ "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-wipe", "name": "Xiao_S3_WIO_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-update", - "name": "Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - }, { "type": "flash-update", "name": "Xiao_S3_WIO_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", @@ -7975,24 +8101,12 @@ "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-wipe", "name": "Xiao_S3_WIO_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-update", - "name": "Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - }, { "type": "flash-update", "name": "Xiao_S3_WIO_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", @@ -8010,24 +8124,12 @@ "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-wipe", "name": "Xiao_S3_WIO_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-update", - "name": "Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - }, { "type": "flash-update", "name": "Xiao_S3_WIO_sensor-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", diff --git a/mesh-america/keymind-cascade-v1.16.0-provider.json b/mesh-america/keymind-cascade-v1.16.0-provider.json index a5d062d3..ee79902c 100644 --- a/mesh-america/keymind-cascade-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-v1.16.0-provider.json @@ -5697,24 +5697,12 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TBeam_1W_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-wipe", "name": "Tbeam_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-update", - "name": "LilyGo_TBeam_1W_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - }, { "type": "flash-update", "name": "Tbeam_SX1262_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", @@ -5733,24 +5721,12 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TBeam_1W_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-wipe", "name": "Tbeam_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-update", - "name": "LilyGo_TBeam_1W_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - }, { "type": "flash-update", "name": "Tbeam_SX1262_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", @@ -5761,52 +5737,6 @@ } } }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TBeam_1W_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TBeam_1W_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TBeam_1W_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TBeam_1W_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, { "role": "repeater", "title": "Repeater", @@ -5814,24 +5744,12 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-wipe", "name": "Tbeam_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-update", - "name": "LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - }, { "type": "flash-update", "name": "Tbeam_SX1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", @@ -5849,24 +5767,12 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TBeam_1W_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-wipe", "name": "Tbeam_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-update", - "name": "LilyGo_TBeam_1W_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - }, { "type": "flash-update", "name": "Tbeam_SX1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", @@ -5884,24 +5790,12 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TBeam_1W_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-wipe", "name": "Tbeam_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tbeam_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-update", - "name": "LilyGo_TBeam_1W_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - }, { "type": "flash-update", "name": "Tbeam_SX1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", @@ -6037,6 +5931,175 @@ } ] }, + { + "maker": "lilygo", + "name": "LilyGo T-Beam 1W", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, { "maker": "lilygo", "name": "LilyGo T-Beam Supreme (SX1262)", @@ -9967,7 +10030,7 @@ }, { "maker": "seeed", - "name": "Seeed Studio Xiao S3 WIO", + "name": "Seeed Studio Xiao S3", "type": "esp32", "firmware": [ { @@ -9983,23 +10046,11 @@ "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-update", "name": "Xiao_S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" } ] } @@ -10019,23 +10070,11 @@ "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-update", "name": "Xiao_S3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" } ] } @@ -10054,17 +10093,174 @@ "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-wipe", - "name": "Xiao_S3_WIO_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-update", "name": "Xiao_S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeaterBridgeEspnow", + "title": "Repeater Bridge ESP-NOW", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "sensor", + "title": "Sensor", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + } + ] + }, + { + "maker": "seeed", + "name": "Seeed Studio Xiao S3 WIO", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "title": "Companion BLE", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionBle", + "title": "Companion BLE", + "subTitle": "Power saving", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_ble_ps-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_WIO_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" }, { "type": "flash-update", @@ -10130,24 +10326,12 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-wipe", "name": "Xiao_S3_WIO_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-update", - "name": "Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - }, { "type": "flash-update", "name": "Xiao_S3_WIO_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", @@ -10165,24 +10349,12 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-wipe", "name": "Xiao_S3_WIO_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-update", - "name": "Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - }, { "type": "flash-update", "name": "Xiao_S3_WIO_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", @@ -10200,24 +10372,12 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-wipe", "name": "Xiao_S3_WIO_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-update", - "name": "Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - }, { "type": "flash-update", "name": "Xiao_S3_WIO_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", @@ -10235,24 +10395,12 @@ "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, { "type": "flash-wipe", "name": "Xiao_S3_WIO_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_WIO_sensor-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", "title": "Full install (bootloader + firmware)" }, - { - "type": "flash-update", - "name": "Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - }, { "type": "flash-update", "name": "Xiao_S3_WIO_sensor-v1.16.0-halo-keymind-dev-28ee5d2c.bin", From 04a90654ae4d1b6b701c8c2b44d325eaf10852ec Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 30 Jun 2026 23:45:44 -0700 Subject: [PATCH 188/214] Add pin notes for split Mesh America boards --- .../generate-mesh-america-catalogs.ps1 | 28 +++++++++++- ...mind-cascade-logging-v1.16.0-provider.json | 30 ++++++++----- .../keymind-cascade-v1.16.0-provider.json | 44 ++++++++++++------- 3 files changed, 74 insertions(+), 28 deletions(-) diff --git a/mesh-america/generate-mesh-america-catalogs.ps1 b/mesh-america/generate-mesh-america-catalogs.ps1 index f4b5bed6..80ad76fa 100644 --- a/mesh-america/generate-mesh-america-catalogs.ps1 +++ b/mesh-america/generate-mesh-america-catalogs.ps1 @@ -230,6 +230,7 @@ $DeviceSubTitleOverrides = @{ 'heltec_v4_tft' = 'TFT' 'heltec_v4_expansionkit' = 'Expansion Kit' 'heltec_v4_expansionkit_tft' = 'Expansion Kit TFT' + 'LilyGo_TBeam_1W' = '1W PA pins: DIO1 1, NSS 15, BUSY 38' 'LilyGo_T-Echo-Lite_non_shell' = 'Non shell' 'ikoka_nano_nrf_22dbm' = '22 dBm' 'ikoka_nano_nrf_30dbm' = '30 dBm' @@ -237,6 +238,12 @@ $DeviceSubTitleOverrides = @{ 'ikoka_stick_nrf_22dbm' = '22 dBm' 'ikoka_stick_nrf_30dbm' = '30 dBm' 'ikoka_stick_nrf_33dbm' = '33 dBm' + 'Xiao_S3' = 'SX1262 pins: DIO1 2, NSS 5, BUSY 4' +} + +$DeviceSelectionNotes = @{ + 'LilyGo_TBeam_1W' = 'Board selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.' + 'Xiao_S3' = 'Board selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.' } function ConvertTo-DeviceName { @@ -288,6 +295,22 @@ function Join-SubTitle { return ($parts -join ', ') } +function Join-VersionNotes { + param( + [string]$Description, + [string[]]$DeviceKeys + ) + + $parts = @($Description) + foreach ($deviceKey in @($DeviceKeys | Sort-Object -Unique)) { + if ($DeviceSelectionNotes.ContainsKey($deviceKey)) { + $parts += $DeviceSelectionNotes[$deviceKey] + } + } + + return ($parts -join "`n`n") +} + function Get-RoleInfo { param([string]$DeviceRole) @@ -456,12 +479,13 @@ function New-Catalog { } if ($first.SubTitle) { - $firmwareEntry.subTitle = $first.SubTitle + $firmwareEntry.subTitle = $first.SubTitle } + $versionNotes = Join-VersionNotes $Definition.Description @($roleGroup.Group | ForEach-Object { $_.DeviceKey }) $firmwareEntry.version = [ordered]@{ $Definition.Tag = [ordered]@{ - notes = $Definition.Description + notes = $versionNotes files = $files } } diff --git a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json index cfb9e9c8..649e24b5 100644 --- a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json @@ -4549,9 +4549,10 @@ { "role": "companionUsb", "title": "Companion USB", + "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.", "files": [ { "type": "flash-wipe", @@ -4572,9 +4573,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.", "files": [ { "type": "flash-wipe", @@ -4595,9 +4597,10 @@ { "role": "repeater", "title": "Repeater", + "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.", "files": [ { "type": "flash-wipe", @@ -4618,9 +4621,10 @@ { "role": "repeaterBridgeEspnow", "title": "Repeater Bridge ESP-NOW", + "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.", "files": [ { "type": "flash-wipe", @@ -4641,9 +4645,10 @@ { "role": "roomServer", "title": "Room Server", + "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.", "files": [ { "type": "flash-wipe", @@ -7859,9 +7864,10 @@ { "role": "companionUsb", "title": "Companion USB", + "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.", "files": [ { "type": "flash-wipe", @@ -7882,9 +7888,10 @@ { "role": "repeater", "title": "Repeater", + "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.", "files": [ { "type": "flash-wipe", @@ -7905,9 +7912,10 @@ { "role": "repeaterBridgeEspnow", "title": "Repeater Bridge ESP-NOW", + "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.", "files": [ { "type": "flash-wipe", @@ -7928,9 +7936,10 @@ { "role": "roomServer", "title": "Room Server", + "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.", "files": [ { "type": "flash-wipe", @@ -7951,9 +7960,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.", "files": [ { "type": "flash-wipe", diff --git a/mesh-america/keymind-cascade-v1.16.0-provider.json b/mesh-america/keymind-cascade-v1.16.0-provider.json index ee79902c..614bf828 100644 --- a/mesh-america/keymind-cascade-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-v1.16.0-provider.json @@ -5939,9 +5939,10 @@ { "role": "companionBle", "title": "Companion BLE", + "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.", "files": [ { "type": "flash-wipe", @@ -5962,10 +5963,10 @@ { "role": "companionBle", "title": "Companion BLE", - "subTitle": "Power saving", + "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38, Power saving", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.", "files": [ { "type": "flash-wipe", @@ -5986,9 +5987,10 @@ { "role": "companionUsb", "title": "Companion USB", + "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.", "files": [ { "type": "flash-wipe", @@ -6009,9 +6011,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.", "files": [ { "type": "flash-wipe", @@ -6032,9 +6035,10 @@ { "role": "repeater", "title": "Repeater", + "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.", "files": [ { "type": "flash-wipe", @@ -6055,9 +6059,10 @@ { "role": "repeaterBridgeEspnow", "title": "Repeater Bridge ESP-NOW", + "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.", "files": [ { "type": "flash-wipe", @@ -6078,9 +6083,10 @@ { "role": "roomServer", "title": "Room Server", + "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.", "files": [ { "type": "flash-wipe", @@ -10036,9 +10042,10 @@ { "role": "companionBle", "title": "Companion BLE", + "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.", "files": [ { "type": "flash-wipe", @@ -10059,10 +10066,10 @@ { "role": "companionBle", "title": "Companion BLE", - "subTitle": "Power saving", + "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4, Power saving", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.", "files": [ { "type": "flash-wipe", @@ -10083,9 +10090,10 @@ { "role": "companionUsb", "title": "Companion USB", + "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.", "files": [ { "type": "flash-wipe", @@ -10106,9 +10114,10 @@ { "role": "repeater", "title": "Repeater", + "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.", "files": [ { "type": "flash-wipe", @@ -10129,9 +10138,10 @@ { "role": "repeaterBridgeEspnow", "title": "Repeater Bridge ESP-NOW", + "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.", "files": [ { "type": "flash-wipe", @@ -10152,9 +10162,10 @@ { "role": "roomServer", "title": "Room Server", + "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.", "files": [ { "type": "flash-wipe", @@ -10175,9 +10186,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.", "files": [ { "type": "flash-wipe", From 60a4dee3aa1a028275f5cb51ce663fe72e3b19d3 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 30 Jun 2026 23:54:01 -0700 Subject: [PATCH 189/214] Map bridge builds to repeater role --- .../generate-mesh-america-catalogs.ps1 | 14 +- ...mind-cascade-logging-v1.16.0-provider.json | 328 +++++++++--------- .../keymind-cascade-v1.16.0-provider.json | 328 +++++++++--------- 3 files changed, 328 insertions(+), 342 deletions(-) diff --git a/mesh-america/generate-mesh-america-catalogs.ps1 b/mesh-america/generate-mesh-america-catalogs.ps1 index 80ad76fa..654732ad 100644 --- a/mesh-america/generate-mesh-america-catalogs.ps1 +++ b/mesh-america/generate-mesh-america-catalogs.ps1 @@ -23,14 +23,12 @@ $Catalogs = @( $RoleDefinitions = [ordered]@{ companionWifi = [ordered]@{ name = 'Companion WiFi' } - repeaterBridgeEspnow = [ordered]@{ name = 'Repeater Bridge ESP-NOW' } - repeaterBridgeRs232 = [ordered]@{ name = 'Repeater Bridge RS232' } sensor = [ordered]@{ name = 'Sensor' } terminalChat = [ordered]@{ name = 'Terminal Chat' } } $RolePatterns = @( - @{ Suffix = 'logging_repeater_bridge_espnow'; Role = 'repeaterBridgeEspnow'; Title = 'Repeater Bridge ESP-NOW'; SubTitle = 'Logging' }, + @{ Suffix = 'logging_repeater_bridge_espnow'; Role = 'repeater'; Title = 'Repeater Bridge ESP-NOW'; SubTitle = 'Logging' }, @{ Suffix = 'logging_repeater'; Role = 'repeater'; Title = 'Repeater'; SubTitle = 'Logging' }, @{ Suffix = 'companion_radio_ble_femoff'; Role = 'companionBle'; Title = 'Companion BLE'; SubTitle = 'FEM off' }, @{ Suffix = 'companion_radio_ble_femon'; Role = 'companionBle'; Title = 'Companion BLE'; SubTitle = 'FEM on' }, @@ -46,11 +44,11 @@ $RolePatterns = @( @{ Suffix = 'comp_radio_usb'; Role = 'companionUsb'; Title = 'Companion USB'; SubTitle = $null }, @{ Suffix = 'companion_radio_serial'; Role = 'companionUsb'; Title = 'Companion USB'; SubTitle = 'Serial' }, @{ Suffix = 'companion_radio_wifi'; Role = 'companionWifi'; Title = 'Companion WiFi'; SubTitle = $null }, - @{ Suffix = 'repeater_bridge_rs232_serial1'; Role = 'repeaterBridgeRs232'; Title = 'Repeater Bridge RS232'; SubTitle = 'Serial 1' }, - @{ Suffix = 'repeater_bridge_rs232_serial2'; Role = 'repeaterBridgeRs232'; Title = 'Repeater Bridge RS232'; SubTitle = 'Serial 2' }, - @{ Suffix = 'repeater_bridge_rs232'; Role = 'repeaterBridgeRs232'; Title = 'Repeater Bridge RS232'; SubTitle = $null }, - @{ Suffix = 'repeater_bridge_espnow_'; Role = 'repeaterBridgeEspnow'; Title = 'Repeater Bridge ESP-NOW'; SubTitle = $null }, - @{ Suffix = 'repeater_bridge_espnow'; Role = 'repeaterBridgeEspnow'; Title = 'Repeater Bridge ESP-NOW'; SubTitle = $null }, + @{ Suffix = 'repeater_bridge_rs232_serial1'; Role = 'repeater'; Title = 'Repeater Bridge RS232'; SubTitle = 'Serial 1' }, + @{ Suffix = 'repeater_bridge_rs232_serial2'; Role = 'repeater'; Title = 'Repeater Bridge RS232'; SubTitle = 'Serial 2' }, + @{ Suffix = 'repeater_bridge_rs232'; Role = 'repeater'; Title = 'Repeater Bridge RS232'; SubTitle = $null }, + @{ Suffix = 'repeater_bridge_espnow_'; Role = 'repeater'; Title = 'Repeater Bridge ESP-NOW'; SubTitle = $null }, + @{ Suffix = 'repeater_bridge_espnow'; Role = 'repeater'; Title = 'Repeater Bridge ESP-NOW'; SubTitle = $null }, @{ Suffix = 'Repeater'; Role = 'repeater'; Title = 'Repeater'; SubTitle = $null }, @{ Suffix = 'repeater_'; Role = 'repeater'; Title = 'Repeater'; SubTitle = $null }, @{ Suffix = 'repeatr'; Role = 'repeater'; Title = 'Repeater'; SubTitle = $null }, diff --git a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json index 649e24b5..5f3158f2 100644 --- a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json @@ -75,12 +75,6 @@ "companionWifi": { "name": "Companion WiFi" }, - "repeaterBridgeEspnow": { - "name": "Repeater Bridge ESP-NOW" - }, - "repeaterBridgeRs232": { - "name": "Repeater Bridge RS232" - }, "sensor": { "name": "Sensor" }, @@ -363,7 +357,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -608,7 +602,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -988,7 +982,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -1041,7 +1035,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -1216,7 +1210,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -1315,7 +1309,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -1414,7 +1408,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -1789,30 +1783,6 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "Without display", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeaterBridgeRs232", "title": "Repeater Bridge RS232", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -1835,7 +1805,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "subTitle": "Without display", "version": { @@ -1858,6 +1828,30 @@ } } }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "Without display", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, { "role": "roomServer", "title": "Room Server", @@ -1982,7 +1976,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -2127,7 +2121,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -2150,7 +2144,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -2367,30 +2361,6 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "TFT", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -2413,7 +2383,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "subTitle": "TFT", "version": { @@ -2436,6 +2406,30 @@ } } }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "TFT", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "roomServer", "title": "Room Server", @@ -2662,7 +2656,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -2761,7 +2755,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -2860,7 +2854,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -2959,7 +2953,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -3081,7 +3075,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -3249,7 +3243,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -3272,7 +3266,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -4052,7 +4046,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -4075,7 +4069,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -4197,7 +4191,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -4319,7 +4313,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -4418,7 +4412,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -4494,7 +4488,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -4596,30 +4590,6 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", "title": "Repeater Bridge ESP-NOW", "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", "version": { @@ -4642,6 +4612,30 @@ } } }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "roomServer", "title": "Room Server", @@ -4720,7 +4714,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -5336,7 +5330,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -5458,7 +5452,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -5884,7 +5878,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -6006,7 +6000,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "subTitle": "Serial 1", "version": { @@ -6152,7 +6146,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -6350,7 +6344,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "subTitle": "Serial 1", "version": { @@ -6374,7 +6368,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "subTitle": "Serial 2", "version": { @@ -6543,7 +6537,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -6566,7 +6560,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -7031,7 +7025,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -7452,7 +7446,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -7887,30 +7881,6 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", "title": "Repeater Bridge ESP-NOW", "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", "version": { @@ -7933,6 +7903,30 @@ } } }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "roomServer", "title": "Room Server", @@ -8082,7 +8076,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -8204,7 +8198,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -8257,7 +8251,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -8509,30 +8503,6 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "Logging", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Station_G2_logging_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_logging_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Station_G2_logging_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_logging_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", "title": "Repeater Bridge ESP-NOW", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -8555,7 +8525,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "subTitle": "Logging", "version": { @@ -8578,6 +8548,30 @@ } } }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "Logging", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G2_logging_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_logging_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G2_logging_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_logging_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "roomServer", "title": "Room Server", diff --git a/mesh-america/keymind-cascade-v1.16.0-provider.json b/mesh-america/keymind-cascade-v1.16.0-provider.json index 614bf828..f08c3f9e 100644 --- a/mesh-america/keymind-cascade-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-v1.16.0-provider.json @@ -75,12 +75,6 @@ "companionWifi": { "name": "Companion WiFi" }, - "repeaterBridgeEspnow": { - "name": "Repeater Bridge ESP-NOW" - }, - "repeaterBridgeRs232": { - "name": "Repeater Bridge RS232" - }, "sensor": { "name": "Sensor" }, @@ -432,7 +426,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -747,7 +741,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -1226,7 +1220,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -1279,7 +1273,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -1501,7 +1495,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -1647,7 +1641,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -1817,7 +1811,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -2308,30 +2302,6 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "Without display", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeaterBridgeRs232", "title": "Repeater Bridge RS232", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -2354,7 +2324,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "subTitle": "Without display", "version": { @@ -2377,6 +2347,30 @@ } } }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "Without display", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Heltec_t114_without_display_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, { "role": "roomServer", "title": "Room Server", @@ -2548,7 +2542,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -2740,7 +2734,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -2763,7 +2757,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -3099,30 +3093,6 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "TFT", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -3145,7 +3115,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "subTitle": "TFT", "version": { @@ -3168,6 +3138,30 @@ } } }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "TFT", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_tft_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "roomServer", "title": "Room Server", @@ -3441,7 +3435,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -3563,7 +3557,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -3685,7 +3679,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -3831,7 +3825,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -4000,7 +3994,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -4215,7 +4209,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -4238,7 +4232,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -5278,7 +5272,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -5301,7 +5295,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -5470,7 +5464,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -5615,7 +5609,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -5761,7 +5755,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -5884,7 +5878,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -6034,30 +6028,6 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.", - "files": [ - { - "type": "flash-wipe", - "name": "LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", "title": "Repeater Bridge ESP-NOW", "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", "version": { @@ -6080,6 +6050,30 @@ } } }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.", + "files": [ + { + "type": "flash-wipe", + "name": "LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/LilyGo_TBeam_1W_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "roomServer", "title": "Room Server", @@ -6205,7 +6199,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -7052,7 +7046,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -7197,7 +7191,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -7738,7 +7732,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -7883,7 +7877,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "subTitle": "Serial 1", "version": { @@ -8029,7 +8023,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -8250,7 +8244,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "subTitle": "Serial 1", "version": { @@ -8274,7 +8268,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "subTitle": "Serial 2", "version": { @@ -8466,7 +8460,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -8489,7 +8483,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -9000,7 +8994,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -9537,7 +9531,7 @@ } }, { - "role": "repeaterBridgeRs232", + "role": "repeater", "title": "Repeater Bridge RS232", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -10113,30 +10107,6 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.", - "files": [ - { - "type": "flash-wipe", - "name": "Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", "title": "Repeater Bridge ESP-NOW", "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", "version": { @@ -10159,6 +10129,30 @@ } } }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.", + "files": [ + { + "type": "flash-wipe", + "name": "Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Xiao_S3_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "roomServer", "title": "Room Server", @@ -10355,7 +10349,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -10477,7 +10471,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -10530,7 +10524,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -10828,30 +10822,6 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "Logging", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Station_G2_logging_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_logging_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Station_G2_logging_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_logging_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeaterBridgeEspnow", "title": "Repeater Bridge ESP-NOW", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -10874,7 +10844,7 @@ } }, { - "role": "repeaterBridgeEspnow", + "role": "repeater", "title": "Repeater Bridge ESP-NOW", "subTitle": "Logging", "version": { @@ -10897,6 +10867,30 @@ } } }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "Logging", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Station_G2_logging_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_logging_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Station_G2_logging_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Station_G2_logging_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "roomServer", "title": "Room Server", From 29842afcc01c7d290c85fd6dbb18f5e755ff1a36 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Wed, 1 Jul 2026 00:04:12 -0700 Subject: [PATCH 190/214] Describe custom Mesh America roles --- .../generate-mesh-america-catalogs.ps1 | 35 ++- ...mind-cascade-logging-v1.16.0-provider.json | 241 +++++++++++------- .../keymind-cascade-v1.16.0-provider.json | 241 +++++++++++------- 3 files changed, 327 insertions(+), 190 deletions(-) diff --git a/mesh-america/generate-mesh-america-catalogs.ps1 b/mesh-america/generate-mesh-america-catalogs.ps1 index 654732ad..1f52ee3e 100644 --- a/mesh-america/generate-mesh-america-catalogs.ps1 +++ b/mesh-america/generate-mesh-america-catalogs.ps1 @@ -22,9 +22,18 @@ $Catalogs = @( ) $RoleDefinitions = [ordered]@{ - companionWifi = [ordered]@{ name = 'Companion WiFi' } - sensor = [ordered]@{ name = 'Sensor' } - terminalChat = [ordered]@{ name = 'Terminal Chat' } + companionWifi = [ordered]@{ + name = 'Companion WiFi' + description = 'Companion radio build that exposes the MeshCore companion interface over Wi-Fi instead of BLE or USB.' + } + sensor = [ordered]@{ + name = 'Sensor' + description = 'Sensor and telemetry build for boards that read and publish sensor data on the mesh.' + } + terminalChat = [ordered]@{ + name = 'Terminal Chat' + description = 'Standalone serial terminal chat build for text messaging from a terminal console.' + } } $RolePatterns = @( @@ -43,7 +52,7 @@ $RolePatterns = @( @{ Suffix = 'companion_usb'; Role = 'companionUsb'; Title = 'Companion USB'; SubTitle = $null }, @{ Suffix = 'comp_radio_usb'; Role = 'companionUsb'; Title = 'Companion USB'; SubTitle = $null }, @{ Suffix = 'companion_radio_serial'; Role = 'companionUsb'; Title = 'Companion USB'; SubTitle = 'Serial' }, - @{ Suffix = 'companion_radio_wifi'; Role = 'companionWifi'; Title = 'Companion WiFi'; SubTitle = $null }, + @{ Suffix = 'companion_radio_wifi'; Role = 'companionWifi'; Title = 'Companion WiFi'; SubTitle = 'Wi-Fi companion interface' }, @{ Suffix = 'repeater_bridge_rs232_serial1'; Role = 'repeater'; Title = 'Repeater Bridge RS232'; SubTitle = 'Serial 1' }, @{ Suffix = 'repeater_bridge_rs232_serial2'; Role = 'repeater'; Title = 'Repeater Bridge RS232'; SubTitle = 'Serial 2' }, @{ Suffix = 'repeater_bridge_rs232'; Role = 'repeater'; Title = 'Repeater Bridge RS232'; SubTitle = $null }, @@ -56,8 +65,8 @@ $RolePatterns = @( @{ Suffix = 'room_server_'; Role = 'roomServer'; Title = 'Room Server'; SubTitle = $null }, @{ Suffix = 'room_server'; Role = 'roomServer'; Title = 'Room Server'; SubTitle = $null }, @{ Suffix = 'room_svr'; Role = 'roomServer'; Title = 'Room Server'; SubTitle = $null }, - @{ Suffix = 'terminal_chat'; Role = 'terminalChat'; Title = 'Terminal Chat'; SubTitle = $null }, - @{ Suffix = 'sensor'; Role = 'sensor'; Title = 'Sensor'; SubTitle = $null } + @{ Suffix = 'terminal_chat'; Role = 'terminalChat'; Title = 'Terminal Chat'; SubTitle = 'Serial terminal chat' }, + @{ Suffix = 'sensor'; Role = 'sensor'; Title = 'Sensor'; SubTitle = 'Sensor telemetry node' } ) $MakerDefinitions = [ordered]@{ @@ -244,6 +253,12 @@ $DeviceSelectionNotes = @{ 'Xiao_S3' = 'Board selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.' } +$RoleSelectionNotes = @{ + companionWifi = 'Role note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.' + sensor = 'Role note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.' + terminalChat = 'Role note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.' +} + function ConvertTo-DeviceName { param([string]$Prefix) if ($DeviceNameOverrides.ContainsKey($Prefix)) { @@ -296,7 +311,8 @@ function Join-SubTitle { function Join-VersionNotes { param( [string]$Description, - [string[]]$DeviceKeys + [string[]]$DeviceKeys, + [string]$Role ) $parts = @($Description) @@ -305,6 +321,9 @@ function Join-VersionNotes { $parts += $DeviceSelectionNotes[$deviceKey] } } + if ($RoleSelectionNotes.ContainsKey($Role)) { + $parts += $RoleSelectionNotes[$Role] + } return ($parts -join "`n`n") } @@ -480,7 +499,7 @@ function New-Catalog { $firmwareEntry.subTitle = $first.SubTitle } - $versionNotes = Join-VersionNotes $Definition.Description @($roleGroup.Group | ForEach-Object { $_.DeviceKey }) + $versionNotes = Join-VersionNotes -Description $Definition.Description -DeviceKeys @($roleGroup.Group | ForEach-Object { $_.DeviceKey }) -Role $first.Role $firmwareEntry.version = [ordered]@{ $Definition.Tag = [ordered]@{ notes = $versionNotes diff --git a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json index 5f3158f2..ad2e3062 100644 --- a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json @@ -73,13 +73,16 @@ }, "role": { "companionWifi": { - "name": "Companion WiFi" + "name": "Companion WiFi", + "description": "Companion radio build that exposes the MeshCore companion interface over Wi-Fi instead of BLE or USB." }, "sensor": { - "name": "Sensor" + "name": "Sensor", + "description": "Sensor and telemetry build for boards that read and publish sensor data on the mesh." }, "terminalChat": { - "name": "Terminal Chat" + "name": "Terminal Chat", + "description": "Standalone serial terminal chat build for text messaging from a terminal console." } }, "device": [ @@ -160,9 +163,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -313,9 +317,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -405,9 +410,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -558,9 +564,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -650,9 +657,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -1136,9 +1144,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -1235,9 +1244,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash-wipe", @@ -1456,9 +1466,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash", @@ -1932,9 +1943,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -2024,9 +2036,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -2077,9 +2090,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -2192,9 +2206,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash-wipe", @@ -2215,9 +2230,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -2292,33 +2308,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "TFT, Wi-Fi companion interface", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "subTitle": "TFT", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -2336,6 +2329,30 @@ } } }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_wifi-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "repeater", "title": "Repeater", @@ -2480,9 +2497,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash-wipe", @@ -2503,10 +2521,10 @@ { "role": "sensor", "title": "Sensor", - "subTitle": "TFT", + "subTitle": "TFT, Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash-wipe", @@ -2527,9 +2545,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -2550,10 +2569,10 @@ { "role": "terminalChat", "title": "Terminal Chat", - "subTitle": "TFT", + "subTitle": "TFT, Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -3031,9 +3050,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -3123,9 +3143,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash-wipe", @@ -3146,9 +3167,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -3199,9 +3221,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -3314,9 +3337,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash-wipe", @@ -4002,9 +4026,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -4117,9 +4142,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -4239,9 +4265,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -4361,9 +4388,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -4567,10 +4595,10 @@ { "role": "companionWifi", "title": "Companion WiFi", - "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", + "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38, Wi-Fi companion interface", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -4670,9 +4698,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -5378,9 +5407,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -5500,9 +5530,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -5659,9 +5690,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash", @@ -5758,9 +5790,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash", @@ -5781,9 +5814,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash", @@ -5834,9 +5868,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -5926,9 +5961,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -6049,9 +6085,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash", @@ -6072,9 +6109,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash", @@ -6194,9 +6232,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "download", @@ -6270,9 +6309,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "download", @@ -6417,9 +6457,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash", @@ -6440,9 +6481,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash", @@ -6493,9 +6535,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -6608,9 +6651,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash-wipe", @@ -6631,9 +6675,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -6730,9 +6775,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash", @@ -6753,9 +6799,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash", @@ -6852,9 +6899,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash", @@ -6951,9 +6999,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "download", @@ -7073,9 +7122,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "download", @@ -7524,9 +7574,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "download", @@ -7577,9 +7628,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -7828,9 +7880,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "download", @@ -7954,10 +8007,10 @@ { "role": "sensor", "title": "Sensor", - "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", + "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4, Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash-wipe", @@ -8032,9 +8085,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -8124,9 +8178,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash-wipe", @@ -8147,9 +8202,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -8329,9 +8385,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "download", @@ -8458,9 +8515,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -8628,9 +8686,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", diff --git a/mesh-america/keymind-cascade-v1.16.0-provider.json b/mesh-america/keymind-cascade-v1.16.0-provider.json index f08c3f9e..769dbe4d 100644 --- a/mesh-america/keymind-cascade-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-v1.16.0-provider.json @@ -73,13 +73,16 @@ }, "role": { "companionWifi": { - "name": "Companion WiFi" + "name": "Companion WiFi", + "description": "Companion radio build that exposes the MeshCore companion interface over Wi-Fi instead of BLE or USB." }, "sensor": { - "name": "Sensor" + "name": "Sensor", + "description": "Sensor and telemetry build for boards that read and publish sensor data on the mesh." }, "terminalChat": { - "name": "Terminal Chat" + "name": "Terminal Chat", + "description": "Standalone serial terminal chat build for text messaging from a terminal console." } }, "device": [ @@ -183,9 +186,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -382,9 +386,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -474,9 +479,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -697,9 +703,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -789,9 +796,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -1374,9 +1382,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -1520,9 +1529,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash-wipe", @@ -1859,9 +1869,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash", @@ -2498,9 +2509,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -2590,9 +2602,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -2690,9 +2703,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -2805,9 +2819,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash-wipe", @@ -2828,9 +2843,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -3024,33 +3040,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "TFT, Wi-Fi companion interface", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "heltec_v4_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "heltec_v4_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "companionWifi", - "title": "Companion WiFi", - "subTitle": "TFT", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -3068,6 +3061,30 @@ } } }, + { + "role": "companionWifi", + "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", + "files": [ + { + "type": "flash-wipe", + "name": "heltec_v4_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "heltec_v4_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/heltec_v4_companion_radio_wifi-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, { "role": "repeater", "title": "Repeater", @@ -3212,9 +3229,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash-wipe", @@ -3235,10 +3253,10 @@ { "role": "sensor", "title": "Sensor", - "subTitle": "TFT", + "subTitle": "TFT, Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash-wipe", @@ -3259,9 +3277,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -3282,10 +3301,10 @@ { "role": "terminalChat", "title": "Terminal Chat", - "subTitle": "TFT", + "subTitle": "TFT, Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -3950,9 +3969,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -4042,9 +4062,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash-wipe", @@ -4065,9 +4086,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -4165,9 +4187,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -4280,9 +4303,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash-wipe", @@ -5228,9 +5252,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -5343,9 +5368,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -5512,9 +5538,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -5657,9 +5684,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -6005,10 +6033,10 @@ { "role": "companionWifi", "title": "Companion WiFi", - "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38", + "subTitle": "1W PA pins: DIO1 1, NSS 15, BUSY 38, Wi-Fi companion interface", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -6155,9 +6183,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -7094,9 +7123,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -7239,9 +7269,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -7467,9 +7498,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash", @@ -7589,9 +7621,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash", @@ -7612,9 +7645,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash", @@ -7688,9 +7722,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -7780,9 +7815,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -7926,9 +7962,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash", @@ -7949,9 +7986,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash", @@ -8071,9 +8109,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "download", @@ -8147,9 +8186,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "download", @@ -8317,9 +8357,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash", @@ -8340,9 +8381,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash", @@ -8416,9 +8458,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -8531,9 +8574,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash-wipe", @@ -8554,9 +8598,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -8676,9 +8721,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash", @@ -8699,9 +8745,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash", @@ -8821,9 +8868,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash", @@ -8920,9 +8968,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "download", @@ -9042,9 +9091,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "download", @@ -9609,9 +9659,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "download", @@ -9709,9 +9760,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -10006,9 +10058,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "download", @@ -10180,10 +10233,10 @@ { "role": "sensor", "title": "Sensor", - "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4", + "subTitle": "SX1262 pins: DIO1 2, NSS 5, BUSY 4, Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nBoard selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash-wipe", @@ -10305,9 +10358,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -10397,9 +10451,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "flash-wipe", @@ -10420,9 +10475,10 @@ { "role": "terminalChat", "title": "Terminal Chat", + "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", "files": [ { "type": "flash-wipe", @@ -10602,9 +10658,10 @@ { "role": "sensor", "title": "Sensor", + "subTitle": "Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Sensor builds publish board-supported sensor or telemetry data on the mesh. They are flashable custom-role builds, not standard companion or repeater firmware.", "files": [ { "type": "download", @@ -10777,9 +10834,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", @@ -10970,9 +11028,10 @@ { "role": "companionWifi", "title": "Companion WiFi", + "subTitle": "Wi-Fi companion interface", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Companion WiFi exposes the MeshCore companion interface over Wi-Fi rather than BLE or USB. Use it for boards that should be controlled over a local Wi-Fi network. It is flashable, but the validator treats it as a custom role, so the standard Configure step may not appear.", "files": [ { "type": "flash-wipe", From 4f7c1cb88f25578633ebd7bc5755f71d888c168b Mon Sep 17 00:00:00 2001 From: Christoph Koehler Date: Wed, 17 Jun 2026 08:55:54 -0600 Subject: [PATCH 191/214] build: add gcc, gtest to nix shell, fix native test framework --- default.nix | 7 ++++--- platformio.ini | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/default.nix b/default.nix index 4f9e1c59..13d410de 100644 --- a/default.nix +++ b/default.nix @@ -1,11 +1,12 @@ -{ pkgs ? import {} }: -let +{pkgs ? import {}}: let in pkgs.mkShell { buildInputs = [ pkgs.platformio pkgs.python3 + pkgs.gcc + pkgs.gtest # optional: needed as a programmer i.e. for esp32 pkgs.avrdude ]; -} + } diff --git a/platformio.ini b/platformio.ini index e16f7b83..b08854e5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -157,6 +157,7 @@ lib_deps = [env:native] platform = native +test_framework = googletest build_flags = -std=c++17 -I src -I test/mocks From 7e4c4606034d270c4bcca4b28db1a7c7bb3b8aa9 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Wed, 1 Jul 2026 09:51:03 -0700 Subject: [PATCH 192/214] Group Ikoka Handheld variants --- .../generate-mesh-america-catalogs.ps1 | 7 +- ...mind-cascade-logging-v1.16.0-provider.json | 110 ++++++------ .../keymind-cascade-v1.16.0-provider.json | 158 +++++++++--------- 3 files changed, 130 insertions(+), 145 deletions(-) diff --git a/mesh-america/generate-mesh-america-catalogs.ps1 b/mesh-america/generate-mesh-america-catalogs.ps1 index 1f52ee3e..cb733e19 100644 --- a/mesh-america/generate-mesh-america-catalogs.ps1 +++ b/mesh-america/generate-mesh-america-catalogs.ps1 @@ -167,8 +167,8 @@ $DeviceNameOverrides = @{ 'ikoka_nano_nrf_30dbm' = 'Ikoka Nano' 'ikoka_nano_nrf_33dbm' = 'Ikoka Nano' 'ikoka_handheld_nrf_e22_30dbm' = 'Ikoka Handheld nRF E22 30 dBm' - 'ikoka_handheld_nrf_e22_30dbm_096' = 'Ikoka Handheld nRF E22 30 dBm 0.96' - 'ikoka_handheld_nrf_e22_30dbm_096_rotated' = 'Ikoka Handheld nRF E22 30 dBm 0.96 Rotated' + 'ikoka_handheld_nrf_e22_30dbm_096' = 'Ikoka Handheld nRF E22 30 dBm' + 'ikoka_handheld_nrf_e22_30dbm_096_rotated' = 'Ikoka Handheld nRF E22 30 dBm' 'ikoka_stick_nrf_22dbm' = 'Ikoka Stick' 'ikoka_stick_nrf_30dbm' = 'Ikoka Stick' 'ikoka_stick_nrf_33dbm' = 'Ikoka Stick' @@ -242,6 +242,9 @@ $DeviceSubTitleOverrides = @{ 'ikoka_nano_nrf_22dbm' = '22 dBm' 'ikoka_nano_nrf_30dbm' = '30 dBm' 'ikoka_nano_nrf_33dbm' = '33 dBm' + 'ikoka_handheld_nrf_e22_30dbm' = 'No display' + 'ikoka_handheld_nrf_e22_30dbm_096' = '0.96 display' + 'ikoka_handheld_nrf_e22_30dbm_096_rotated' = '0.96 display rotated' 'ikoka_stick_nrf_22dbm' = '22 dBm' 'ikoka_stick_nrf_30dbm' = '30 dBm' 'ikoka_stick_nrf_33dbm' = '33 dBm' diff --git a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json index ad2e3062..7c616444 100644 --- a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json @@ -3365,9 +3365,58 @@ "name": "Ikoka Handheld nRF E22 30 dBm", "type": "nrf52", "firmware": [ + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "0.96 display", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "0.96 display rotated", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, { "role": "repeater", "title": "Repeater", + "subTitle": "No display", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3391,6 +3440,7 @@ { "role": "roomServer", "title": "Room Server", + "subTitle": "No display", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3413,66 +3463,6 @@ } ] }, - { - "maker": "Ikoka", - "name": "Ikoka Handheld nRF E22 30 dBm 0.96", - "type": "nrf52", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "Ikoka", - "name": "Ikoka Handheld nRF E22 30 dBm 0.96 Rotated", - "type": "nrf52", - "firmware": [ - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, { "maker": "Ikoka", "name": "Ikoka Nano", diff --git a/mesh-america/keymind-cascade-v1.16.0-provider.json b/mesh-america/keymind-cascade-v1.16.0-provider.json index 769dbe4d..c336ec5f 100644 --- a/mesh-america/keymind-cascade-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-v1.16.0-provider.json @@ -4330,63 +4330,11 @@ "maker": "Ikoka", "name": "Ikoka Handheld nRF E22 30 dBm", "type": "nrf52", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_handheld_nrf_e22_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_handheld_nrf_e22_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_handheld_nrf_e22_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_handheld_nrf_e22_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "Ikoka", - "name": "Ikoka Handheld nRF E22 30 dBm 0.96", - "type": "nrf52", "firmware": [ { "role": "companionBle", "title": "Companion BLE", + "subTitle": "0.96 display", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4407,39 +4355,10 @@ } } }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - } - ] - }, - { - "maker": "Ikoka", - "name": "Ikoka Handheld nRF E22 30 dBm 0.96 Rotated", - "type": "nrf52", - "firmware": [ { "role": "companionBle", "title": "Companion BLE", + "subTitle": "0.96 display rotated", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4463,6 +4382,31 @@ { "role": "companionUsb", "title": "Companion USB", + "subTitle": "0.96 display", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion USB", + "subTitle": "0.96 display rotated", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4482,6 +4426,54 @@ ] } } + }, + { + "role": "repeater", + "title": "Repeater", + "subTitle": "No display", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_handheld_nrf_e22_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_handheld_nrf_e22_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server", + "subTitle": "No display", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_handheld_nrf_e22_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_handheld_nrf_e22_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_handheld_nrf_e22_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } } ] }, From 6c27c00fbc436b20996f7d5abb9021dbff82fb22 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Wed, 1 Jul 2026 09:55:35 -0700 Subject: [PATCH 193/214] Group radio variant device tiles --- .../generate-mesh-america-catalogs.ps1 | 81 ++- ...mind-cascade-logging-v1.16.0-provider.json | 502 +++++++-------- .../keymind-cascade-v1.16.0-provider.json | 574 ++++++++---------- 3 files changed, 550 insertions(+), 607 deletions(-) diff --git a/mesh-america/generate-mesh-america-catalogs.ps1 b/mesh-america/generate-mesh-america-catalogs.ps1 index cb733e19..ed29c6d0 100644 --- a/mesh-america/generate-mesh-america-catalogs.ps1 +++ b/mesh-america/generate-mesh-america-catalogs.ps1 @@ -144,8 +144,8 @@ $DeviceNameOverrides = @{ 'GAT562_Mesh_EVB_Pro' = 'GAT-IoT GAT562 EVB Pro' 'GAT562_Mesh_Tracker_Pro' = 'GAT-IoT GAT562 Tracker' 'GAT562_Mesh_Watch13' = 'GAT-IoT GAT562 Watch13' - 'Generic_E22_sx1262' = 'Generic E22 SX1262' - 'Generic_E22_sx1268' = 'Generic E22 SX1268' + 'Generic_E22_sx1262' = 'Generic E22' + 'Generic_E22_sx1268' = 'Generic E22' 'Generic_ESPNOW' = 'Generic ESP-NOW' 'Heltec_ct62' = 'Heltec CT62' 'Heltec_E213' = 'Heltec Vision Master E213' @@ -184,8 +184,8 @@ $DeviceNameOverrides = @{ 'LilyGo_TLora_V2_1_1_6' = 'LilyGo LoRa32 V2.1_1.6' 'LilyGo_Tlora_C6' = 'LilyGo T-LoRa C6' 'M5Stack_Unit_C6L' = 'M5Stack Unit C6L' - 'Meshadventurer_sx1262' = 'Meshadventurer SX1262' - 'Meshadventurer_sx1268' = 'Meshadventurer SX1268' + 'Meshadventurer_sx1262' = 'Meshadventurer' + 'Meshadventurer_sx1268' = 'Meshadventurer' 'Meshimi' = 'Meshimi' 'Meshtiny' = 'Meshtiny' 'Mesh_pocket' = 'Heltec MeshPocket' @@ -208,8 +208,8 @@ $DeviceNameOverrides = @{ 't1000e' = 'Seeed Studio SenseCAP T1000-E' 'Tbeam_SX1262' = 'LilyGo T-Beam (SX1262)' 'Tbeam_SX1276' = 'LilyGo T-Beam 1.2 (SX1276)' - 'Tenstar_C3_sx1262' = 'Tenstar C3 SX1262' - 'Tenstar_C3_sx1268' = 'Tenstar C3 SX1268' + 'Tenstar_C3_sx1262' = 'Tenstar C3' + 'Tenstar_C3_sx1268' = 'Tenstar C3' 'ThinkNode_M1' = 'Elecrow ThinkNode M1' 'ThinkNode_M2' = 'Elecrow ThinkNode M2' 'ThinkNode_M3' = 'Elecrow ThinkNode M3' @@ -220,7 +220,7 @@ $DeviceNameOverrides = @{ 'WHY2025_badge' = 'WHY2025 Badge' 'wio_wm1110' = 'Seeed Studio Wio WM1110' 'wio-e5' = 'Seeed Studio Wio-E5' - 'wio-e5-mini' = 'Seeed Studio Wio-E5 mini' + 'wio-e5-mini' = 'Seeed Studio Wio-E5' 'WioTrackerL1' = 'Seeed Studio Wio Tracker L1 Pro' 'WioTrackerL1Eink' = 'Seeed Studio Wio Tracker L1 EINK' 'Xiao_C3' = 'Seeed Studio Xiao C3' @@ -232,6 +232,8 @@ $DeviceNameOverrides = @{ } $DeviceSubTitleOverrides = @{ + 'Generic_E22_sx1262' = 'SX1262' + 'Generic_E22_sx1268' = 'SX1268' 'Heltec_t114_without_display' = 'Without display' 'heltec_v4_3' = 'v4.3' 'heltec_v4_tft' = 'TFT' @@ -239,18 +241,44 @@ $DeviceSubTitleOverrides = @{ 'heltec_v4_expansionkit_tft' = 'Expansion Kit TFT' 'LilyGo_TBeam_1W' = '1W PA pins: DIO1 1, NSS 15, BUSY 38' 'LilyGo_T-Echo-Lite_non_shell' = 'Non shell' - 'ikoka_nano_nrf_22dbm' = '22 dBm' - 'ikoka_nano_nrf_30dbm' = '30 dBm' - 'ikoka_nano_nrf_33dbm' = '33 dBm' + 'ikoka_nano_nrf_22dbm' = '22dBm' + 'ikoka_nano_nrf_30dbm' = '30dBm' + 'ikoka_nano_nrf_33dbm' = '33dBm' 'ikoka_handheld_nrf_e22_30dbm' = 'No display' 'ikoka_handheld_nrf_e22_30dbm_096' = '0.96 display' 'ikoka_handheld_nrf_e22_30dbm_096_rotated' = '0.96 display rotated' - 'ikoka_stick_nrf_22dbm' = '22 dBm' - 'ikoka_stick_nrf_30dbm' = '30 dBm' - 'ikoka_stick_nrf_33dbm' = '33 dBm' + 'ikoka_stick_nrf_22dbm' = '22dBm' + 'ikoka_stick_nrf_30dbm' = '30dBm' + 'ikoka_stick_nrf_33dbm' = '33dBm' + 'Meshadventurer_sx1262' = 'SX1262' + 'Meshadventurer_sx1268' = 'SX1268' + 'Tenstar_C3_sx1262' = 'SX1262' + 'Tenstar_C3_sx1268' = 'SX1268' + 'wio-e5' = 'Wio-E5' + 'wio-e5-mini' = 'Wio-E5 mini' 'Xiao_S3' = 'SX1262 pins: DIO1 2, NSS 5, BUSY 4' } +$DeviceVariantInTitle = @{ + 'Generic_E22_sx1262' = $true + 'Generic_E22_sx1268' = $true + 'ikoka_nano_nrf_22dbm' = $true + 'ikoka_nano_nrf_30dbm' = $true + 'ikoka_nano_nrf_33dbm' = $true + 'ikoka_handheld_nrf_e22_30dbm' = $true + 'ikoka_handheld_nrf_e22_30dbm_096' = $true + 'ikoka_handheld_nrf_e22_30dbm_096_rotated' = $true + 'ikoka_stick_nrf_22dbm' = $true + 'ikoka_stick_nrf_30dbm' = $true + 'ikoka_stick_nrf_33dbm' = $true + 'Meshadventurer_sx1262' = $true + 'Meshadventurer_sx1268' = $true + 'Tenstar_C3_sx1262' = $true + 'Tenstar_C3_sx1268' = $true + 'wio-e5' = $true + 'wio-e5-mini' = $true +} + $DeviceSelectionNotes = @{ 'LilyGo_TBeam_1W' = 'Board selection note: Use this only for the LilyGo T-Beam 1W high-power PA board. It is not the standard T-Beam SX1262 target: this build uses LoRa DIO1=1, NSS=15, RESET=3, BUSY=38, SCLK/MISO/MOSI=13/12/11, RXEN=21, and GPS TX/RX=5/6. The standard T-Beam SX1262 target uses DIO1=33, NSS=18, RESET=23, SCLK/MISO/MOSI=5/19/27, and GPS RX/TX=12/34.' 'Xiao_S3' = 'Board selection note: Use this for a Seeed XIAO ESP32S3 with an external SX1262 wired to DIO1=2, NSS=5, RESET=3, BUSY=4, SCLK/MISO/MOSI=7/8/9, and RXEN=6. Do not use it for the XIAO S3 WIO board, which uses DIO1=39, NSS=41, RESET=42, BUSY=40, and RXEN=38.' @@ -297,7 +325,7 @@ function Join-SubTitle { ) $parts = @() - if ($DeviceSubTitleOverrides.ContainsKey($DeviceKey)) { + if ($DeviceSubTitleOverrides.ContainsKey($DeviceKey) -and -not $DeviceVariantInTitle.ContainsKey($DeviceKey)) { $parts += $DeviceSubTitleOverrides[$DeviceKey] } if ($RoleSubTitle) { @@ -311,6 +339,29 @@ function Join-SubTitle { return ($parts -join ', ') } +function Join-FirmwareTitle { + param( + [string]$DeviceKey, + [string]$Role, + [string]$Title + ) + + if (-not $DeviceVariantInTitle.ContainsKey($DeviceKey)) { + return $Title + } + + $variant = $DeviceSubTitleOverrides[$DeviceKey] + if (-not $variant) { + return $Title + } + + if ($Role -in @('companionBle', 'companionUsb')) { + return "Companion radio - $variant" + } + + return "$Title - $variant" +} + function Join-VersionNotes { param( [string]$Description, @@ -461,7 +512,7 @@ function New-Catalog { MakerKey = Get-DeviceMakerKey $roleInfo.DeviceKey DeviceName = $roleInfo.DeviceName Role = $roleInfo.Role - Title = $roleInfo.Title + Title = Join-FirmwareTitle $roleInfo.DeviceKey $roleInfo.Role $roleInfo.Title SubTitle = Join-SubTitle $roleInfo.DeviceKey $roleInfo.SubTitle } } diff --git a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json index 7c616444..29bf2a48 100644 --- a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json @@ -963,12 +963,12 @@ }, { "maker": "generic", - "name": "Generic E22 SX1262", + "name": "Generic E22", "type": "esp32", "firmware": [ { "role": "repeater", - "title": "Repeater", + "title": "Repeater - SX1262", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -991,37 +991,7 @@ }, { "role": "repeater", - "title": "Repeater Bridge ESP-NOW", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Generic_E22_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Generic_E22_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "generic", - "name": "Generic E22 SX1268", - "type": "esp32", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", + "title": "Repeater - SX1268", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -1044,7 +1014,30 @@ }, { "role": "repeater", - "title": "Repeater Bridge ESP-NOW", + "title": "Repeater Bridge ESP-NOW - SX1262", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Generic_E22_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Generic_E22_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater Bridge ESP-NOW - SX1268", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3367,8 +3360,7 @@ "firmware": [ { "role": "companionUsb", - "title": "Companion USB", - "subTitle": "0.96 display", + "title": "Companion radio - 0.96 display", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3391,8 +3383,7 @@ }, { "role": "companionUsb", - "title": "Companion USB", - "subTitle": "0.96 display rotated", + "title": "Companion radio - 0.96 display rotated", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3415,8 +3406,7 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "No display", + "title": "Repeater - No display", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3439,8 +3429,7 @@ }, { "role": "roomServer", - "title": "Room Server", - "subTitle": "No display", + "title": "Room Server - No display", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3470,8 +3459,7 @@ "firmware": [ { "role": "companionUsb", - "title": "Companion USB", - "subTitle": "22 dBm", + "title": "Companion radio - 22dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3494,8 +3482,7 @@ }, { "role": "companionUsb", - "title": "Companion USB", - "subTitle": "30 dBm", + "title": "Companion radio - 30dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3518,8 +3505,7 @@ }, { "role": "companionUsb", - "title": "Companion USB", - "subTitle": "33 dBm", + "title": "Companion radio - 33dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3542,8 +3528,7 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "22 dBm", + "title": "Repeater - 22dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3566,8 +3551,7 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "30 dBm", + "title": "Repeater - 30dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3590,8 +3574,7 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "33 dBm", + "title": "Repeater - 33dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3614,8 +3597,7 @@ }, { "role": "roomServer", - "title": "Room Server", - "subTitle": "22 dBm", + "title": "Room Server - 22dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3638,8 +3620,7 @@ }, { "role": "roomServer", - "title": "Room Server", - "subTitle": "30 dBm", + "title": "Room Server - 30dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3662,8 +3643,7 @@ }, { "role": "roomServer", - "title": "Room Server", - "subTitle": "33 dBm", + "title": "Room Server - 33dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3693,8 +3673,7 @@ "firmware": [ { "role": "companionUsb", - "title": "Companion USB", - "subTitle": "22 dBm", + "title": "Companion radio - 22dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3717,8 +3696,7 @@ }, { "role": "companionUsb", - "title": "Companion USB", - "subTitle": "30 dBm", + "title": "Companion radio - 30dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3741,8 +3719,7 @@ }, { "role": "companionUsb", - "title": "Companion USB", - "subTitle": "33 dBm", + "title": "Companion radio - 33dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3765,8 +3742,7 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "22 dBm", + "title": "Repeater - 22dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3789,8 +3765,7 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "30 dBm", + "title": "Repeater - 30dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3813,8 +3788,7 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "33 dBm", + "title": "Repeater - 33dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3837,8 +3811,7 @@ }, { "role": "roomServer", - "title": "Room Server", - "subTitle": "22 dBm", + "title": "Room Server - 22dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3861,8 +3834,7 @@ }, { "role": "roomServer", - "title": "Room Server", - "subTitle": "30 dBm", + "title": "Room Server - 30dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -3885,8 +3857,7 @@ }, { "role": "roomServer", - "title": "Room Server", - "subTitle": "33 dBm", + "title": "Room Server - 33dBm", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -5299,12 +5270,12 @@ }, { "maker": "meshadventurer", - "name": "Meshadventurer SX1262", + "name": "Meshadventurer", "type": "esp32", "firmware": [ { "role": "companionUsb", - "title": "Companion USB", + "title": "Companion radio - SX1262", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -5325,109 +5296,9 @@ } } }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Meshadventurer_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Meshadventurer_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater Bridge ESP-NOW", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Meshadventurer_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Meshadventurer_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Meshadventurer_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Meshadventurer_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "subTitle": "Serial terminal chat", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", - "files": [ - { - "type": "flash-wipe", - "name": "Meshadventurer_sx1262_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Meshadventurer_sx1262_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "meshadventurer", - "name": "Meshadventurer SX1268", - "type": "esp32", - "firmware": [ { "role": "companionUsb", - "title": "Companion USB", + "title": "Companion radio - SX1268", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -5450,7 +5321,30 @@ }, { "role": "repeater", - "title": "Repeater", + "title": "Repeater - SX1262", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater - SX1268", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -5473,7 +5367,30 @@ }, { "role": "repeater", - "title": "Repeater Bridge ESP-NOW", + "title": "Repeater Bridge ESP-NOW - SX1262", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater Bridge ESP-NOW - SX1268", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -5496,7 +5413,30 @@ }, { "role": "roomServer", - "title": "Room Server", + "title": "Room Server - SX1262", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server - SX1268", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -5519,7 +5459,31 @@ }, { "role": "terminalChat", - "title": "Terminal Chat", + "title": "Terminal Chat - SX1262", + "subTitle": "Serial terminal chat", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_terminal_chat-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat - SX1268", "subTitle": "Serial terminal chat", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -7441,7 +7405,7 @@ "firmware": [ { "role": "companionUsb", - "title": "Companion USB", + "title": "Companion radio - Wio-E5", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -7462,62 +7426,9 @@ } } }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater Bridge RS232", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "download", - "name": "wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - } - ] - }, - { - "maker": "seeed", - "name": "Seeed Studio Wio-E5 mini", - "type": "noflash", - "firmware": [ { "role": "companionUsb", - "title": "Companion USB", + "title": "Companion radio - Wio-E5 mini", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -7540,7 +7451,30 @@ }, { "role": "repeater", - "title": "Repeater", + "title": "Repeater - Wio-E5", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater - Wio-E5 mini", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -7561,9 +7495,32 @@ } } }, + { + "role": "repeater", + "title": "Repeater Bridge RS232 - Wio-E5", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "download", + "name": "wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, { "role": "sensor", - "title": "Sensor", + "title": "Sensor - Wio-E5 mini", "subTitle": "Sensor telemetry node", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -8217,12 +8174,12 @@ }, { "maker": "tenstar", - "name": "Tenstar C3 SX1262", + "name": "Tenstar C3", "type": "esp32", "firmware": [ { "role": "repeater", - "title": "Repeater", + "title": "Repeater - SX1262", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -8245,37 +8202,7 @@ }, { "role": "repeater", - "title": "Repeater Bridge ESP-NOW", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash-wipe", - "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "tenstar", - "name": "Tenstar C3 SX1268", - "type": "esp32", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", + "title": "Repeater - SX1268", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", @@ -8298,7 +8225,30 @@ }, { "role": "repeater", - "title": "Repeater Bridge ESP-NOW", + "title": "Repeater Bridge ESP-NOW - SX1262", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash-wipe", + "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-logging-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater Bridge ESP-NOW - SX1268", "version": { "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", diff --git a/mesh-america/keymind-cascade-v1.16.0-provider.json b/mesh-america/keymind-cascade-v1.16.0-provider.json index c336ec5f..3f31b1f5 100644 --- a/mesh-america/keymind-cascade-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-v1.16.0-provider.json @@ -1201,12 +1201,12 @@ }, { "maker": "generic", - "name": "Generic E22 SX1262", + "name": "Generic E22", "type": "esp32", "firmware": [ { "role": "repeater", - "title": "Repeater", + "title": "Repeater - SX1262", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -1229,37 +1229,7 @@ }, { "role": "repeater", - "title": "Repeater Bridge ESP-NOW", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Generic_E22_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Generic_E22_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "generic", - "name": "Generic E22 SX1268", - "type": "esp32", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", + "title": "Repeater - SX1268", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -1282,7 +1252,30 @@ }, { "role": "repeater", - "title": "Repeater Bridge ESP-NOW", + "title": "Repeater Bridge ESP-NOW - SX1262", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Generic_E22_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Generic_E22_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Generic_E22_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater Bridge ESP-NOW - SX1268", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4333,8 +4326,7 @@ "firmware": [ { "role": "companionBle", - "title": "Companion BLE", - "subTitle": "0.96 display", + "title": "Companion radio - 0.96 display", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4357,8 +4349,7 @@ }, { "role": "companionBle", - "title": "Companion BLE", - "subTitle": "0.96 display rotated", + "title": "Companion radio - 0.96 display rotated", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4381,8 +4372,7 @@ }, { "role": "companionUsb", - "title": "Companion USB", - "subTitle": "0.96 display", + "title": "Companion radio - 0.96 display", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4405,8 +4395,7 @@ }, { "role": "companionUsb", - "title": "Companion USB", - "subTitle": "0.96 display rotated", + "title": "Companion radio - 0.96 display rotated", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4429,8 +4418,7 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "No display", + "title": "Repeater - No display", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4453,8 +4441,7 @@ }, { "role": "roomServer", - "title": "Room Server", - "subTitle": "No display", + "title": "Room Server - No display", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4484,8 +4471,7 @@ "firmware": [ { "role": "companionBle", - "title": "Companion BLE", - "subTitle": "22 dBm", + "title": "Companion radio - 22dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4508,8 +4494,7 @@ }, { "role": "companionBle", - "title": "Companion BLE", - "subTitle": "30 dBm", + "title": "Companion radio - 30dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4532,8 +4517,7 @@ }, { "role": "companionBle", - "title": "Companion BLE", - "subTitle": "33 dBm", + "title": "Companion radio - 33dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4556,8 +4540,7 @@ }, { "role": "companionUsb", - "title": "Companion USB", - "subTitle": "22 dBm", + "title": "Companion radio - 22dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4580,8 +4563,7 @@ }, { "role": "companionUsb", - "title": "Companion USB", - "subTitle": "30 dBm", + "title": "Companion radio - 30dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4604,8 +4586,7 @@ }, { "role": "companionUsb", - "title": "Companion USB", - "subTitle": "33 dBm", + "title": "Companion radio - 33dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4628,8 +4609,7 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "22 dBm", + "title": "Repeater - 22dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4652,8 +4632,7 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "30 dBm", + "title": "Repeater - 30dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4676,8 +4655,7 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "33 dBm", + "title": "Repeater - 33dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4700,8 +4678,7 @@ }, { "role": "roomServer", - "title": "Room Server", - "subTitle": "22 dBm", + "title": "Room Server - 22dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4724,8 +4701,7 @@ }, { "role": "roomServer", - "title": "Room Server", - "subTitle": "30 dBm", + "title": "Room Server - 30dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4748,8 +4724,7 @@ }, { "role": "roomServer", - "title": "Room Server", - "subTitle": "33 dBm", + "title": "Room Server - 33dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4779,8 +4754,7 @@ "firmware": [ { "role": "companionBle", - "title": "Companion BLE", - "subTitle": "22 dBm", + "title": "Companion radio - 22dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4803,8 +4777,7 @@ }, { "role": "companionBle", - "title": "Companion BLE", - "subTitle": "30 dBm", + "title": "Companion radio - 30dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4827,8 +4800,7 @@ }, { "role": "companionBle", - "title": "Companion BLE", - "subTitle": "33 dBm", + "title": "Companion radio - 33dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4851,8 +4823,7 @@ }, { "role": "companionUsb", - "title": "Companion USB", - "subTitle": "22 dBm", + "title": "Companion radio - 22dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4875,8 +4846,7 @@ }, { "role": "companionUsb", - "title": "Companion USB", - "subTitle": "30 dBm", + "title": "Companion radio - 30dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4899,8 +4869,7 @@ }, { "role": "companionUsb", - "title": "Companion USB", - "subTitle": "33 dBm", + "title": "Companion radio - 33dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4923,8 +4892,7 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "22 dBm", + "title": "Repeater - 22dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4947,8 +4915,7 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "30 dBm", + "title": "Repeater - 30dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4971,8 +4938,7 @@ }, { "role": "repeater", - "title": "Repeater", - "subTitle": "33 dBm", + "title": "Repeater - 33dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -4995,8 +4961,7 @@ }, { "role": "roomServer", - "title": "Room Server", - "subTitle": "22 dBm", + "title": "Room Server - 22dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -5019,8 +4984,7 @@ }, { "role": "roomServer", - "title": "Room Server", - "subTitle": "30 dBm", + "title": "Room Server - 30dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -5043,8 +5007,7 @@ }, { "role": "roomServer", - "title": "Room Server", - "subTitle": "33 dBm", + "title": "Room Server - 33dBm", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -6994,12 +6957,12 @@ }, { "maker": "meshadventurer", - "name": "Meshadventurer SX1262", + "name": "Meshadventurer", "type": "esp32", "firmware": [ { "role": "companionBle", - "title": "Companion BLE", + "title": "Companion radio - SX1262", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -7020,132 +6983,9 @@ } } }, - { - "role": "companionUsb", - "title": "Companion USB", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Meshadventurer_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Meshadventurer_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Meshadventurer_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Meshadventurer_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater Bridge ESP-NOW", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Meshadventurer_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Meshadventurer_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Meshadventurer_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Meshadventurer_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - }, - { - "role": "terminalChat", - "title": "Terminal Chat", - "subTitle": "Serial terminal chat", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", - "files": [ - { - "type": "flash-wipe", - "name": "Meshadventurer_sx1262_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Meshadventurer_sx1262_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "meshadventurer", - "name": "Meshadventurer SX1268", - "type": "esp32", - "firmware": [ { "role": "companionBle", - "title": "Companion BLE", + "title": "Companion radio - SX1268", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -7168,7 +7008,30 @@ }, { "role": "companionUsb", - "title": "Companion USB", + "title": "Companion radio - SX1262", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "companionUsb", + "title": "Companion radio - SX1268", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -7191,7 +7054,30 @@ }, { "role": "repeater", - "title": "Repeater", + "title": "Repeater - SX1262", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater - SX1268", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -7214,7 +7100,30 @@ }, { "role": "repeater", - "title": "Repeater Bridge ESP-NOW", + "title": "Repeater Bridge ESP-NOW - SX1262", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater Bridge ESP-NOW - SX1268", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -7237,7 +7146,30 @@ }, { "role": "roomServer", - "title": "Room Server", + "title": "Room Server - SX1262", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server - SX1268", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -7260,7 +7192,31 @@ }, { "role": "terminalChat", - "title": "Terminal Chat", + "title": "Terminal Chat - SX1262", + "subTitle": "Serial terminal chat", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.\n\nRole note: Terminal Chat is a standalone serial-terminal text chat build for boards used from a terminal console. It is flashable, but it does not map to the standard Configure step.", + "files": [ + { + "type": "flash-wipe", + "name": "Meshadventurer_sx1262_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Meshadventurer_sx1262_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Meshadventurer_sx1262_terminal_chat-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "terminalChat", + "title": "Terminal Chat - SX1268", "subTitle": "Serial terminal chat", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -9528,7 +9484,7 @@ "firmware": [ { "role": "companionUsb", - "title": "Companion USB", + "title": "Companion radio - Wio-E5", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -9549,62 +9505,9 @@ } } }, - { - "role": "repeater", - "title": "Repeater", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater Bridge RS232", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "download", - "name": "wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.hex", - "title": "HEX download" - }, - { - "type": "download", - "name": "wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Download" - } - ] - } - } - } - ] - }, - { - "maker": "seeed", - "name": "Seeed Studio Wio-E5 mini", - "type": "noflash", - "firmware": [ { "role": "companionUsb", - "title": "Companion USB", + "title": "Companion radio - Wio-E5 mini", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -9627,7 +9530,30 @@ }, { "role": "repeater", - "title": "Repeater", + "title": "Repeater - Wio-E5", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater - Wio-E5 mini", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -9648,9 +9574,32 @@ } } }, + { + "role": "repeater", + "title": "Repeater Bridge RS232 - Wio-E5", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "download", + "name": "wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.hex", + "title": "HEX download" + }, + { + "type": "download", + "name": "wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/wio-e5-repeater_bridge_rs232-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Download" + } + ] + } + } + }, { "role": "sensor", - "title": "Sensor", + "title": "Sensor - Wio-E5 mini", "subTitle": "Sensor telemetry node", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { @@ -10492,12 +10441,12 @@ }, { "maker": "tenstar", - "name": "Tenstar C3 SX1262", + "name": "Tenstar C3", "type": "esp32", "firmware": [ { "role": "repeater", - "title": "Repeater", + "title": "Repeater - SX1262", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -10520,37 +10469,7 @@ }, { "role": "repeater", - "title": "Repeater Bridge ESP-NOW", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash-wipe", - "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", - "title": "Full install (bootloader + firmware)" - }, - { - "type": "flash-update", - "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", - "title": "Update (app only)" - } - ] - } - } - } - ] - }, - { - "maker": "tenstar", - "name": "Tenstar C3 SX1268", - "type": "esp32", - "firmware": [ - { - "role": "repeater", - "title": "Repeater", + "title": "Repeater - SX1268", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", @@ -10573,7 +10492,30 @@ }, { "role": "repeater", - "title": "Repeater Bridge ESP-NOW", + "title": "Repeater Bridge ESP-NOW - SX1262", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash-wipe", + "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c-merged.bin", + "title": "Full install (bootloader + firmware)" + }, + { + "type": "flash-update", + "name": "Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/Tenstar_C3_sx1262_repeater_bridge_espnow-v1.16.0-halo-keymind-dev-28ee5d2c.bin", + "title": "Update (app only)" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater Bridge ESP-NOW - SX1268", "version": { "v1.16.0-halo-keymind-dev-28ee5d2c": { "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", From dc4ac60efc917c636dfa0ac14fca9a660223e11a Mon Sep 17 00:00:00 2001 From: mikecarper Date: Wed, 1 Jul 2026 15:12:22 -0700 Subject: [PATCH 194/214] Sort power variants before roles --- .../generate-mesh-america-catalogs.ps1 | 36 +- ...mind-cascade-logging-v1.16.0-provider.json | 368 ++++++------ .../keymind-cascade-v1.16.0-provider.json | 552 +++++++++--------- 3 files changed, 495 insertions(+), 461 deletions(-) diff --git a/mesh-america/generate-mesh-america-catalogs.ps1 b/mesh-america/generate-mesh-america-catalogs.ps1 index ed29c6d0..c83d668d 100644 --- a/mesh-america/generate-mesh-america-catalogs.ps1 +++ b/mesh-america/generate-mesh-america-catalogs.ps1 @@ -362,6 +362,32 @@ function Join-FirmwareTitle { return "$Title - $variant" } +function Get-VariantSortRank { + param([string]$DeviceKey) + + if (-not $DeviceVariantInTitle.ContainsKey($DeviceKey) -or -not $DeviceSubTitleOverrides.ContainsKey($DeviceKey)) { + return 1000 + } + + $variant = $DeviceSubTitleOverrides[$DeviceKey] + if ($variant -match '^(\d+)dBm$') { + return [int]$Matches[1] + } + + return 1000 +} + +function Get-FirmwareGroupVariantSortRank { + param([array]$Entries) + + $ranks = @($Entries | ForEach-Object { Get-VariantSortRank $_.DeviceKey } | Sort-Object) + if ($ranks.Count) { + return $ranks[0] + } + + return 1000 +} + function Join-VersionNotes { param( [string]$Description, @@ -528,7 +554,15 @@ function New-Catalog { $deviceType = Get-DeviceType $deviceFiles $firmware = New-Object System.Collections.ArrayList - foreach ($roleGroup in @($deviceGroup.Group | Group-Object Role,Title,SubTitle | Sort-Object Name)) { + $firmwareGroups = @( + $deviceGroup.Group | + Group-Object Role,Title,SubTitle | + Sort-Object ` + @{ Expression = { Get-FirmwareGroupVariantSortRank $_.Group } }, + @{ Expression = { $_.Name } } + ) + + foreach ($roleGroup in $firmwareGroups) { $first = $roleGroup.Group[0] $files = @( $roleGroup.Group | diff --git a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json index 29bf2a48..d1fcf443 100644 --- a/mesh-america/keymind-cascade-logging-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json @@ -3480,6 +3480,52 @@ } } }, + { + "role": "repeater", + "title": "Repeater - 22dBm", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server - 22dBm", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, { "role": "companionUsb", "title": "Companion radio - 30dBm", @@ -3503,6 +3549,52 @@ } } }, + { + "role": "repeater", + "title": "Repeater - 30dBm", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server - 30dBm", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, { "role": "companionUsb", "title": "Companion radio - 33dBm", @@ -3526,52 +3618,6 @@ } } }, - { - "role": "repeater", - "title": "Repeater - 22dBm", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater - 30dBm", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, { "role": "repeater", "title": "Repeater - 33dBm", @@ -3595,52 +3641,6 @@ } } }, - { - "role": "roomServer", - "title": "Room Server - 22dBm", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server - 30dBm", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, { "role": "roomServer", "title": "Room Server - 33dBm", @@ -3694,6 +3694,52 @@ } } }, + { + "role": "repeater", + "title": "Repeater - 22dBm", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server - 22dBm", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, { "role": "companionUsb", "title": "Companion radio - 30dBm", @@ -3717,6 +3763,52 @@ } } }, + { + "role": "repeater", + "title": "Repeater - 30dBm", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server - 30dBm", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, { "role": "companionUsb", "title": "Companion radio - 33dBm", @@ -3740,52 +3832,6 @@ } } }, - { - "role": "repeater", - "title": "Repeater - 22dBm", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater - 30dBm", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, { "role": "repeater", "title": "Repeater - 33dBm", @@ -3809,52 +3855,6 @@ } } }, - { - "role": "roomServer", - "title": "Room Server - 22dBm", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server - 30dBm", - "version": { - "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/logging-v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-logging-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, { "role": "roomServer", "title": "Room Server - 33dBm", diff --git a/mesh-america/keymind-cascade-v1.16.0-provider.json b/mesh-america/keymind-cascade-v1.16.0-provider.json index 3f31b1f5..b4784d2c 100644 --- a/mesh-america/keymind-cascade-v1.16.0-provider.json +++ b/mesh-america/keymind-cascade-v1.16.0-provider.json @@ -4492,6 +4492,75 @@ } } }, + { + "role": "companionUsb", + "title": "Companion radio - 22dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater - 22dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server - 22dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, { "role": "companionBle", "title": "Companion radio - 30dBm", @@ -4515,6 +4584,75 @@ } } }, + { + "role": "companionUsb", + "title": "Companion radio - 30dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater - 30dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server - 30dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, { "role": "companionBle", "title": "Companion radio - 33dBm", @@ -4538,52 +4676,6 @@ } } }, - { - "role": "companionUsb", - "title": "Companion radio - 22dBm", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion radio - 30dBm", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, { "role": "companionUsb", "title": "Companion radio - 33dBm", @@ -4607,52 +4699,6 @@ } } }, - { - "role": "repeater", - "title": "Repeater - 22dBm", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater - 30dBm", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, { "role": "repeater", "title": "Repeater - 33dBm", @@ -4676,52 +4722,6 @@ } } }, - { - "role": "roomServer", - "title": "Room Server - 22dBm", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server - 30dBm", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_nano_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, { "role": "roomServer", "title": "Room Server - 33dBm", @@ -4775,6 +4775,75 @@ } } }, + { + "role": "companionUsb", + "title": "Companion radio - 22dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater - 22dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server - 22dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, { "role": "companionBle", "title": "Companion radio - 30dBm", @@ -4798,6 +4867,75 @@ } } }, + { + "role": "companionUsb", + "title": "Companion radio - 30dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "repeater", + "title": "Repeater - 30dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, + { + "role": "roomServer", + "title": "Room Server - 30dBm", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", + "files": [ + { + "type": "flash", + "name": "ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", + "title": "Serial DFU package" + }, + { + "type": "download", + "name": "ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", + "title": "UF2 download" + } + ] + } + } + }, { "role": "companionBle", "title": "Companion radio - 33dBm", @@ -4821,52 +4959,6 @@ } } }, - { - "role": "companionUsb", - "title": "Companion radio - 22dBm", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "companionUsb", - "title": "Companion radio - 30dBm", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_companion_radio_usb-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, { "role": "companionUsb", "title": "Companion radio - 33dBm", @@ -4890,52 +4982,6 @@ } } }, - { - "role": "repeater", - "title": "Repeater - 22dBm", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "repeater", - "title": "Repeater - 30dBm", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_repeater-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, { "role": "repeater", "title": "Repeater - 33dBm", @@ -4959,52 +5005,6 @@ } } }, - { - "role": "roomServer", - "title": "Room Server - 22dBm", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_22dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, - { - "role": "roomServer", - "title": "Room Server - 30dBm", - "version": { - "v1.16.0-halo-keymind-dev-28ee5d2c": { - "notes": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.", - "files": [ - { - "type": "flash", - "name": "ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.zip", - "title": "Serial DFU package" - }, - { - "type": "download", - "name": "ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "url": "https://github.com/mikecarper/MeshCore/releases/download/v1.16.0-halo-keymind-dev-28ee5d2c/ikoka_stick_nrf_30dbm_room_server-v1.16.0-halo-keymind-dev-28ee5d2c.uf2", - "title": "UF2 download" - } - ] - } - } - }, { "role": "roomServer", "title": "Room Server - 33dBm", From 4f701b7aec539eee31d98aadfea79063866e53e8 Mon Sep 17 00:00:00 2001 From: Christoph Koehler Date: Wed, 1 Jul 2026 20:01:58 -0600 Subject: [PATCH 195/214] refactor: split MeshTables::hasSeen into pure query + markSeen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hasSeen() was simultaneously a predicate and a mutator — it inserted the packet hash on every miss, making five call sites that only wanted to mark a packet as sent call it with the return value discarded. Split into: - wasSeen() — pure predicate, no side effects - markSeen() — explicit insert All query sites now call markSeen() immediately after wasSeen() returns false, preserving identical runtime behaviour. The five mark-only send sites (sendFlood, sendDirect, sendZeroHop x2) now call markSeen directly. Also fixes three bridge sites (BridgeBase, ESPNowBridge, RS232Bridge) that had the same query+implicit-insert pattern. Tests: add test/test_mesh_tables/ covering wasSeen purity, markSeen, dup stats, and clear. Update SHA256 mock to produce deterministic output (previously finalize() was a no-op). Add Packet.cpp to native build filter. --- platformio.ini | 1 + src/Mesh.cpp | 43 +++++--- src/Mesh.h | 5 +- src/helpers/SimpleMeshTables.h | 16 ++- src/helpers/bridges/BridgeBase.cpp | 3 +- src/helpers/bridges/BridgeBase.h | 2 +- src/helpers/bridges/ESPNowBridge.cpp | 3 +- src/helpers/bridges/RS232Bridge.cpp | 3 +- test/mocks/SHA256.h | 29 ++++- .../test_simple_mesh_tables.cpp | 103 ++++++++++++++++++ 10 files changed, 176 insertions(+), 32 deletions(-) create mode 100644 test/test_mesh_tables/test_simple_mesh_tables.cpp diff --git a/platformio.ini b/platformio.ini index b08854e5..47cc0ab8 100644 --- a/platformio.ini +++ b/platformio.ini @@ -165,5 +165,6 @@ test_build_src = yes build_src_filter = -<*> +<../src/Utils.cpp> + +<../src/Packet.cpp> lib_deps = google/googletest @ 1.17.0 diff --git a/src/Mesh.cpp b/src/Mesh.cpp index e9b92262..c11f37ca 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -55,7 +55,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { uint16_t offset = (uint16_t)pkt->path_len << path_sz; if (offset >= len) { // TRACE has reached end of given path onTraceRecv(pkt, trace_tag, auth_code, flags, pkt->path, &pkt->payload[i], len); - } else if (self_id.isHashMatch(&pkt->payload[i + offset], 1 << path_sz) && allowPacketForward(pkt) && !_tables->hasSeen(pkt)) { + } else if (self_id.isHashMatch(&pkt->payload[i + offset], 1 << path_sz) && allowPacketForward(pkt) && !_tables->wasSeen(pkt)) { + _tables->markSeen(pkt); // append SNR (Not hash!) pkt->path[pkt->path_len++] = (int8_t) (pkt->getSNR()*4); @@ -89,14 +90,16 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { if (pkt->getPayloadType() == PAYLOAD_TYPE_MULTIPART) { return forwardMultipartDirect(pkt); } else if (pkt->getPayloadType() == PAYLOAD_TYPE_ACK) { - if (!_tables->hasSeen(pkt)) { // don't retransmit! + if (!_tables->wasSeen(pkt)) { // don't retransmit! + _tables->markSeen(pkt); removeSelfFromPath(pkt); routeDirectRecvAcks(pkt, 0); } return ACTION_RELEASE; } - if (!_tables->hasSeen(pkt)) { + if (!_tables->wasSeen(pkt)) { + _tables->markSeen(pkt); removeSelfFromPath(pkt); uint32_t d = getDirectRetransmitDelay(pkt); @@ -117,7 +120,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { memcpy(&ack_crc, &pkt->payload[i], 4); i += 4; if (i > pkt->payload_len) { MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): incomplete ACK packet", getLogDateTime()); - } else if (!_tables->hasSeen(pkt)) { + } else if (!_tables->wasSeen(pkt)) { + _tables->markSeen(pkt); onAckRecv(pkt, ack_crc); action = routeRecvPacket(pkt); } @@ -134,7 +138,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { uint8_t* macAndData = &pkt->payload[i]; // MAC + encrypted data if (i + CIPHER_MAC_SIZE >= pkt->payload_len) { MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): incomplete data packet", getLogDateTime()); - } else if (!_tables->hasSeen(pkt)) { + } else if (!_tables->wasSeen(pkt)) { + _tables->markSeen(pkt); // NOTE: this is a 'first packet wins' impl. When receiving from multiple paths, the first to arrive wins. // For flood mode, the path may not be the 'best' in terms of hops. // FUTURE: could send back multiple paths, using createPathReturn(), and let sender choose which to use(?) @@ -197,7 +202,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { uint8_t* macAndData = &pkt->payload[i]; // MAC + encrypted data if (i + 2 >= pkt->payload_len) { MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): incomplete data packet", getLogDateTime()); - } else if (!_tables->hasSeen(pkt)) { + } else if (!_tables->wasSeen(pkt)) { + _tables->markSeen(pkt); if (self_id.isHashMatch(&dest_hash)) { Identity sender(sender_pub_key); @@ -224,7 +230,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { uint8_t* macAndData = &pkt->payload[i]; // MAC + encrypted data if (i + 2 >= pkt->payload_len) { MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): incomplete data packet", getLogDateTime()); - } else if (!_tables->hasSeen(pkt)) { + } else if (!_tables->wasSeen(pkt)) { + _tables->markSeen(pkt); // scan channels DB, for all matching hashes of 'channel_hash' (max 4 matches supported ATM) GroupChannel channels[4]; int num = searchChannelsByHash(&channel_hash, channels, 4); @@ -255,7 +262,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): incomplete advertisement packet", getLogDateTime()); } else if (self_id.matches(id.pub_key)) { MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): receiving SELF advert packet", getLogDateTime()); - } else if (!_tables->hasSeen(pkt)) { + } else if (!_tables->wasSeen(pkt)) { + _tables->markSeen(pkt); uint8_t* app_data = &pkt->payload[i]; int app_data_len = pkt->payload_len - i; if (app_data_len > MAX_ADVERT_DATA_SIZE) { app_data_len = MAX_ADVERT_DATA_SIZE; } @@ -282,7 +290,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { break; } case PAYLOAD_TYPE_RAW_CUSTOM: { - if (pkt->isRouteDirect() && !_tables->hasSeen(pkt)) { + if (pkt->isRouteDirect() && !_tables->wasSeen(pkt)) { + _tables->markSeen(pkt); onRawDataRecv(pkt); //action = routeRecvPacket(pkt); don't flood route these (yet) } @@ -300,7 +309,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { tmp.payload_len = pkt->payload_len - 1; memcpy(tmp.payload, &pkt->payload[1], tmp.payload_len); - if (!_tables->hasSeen(&tmp)) { + if (!_tables->wasSeen(&tmp)) { + _tables->markSeen(&tmp); uint32_t ack_crc; memcpy(&ack_crc, tmp.payload, 4); @@ -357,7 +367,8 @@ DispatcherAction Mesh::forwardMultipartDirect(Packet* pkt) { tmp.payload_len = pkt->payload_len - 1; memcpy(tmp.payload, &pkt->payload[1], tmp.payload_len); - if (!_tables->hasSeen(&tmp)) { // don't retransmit! + if (!_tables->wasSeen(&tmp)) { // don't retransmit! + _tables->markSeen(&tmp); removeSelfFromPath(&tmp); routeDirectRecvAcks(&tmp, ((uint32_t)remaining + 1) * 300); // expect multipart ACKs 300ms apart (x2) } @@ -637,7 +648,7 @@ void Mesh::sendFlood(Packet* packet, uint32_t delay_millis, uint8_t path_hash_si packet->header |= ROUTE_TYPE_FLOOD; packet->setPathHashSizeAndCount(path_hash_size, 0); - _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us + _tables->markSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us uint8_t pri; if (packet->getPayloadType() == PAYLOAD_TYPE_PATH) { @@ -666,7 +677,7 @@ void Mesh::sendFlood(Packet* packet, uint16_t* transport_codes, uint32_t delay_m packet->transport_codes[1] = transport_codes[1]; packet->setPathHashSizeAndCount(path_hash_size, 0); - _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us + _tables->markSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us uint8_t pri; if (packet->getPayloadType() == PAYLOAD_TYPE_PATH) { @@ -699,7 +710,7 @@ void Mesh::sendDirect(Packet* packet, const uint8_t* path, uint8_t path_len, uin pri = 0; } } - _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us + _tables->markSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us sendPacket(packet, pri, delay_millis); } @@ -709,7 +720,7 @@ void Mesh::sendZeroHop(Packet* packet, uint32_t delay_millis) { packet->path_len = 0; // path_len of zero means Zero Hop - _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us + _tables->markSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us sendPacket(packet, 0, delay_millis); } @@ -722,7 +733,7 @@ void Mesh::sendZeroHop(Packet* packet, uint16_t* transport_codes, uint32_t delay packet->path_len = 0; // path_len of zero means Zero Hop - _tables->hasSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us + _tables->markSeen(packet); // mark this packet as already sent in case it is rebroadcast back to us sendPacket(packet, 0, delay_millis); } diff --git a/src/Mesh.h b/src/Mesh.h index 932541db..49a299a6 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -15,8 +15,9 @@ public: */ class MeshTables { public: - virtual bool hasSeen(const Packet* packet) = 0; - virtual void clear(const Packet* packet) = 0; // remove this packet hash from table + virtual bool wasSeen(const Packet* packet) = 0; + virtual void markSeen(const Packet* packet) = 0; + virtual void clear(const Packet* packet) = 0; // remove this packet hash from table }; /** diff --git a/src/helpers/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index 0b79cfb4..956f36fa 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -31,27 +31,31 @@ public: } #endif - bool hasSeen(const mesh::Packet* packet) override { + bool wasSeen(const mesh::Packet* packet) override { uint8_t hash[MAX_HASH_SIZE]; packet->calculatePacketHash(hash); const uint8_t* sp = _hashes; for (int i = 0; i < MAX_PACKET_HASHES; i++, sp += MAX_HASH_SIZE) { - if (memcmp(hash, sp, MAX_HASH_SIZE) == 0) { + if (memcmp(hash, sp, MAX_HASH_SIZE) == 0) { if (packet->isRouteDirect()) { - _direct_dups++; // keep some stats + _direct_dups++; } else { _flood_dups++; } return true; } } - - memcpy(&_hashes[_next_idx*MAX_HASH_SIZE], hash, MAX_HASH_SIZE); - _next_idx = (_next_idx + 1) % MAX_PACKET_HASHES; // cyclic table return false; } + void markSeen(const mesh::Packet* packet) override { + uint8_t hash[MAX_HASH_SIZE]; + packet->calculatePacketHash(hash); + memcpy(&_hashes[_next_idx * MAX_HASH_SIZE], hash, MAX_HASH_SIZE); + _next_idx = (_next_idx + 1) % MAX_PACKET_HASHES; + } + void clear(const mesh::Packet* packet) override { uint8_t hash[MAX_HASH_SIZE]; packet->calculatePacketHash(hash); diff --git a/src/helpers/bridges/BridgeBase.cpp b/src/helpers/bridges/BridgeBase.cpp index d2e2e5e0..8093d3cb 100644 --- a/src/helpers/bridges/BridgeBase.cpp +++ b/src/helpers/bridges/BridgeBase.cpp @@ -39,7 +39,8 @@ void BridgeBase::handleReceivedPacket(mesh::Packet *packet) { return; } - if (!_seen_packets.hasSeen(packet)) { + if (!_seen_packets.wasSeen(packet)) { + _seen_packets.markSeen(packet); // bridge_delay provides a buffer to prevent immediate processing conflicts in the mesh network. _mgr->queueInbound(packet, millis() + _prefs->bridge_delay); } else { diff --git a/src/helpers/bridges/BridgeBase.h b/src/helpers/bridges/BridgeBase.h index 04c1564b..8bbe6466 100644 --- a/src/helpers/bridges/BridgeBase.h +++ b/src/helpers/bridges/BridgeBase.h @@ -110,7 +110,7 @@ protected: * @brief Common packet handling for received packets * * Implements the standard pattern used by all bridges: - * - Check if packet was seen before using _seen_packets.hasSeen() + * - Check if packet was seen before using _seen_packets.wasSeen() * - Queue packet for mesh processing if not seen before * - Free packet if already seen to prevent duplicates * diff --git a/src/helpers/bridges/ESPNowBridge.cpp b/src/helpers/bridges/ESPNowBridge.cpp index 808e9df4..3c094e7c 100644 --- a/src/helpers/bridges/ESPNowBridge.cpp +++ b/src/helpers/bridges/ESPNowBridge.cpp @@ -167,7 +167,8 @@ void ESPNowBridge::sendPacket(mesh::Packet *packet) { return; } - if (!_seen_packets.hasSeen(packet)) { + if (!_seen_packets.wasSeen(packet)) { + _seen_packets.markSeen(packet); // Create a temporary buffer just for size calculation and reuse for actual writing uint8_t sizingBuffer[MAX_PAYLOAD_SIZE]; uint16_t meshPacketLen = packet->writeTo(sizingBuffer); diff --git a/src/helpers/bridges/RS232Bridge.cpp b/src/helpers/bridges/RS232Bridge.cpp index 0024f6f2..f719d342 100644 --- a/src/helpers/bridges/RS232Bridge.cpp +++ b/src/helpers/bridges/RS232Bridge.cpp @@ -115,7 +115,8 @@ void RS232Bridge::sendPacket(mesh::Packet *packet) { return; } - if (!_seen_packets.hasSeen(packet)) { + if (!_seen_packets.wasSeen(packet)) { + _seen_packets.markSeen(packet); uint8_t buffer[MAX_SERIAL_PACKET_SIZE]; uint16_t len = packet->writeTo(buffer + 4); diff --git a/test/mocks/SHA256.h b/test/mocks/SHA256.h index b6e551a0..0cefe5c6 100644 --- a/test/mocks/SHA256.h +++ b/test/mocks/SHA256.h @@ -3,12 +3,33 @@ #include #include -// Mock SHA256 class for testing -// Provides minimal interface to allow Utils.cpp to compile +// Mock SHA256 for native testing — deterministic but not cryptographic. +// finalize() writes real (non-garbage) output so calculatePacketHash() produces +// distinguishable results for packets with different payloads. +#include + class SHA256 { + uint8_t _state[32]; + size_t _len; public: - void update(const uint8_t* data, size_t len) {} - void finalize(uint8_t* hash, size_t hashLen) {} + SHA256() : _len(0) { memset(_state, 0, sizeof(_state)); } + + void update(const void* data, size_t len) { + const uint8_t* bytes = static_cast(data); + for (size_t i = 0; i < len; i++) { + uint8_t b = bytes[i]; + _state[_len % 32] ^= b; + _state[(_len + 1) % 32] += (uint8_t)((b >> 1) | (b << 7)); + _len++; + } + } + + void finalize(uint8_t* hash, size_t hashLen) { + for (size_t i = 0; i < hashLen; i++) { + hash[i] = _state[i % 32]; + } + } + void resetHMAC(const uint8_t* key, size_t keyLen) {} void finalizeHMAC(const uint8_t* key, size_t keyLen, uint8_t* hash, size_t hashLen) {} }; diff --git a/test/test_mesh_tables/test_simple_mesh_tables.cpp b/test/test_mesh_tables/test_simple_mesh_tables.cpp new file mode 100644 index 00000000..46b477d9 --- /dev/null +++ b/test/test_mesh_tables/test_simple_mesh_tables.cpp @@ -0,0 +1,103 @@ +#include +#include "helpers/SimpleMeshTables.h" + +using namespace mesh; + +// Build a packet that calculatePacketHash() distinguishes by payload content. +// header selects ROUTE_TYPE_FLOOD so isRouteDirect() returns false. +static Packet makeFloodPacket(uint8_t seed) { + Packet p; + p.header = ROUTE_TYPE_FLOOD | (PAYLOAD_TYPE_ACK << PH_TYPE_SHIFT); + p.payload[0] = seed; + p.payload_len = 1; + p.path_len = 0; + return p; +} + +static Packet makeDirectPacket(uint8_t seed) { + Packet p; + p.header = ROUTE_TYPE_DIRECT | (PAYLOAD_TYPE_ACK << PH_TYPE_SHIFT); + p.payload[0] = seed; + p.payload_len = 1; + p.path_len = 0; + return p; +} + +// ── wasSeen: pure query ─────────────────────────────────────────────────────── + +TEST(SimpleMeshTables, WasSeen_ReturnsFalseForUnseen) { + SimpleMeshTables t; + Packet p = makeFloodPacket(0x01); + EXPECT_FALSE(t.wasSeen(&p)); +} + +// wasSeen shouldn't change state +TEST(SimpleMeshTables, WasSeen_IsPureQuery_DoesNotInsert) { + SimpleMeshTables t; + Packet p = makeFloodPacket(0x01); + EXPECT_FALSE(t.wasSeen(&p)); + EXPECT_FALSE(t.wasSeen(&p)); +} + +// ── markSeen + wasSeen ─────────────────────────────────────────────────────── + +TEST(SimpleMeshTables, MarkSeen_MakesWasSeenReturnTrue) { + SimpleMeshTables t; + Packet p = makeFloodPacket(0x01); + t.markSeen(&p); + EXPECT_TRUE(t.wasSeen(&p)); +} + +TEST(SimpleMeshTables, MarkSeen_DoesNotAffectOtherPackets) { + SimpleMeshTables t; + Packet p1 = makeFloodPacket(0x01); + Packet p2 = makeFloodPacket(0x02); + t.markSeen(&p1); + EXPECT_FALSE(t.wasSeen(&p2)); +} + +// Canonical pattern used at every onRecvPacket call site: +// if (!wasSeen(pkt)) { markSeen(pkt); process(pkt); } +TEST(SimpleMeshTables, QueryThenMark_WorksCorrectly) { + SimpleMeshTables t; + Packet p = makeFloodPacket(0x01); + EXPECT_FALSE(t.wasSeen(&p)); + t.markSeen(&p); + EXPECT_TRUE(t.wasSeen(&p)); +} + +// ── dup stats ──────────────────────────────────────────────────────────────── + +TEST(SimpleMeshTables, WasSeen_IncrementsFloodDupStat) { + SimpleMeshTables t; + Packet p = makeFloodPacket(0x01); + t.markSeen(&p); + t.wasSeen(&p); + EXPECT_EQ(1u, t.getNumFloodDups()); + EXPECT_EQ(0u, t.getNumDirectDups()); +} + +TEST(SimpleMeshTables, WasSeen_IncrementsDirectDupStat) { + SimpleMeshTables t; + Packet p = makeDirectPacket(0x01); + t.markSeen(&p); + t.wasSeen(&p); + EXPECT_EQ(0u, t.getNumFloodDups()); + EXPECT_EQ(1u, t.getNumDirectDups()); +} + +// ── clear ──────────────────────────────────────────────────────────────────── + +TEST(SimpleMeshTables, Clear_RemovesSeenPacket) { + SimpleMeshTables t; + Packet p = makeFloodPacket(0x01); + t.markSeen(&p); + ASSERT_TRUE(t.wasSeen(&p)); + t.clear(&p); + EXPECT_FALSE(t.wasSeen(&p)); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From 0d7379520ff3fe92c2ad06cf8b39b3bdceb29b45 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 1 Jul 2026 15:25:34 +0100 Subject: [PATCH 196/214] Fix `recv_pkt_region` incorrect usage The `recv_pkt_region` is set when processing a flood packet in `filterRecvFloodPacket` but direct/non-flood packets would never pass through that function, so the pointer was not cleared for them. `sendFloodReply` would then later use it blindly, which meant that the response would either inherit the region from the last flood packet, or refer to a non-initialised pointer if no region floods had been received yet. --- examples/simple_repeater/MyMesh.cpp | 7 +++---- examples/simple_repeater/MyMesh.h | 2 +- examples/simple_room_server/MyMesh.cpp | 7 +++---- examples/simple_room_server/MyMesh.h | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index ca4cfad2..b66e1952 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -549,8 +549,7 @@ uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) { return getRNG()->nextInt(0, 5*t + 1); } -bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) { - // just try to determine region for packet (apply later in allowPacketForward()) +mesh::DispatcherAction MyMesh::onRecvPacket(mesh::Packet* pkt) { if (pkt->getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD) { recv_pkt_region = region_map.findMatch(pkt, REGION_DENY_FLOOD); } else if (pkt->getRouteType() == ROUTE_TYPE_FLOOD) { @@ -562,8 +561,7 @@ bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) { } else { recv_pkt_region = NULL; } - // do normal processing - return false; + return Mesh::onRecvPacket(pkt); } void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const mesh::Identity &sender, @@ -867,6 +865,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc set_radio_at = revert_radio_at = 0; _logging = false; region_load_active = false; + recv_pkt_region = NULL; #if MAX_NEIGHBOURS memset(neighbours, 0, sizeof(neighbours)); diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index fb091a4c..0b2e7491 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -166,7 +166,7 @@ protected: } #endif - bool filterRecvFloodPacket(mesh::Packet* pkt) override; + mesh::DispatcherAction onRecvPacket(mesh::Packet* pkt) override; void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override; int searchPeersByHash(const uint8_t* hash) override; diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 12d0b0c3..36978e80 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -290,8 +290,7 @@ bool MyMesh::allowPacketForward(const mesh::Packet *packet) { return true; } -bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) { - // just try to determine region for packet (apply later in allowPacketForward()) +mesh::DispatcherAction MyMesh::onRecvPacket(mesh::Packet* pkt) { if (pkt->getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD) { recv_pkt_region = region_map.findMatch(pkt, REGION_DENY_FLOOD); } else if (pkt->getRouteType() == ROUTE_TYPE_FLOOD) { @@ -303,8 +302,7 @@ bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) { } else { recv_pkt_region = NULL; } - // do normal processing - return false; + return Mesh::onRecvPacket(pkt); } void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const mesh::Identity &sender, @@ -627,6 +625,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _logging = false; region_load_active = false; set_radio_at = revert_radio_at = 0; + recv_pkt_region = NULL; // defaults memset(&_prefs, 0, sizeof(_prefs)); diff --git a/examples/simple_room_server/MyMesh.h b/examples/simple_room_server/MyMesh.h index e9e53ec9..6bab9dc2 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -154,7 +154,7 @@ protected: return _prefs.multi_acks; } - bool filterRecvFloodPacket(mesh::Packet* pkt) override; + mesh::DispatcherAction onRecvPacket(mesh::Packet* pkt) override; bool allowPacketForward(const mesh::Packet* packet) override; void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override; From e8a2f9a4d52535e50de1d8739e86a0bee374f936 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Thu, 2 Jul 2026 10:57:46 -0700 Subject: [PATCH 197/214] Disable companion auto-add in cascade --- build.sh | 2 +- examples/companion_radio/MyMesh.cpp | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 0f868ed6..0370063a 100755 --- a/build.sh +++ b/build.sh @@ -1179,7 +1179,7 @@ apply_radio_overrides() { apply_firmware_profile_overrides() { case "${FIRMWARE_PROFILE_OVERRIDE,,}" in cascade) - export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DCASCADE_PROFILE=1 -DDEFAULT_PATH_HASH_MODE=2 -DDEFAULT_LOOP_DETECT=1 -DDEFAULT_RX_DELAY_BASE=2.0f -DDEFAULT_AGC_RESET_INTERVAL_SECONDS=8 -DDEFAULT_ADVERT_INTERVAL_MINUTES=0 -DDEFAULT_FLOOD_ADVERT_INTERVAL_HOURS=83 -DDEFAULT_MULTI_ACKS=1" + export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DCASCADE_PROFILE=1 -DDEFAULT_PATH_HASH_MODE=2 -DDEFAULT_LOOP_DETECT=1 -DDEFAULT_RX_DELAY_BASE=2.0f -DDEFAULT_AGC_RESET_INTERVAL_SECONDS=8 -DDEFAULT_ADVERT_INTERVAL_MINUTES=0 -DDEFAULT_FLOOD_ADVERT_INTERVAL_HOURS=83 -DDEFAULT_MULTI_ACKS=1 -DDEFAULT_MANUAL_ADD_CONTACTS=1 -DDEFAULT_AUTOADD_CONFIG=0" ;; esac } diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 5d1adb46..56b6530a 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -108,6 +108,19 @@ #define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250 #define LAZY_CONTACTS_WRITE_DELAY 5000 +#ifndef DEFAULT_MULTI_ACKS +#define DEFAULT_MULTI_ACKS 0 +#endif +#ifndef DEFAULT_PATH_HASH_MODE +#define DEFAULT_PATH_HASH_MODE 0 +#endif +#ifndef DEFAULT_MANUAL_ADD_CONTACTS +#define DEFAULT_MANUAL_ADD_CONTACTS 0 +#endif +#ifndef DEFAULT_AUTOADD_CONFIG +#define DEFAULT_AUTOADD_CONFIG 0 +#endif + #define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg==" // these are _pushed_ to client app at any time @@ -880,9 +893,13 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe _prefs.sf = LORA_SF; _prefs.bw = LORA_BW; _prefs.cr = LORA_CR; + _prefs.multi_acks = DEFAULT_MULTI_ACKS; + _prefs.manual_add_contacts = DEFAULT_MANUAL_ADD_CONTACTS; _prefs.tx_power_dbm = LORA_TX_POWER; _prefs.gps_enabled = 0; // GPS disabled by default _prefs.gps_interval = 0; // No automatic GPS updates by default + _prefs.autoadd_config = DEFAULT_AUTOADD_CONFIG; + _prefs.path_hash_mode = DEFAULT_PATH_HASH_MODE; //_prefs.rx_delay_base = 10.0f; enable once new algo fixed #if defined(USE_SX1262) || defined(USE_SX1268) #ifdef SX126X_RX_BOOSTED_GAIN @@ -939,8 +956,12 @@ void MyMesh::begin(bool has_display) { _prefs.sf = constrain(_prefs.sf, 5, 12); _prefs.cr = constrain(_prefs.cr, 5, 8); _prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, -9, MAX_LORA_TX_POWER); + _prefs.multi_acks = constrain(_prefs.multi_acks, 0, 1); + _prefs.manual_add_contacts = constrain(_prefs.manual_add_contacts, 0, 1); _prefs.gps_enabled = constrain(_prefs.gps_enabled, 0, 1); // Ensure boolean 0 or 1 _prefs.gps_interval = constrain(_prefs.gps_interval, 0, 86400); // Max 24 hours + _prefs.autoadd_config &= AUTO_ADD_OVERWRITE_OLDEST | AUTO_ADD_CHAT | AUTO_ADD_REPEATER | AUTO_ADD_ROOM_SERVER | AUTO_ADD_SENSOR; + _prefs.path_hash_mode = constrain(_prefs.path_hash_mode, 0, 2); _prefs.radio_fem_rxgain = constrain(_prefs.radio_fem_rxgain, 0, 1); #ifdef BLE_PIN_CODE // 123456 by default From b40968a0f08b93cf14104e6f8d7ef45338c913ab Mon Sep 17 00:00:00 2001 From: liamcottle Date: Sun, 5 Jul 2026 12:23:22 +1200 Subject: [PATCH 198/214] stop tracking vscode extensions.json --- .vscode/extensions.json | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 8057bc70..00000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "recommendations": [ - "pioarduino.pioarduino-ide", - "platformio.platformio-ide" - ], - "unwantedRecommendations": [ - "ms-vscode.cpptools-extension-pack" - ] -} From 0da1fe33e1350015131d57db5f8910f692123ce2 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Sun, 5 Jul 2026 18:54:26 +0700 Subject: [PATCH 199/214] Fixed powerOff for Techo --- variants/lilygo_techo/TechoBoard.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/lilygo_techo/TechoBoard.h b/variants/lilygo_techo/TechoBoard.h index d8cde1fe..e957d2e5 100644 --- a/variants/lilygo_techo/TechoBoard.h +++ b/variants/lilygo_techo/TechoBoard.h @@ -24,6 +24,7 @@ public: } void powerOff() override { + NRF52Board::powerOff(); #ifdef LED_RED digitalWrite(LED_RED, HIGH); #endif @@ -39,6 +40,5 @@ public: #ifdef PIN_PWR_EN digitalWrite(PIN_PWR_EN, LOW); #endif - NRF52Board::powerOff(); } }; From a92046b0eb73d5bc40af07c70c14fff246b196a7 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Sun, 5 Jul 2026 21:20:13 +0700 Subject: [PATCH 200/214] Put powerOff and enterDeepSleep to ESP32Board --- src/helpers/ESP32Board.cpp | 42 ++++++++++++++++++++++++++++++++++++++ src/helpers/ESP32Board.h | 3 +++ 2 files changed, 45 insertions(+) diff --git a/src/helpers/ESP32Board.cpp b/src/helpers/ESP32Board.cpp index e0ca1d0e..a55abb26 100644 --- a/src/helpers/ESP32Board.cpp +++ b/src/helpers/ESP32Board.cpp @@ -1,6 +1,7 @@ #ifdef ESP_PLATFORM #include "ESP32Board.h" +#include #if defined(ADMIN_PASSWORD) && !defined(DISABLE_WIFI_OTA) // Repeater or Room Server only #include @@ -44,4 +45,45 @@ bool ESP32Board::startOTAUpdate(const char* id, char reply[]) { } #endif +void ESP32Board::powerOff() { + enterDeepSleep(0); // Do not wakeup +} + +void ESP32Board::enterDeepSleep(uint32_t secs) { + // Power off the display if any +#ifdef DISPLAY_CLASS + display.turnOff(); +#endif + + // Power off LoRa + radio_driver.powerOff(); + + // Keep LoRa inactive during deepsleep + digitalWrite(P_LORA_NSS, HIGH); +#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) + gpio_hold_en((gpio_num_t)P_LORA_NSS); +#else + rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); +#endif + + // Power off GPS if any + if (sensors.getLocationProvider() != NULL) { + sensors.getLocationProvider()->stop(); + } + + // Flush serial buffers + Serial.flush(); + delay(100); + + // Clear stale wakeup sources to avoid ghost wakeup + // This is required when Power Management and automatic lightsleep are enabled + esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); + + if (secs > 0) { + esp_sleep_enable_timer_wakeup(secs * 1000000ULL); + } + + // Finally set ESP32 into deepsleep + esp_deep_sleep_start(); // CPU halts here and never returns! +} #endif diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index 1efc99f3..45d7761b 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -62,6 +62,9 @@ public: return raw / 4; } + virtual void powerOff() override; + void enterDeepSleep(uint32_t secs); + uint32_t getIRQGpio() override { return P_LORA_DIO_1; // default for SX1262 } From 73b7367e4559d42f8b9ac8329a0c4d36ba0408d5 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Sun, 5 Jul 2026 21:20:39 +0700 Subject: [PATCH 201/214] Put powerOff to NRF52Board --- src/helpers/NRF52Board.cpp | 32 ++++++++++++++++++++++++++++++++ src/helpers/NRF52Board.h | 1 + 2 files changed, 33 insertions(+) diff --git a/src/helpers/NRF52Board.cpp b/src/helpers/NRF52Board.cpp index 17265f04..beee3212 100644 --- a/src/helpers/NRF52Board.cpp +++ b/src/helpers/NRF52Board.cpp @@ -1,5 +1,6 @@ #if defined(NRF52_PLATFORM) #include "NRF52Board.h" +#include #include #include @@ -297,6 +298,37 @@ float NRF52Board::getMCUTemperature() { return temp * 0.25f; // Convert to *C } +void NRF52Board::powerOff() { + // Power off the display if any +#ifdef DISPLAY_CLASS + display.turnOff(); +#endif + + // Power off LoRa + radio_driver.powerOff(); + + // Keep LoRa inactive during deepsleep + digitalWrite(P_LORA_NSS, HIGH); + + // Power off GPS if any + if(sensors.getLocationProvider() != NULL) { + sensors.getLocationProvider()->stop(); + } + + // Flush serial buffers + Serial.flush(); + delay(100); + + // Enter SYSTEMOFF + uint8_t sd_enabled = 0; + sd_softdevice_is_enabled(&sd_enabled); + if (sd_enabled) { // SoftDevice is enabled + sd_power_system_off(); + } else { // SoftDevice is not enable + NRF_POWER->SYSTEMOFF = POWER_SYSTEMOFF_SYSTEMOFF_Enter; + } +} + bool NRF52Board::getBootloaderVersion(char* out, size_t max_len) { static const char BOOTLOADER_MARKER[] = "UF2 Bootloader "; const uint8_t* flash = (const uint8_t*)0x000FB000; // earliest known info.txt location is 0xFB90B, latest is 0xFCC4B diff --git a/src/helpers/NRF52Board.h b/src/helpers/NRF52Board.h index 17065cf4..cbf4cd49 100644 --- a/src/helpers/NRF52Board.h +++ b/src/helpers/NRF52Board.h @@ -50,6 +50,7 @@ public: virtual uint8_t getStartupReason() const override { return startup_reason; } virtual float getMCUTemperature() override; virtual void reboot() override { NVIC_SystemReset(); } + virtual void powerOff() override; virtual bool getBootloaderVersion(char* version, size_t max_len) override; virtual bool startOTAUpdate(const char *id, char reply[]) override; virtual void sleep(uint32_t secs) override; From 7d7de8870719bce1178b9966f0c1c18ce18dba63 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Sun, 5 Jul 2026 21:22:13 +0700 Subject: [PATCH 202/214] board->powerOff() is sufficient to power off radio, display, GPS and components --- examples/companion_radio/ui-new/UITask.cpp | 3 +-- examples/companion_radio/ui-orig/UITask.cpp | 2 +- examples/companion_radio/ui-tiny/UITask.cpp | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 7c842019..403ea463 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -697,8 +697,7 @@ void UITask::shutdown(bool restart){ if (restart) { _board->reboot(); } else { - _display->turnOff(); - radio_driver.powerOff(); + // Power off board including radio, display, GPS and components _board->powerOff(); } } diff --git a/examples/companion_radio/ui-orig/UITask.cpp b/examples/companion_radio/ui-orig/UITask.cpp index 55290467..b48f6412 100644 --- a/examples/companion_radio/ui-orig/UITask.cpp +++ b/examples/companion_radio/ui-orig/UITask.cpp @@ -307,7 +307,7 @@ void UITask::shutdown(bool restart){ if (restart) { _board->reboot(); } else { - radio_driver.powerOff(); + // Power off board including radio, display, GPS and components _board->powerOff(); } } diff --git a/examples/companion_radio/ui-tiny/UITask.cpp b/examples/companion_radio/ui-tiny/UITask.cpp index 0119475e..452c02d4 100644 --- a/examples/companion_radio/ui-tiny/UITask.cpp +++ b/examples/companion_radio/ui-tiny/UITask.cpp @@ -566,8 +566,7 @@ void UITask::shutdown(bool restart){ if (restart) { _board->reboot(); } else { - _display->turnOff(); - radio_driver.powerOff(); + // Power off board including radio, display, GPS and components _board->powerOff(); } } From eec75eab312d8338001760a3ac17cfd7518af5e8 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Sun, 5 Jul 2026 22:10:47 +0700 Subject: [PATCH 203/214] Cleanup Poweroff for NRF52 boards --- .../GAT56230SMeshKitBoard.h | 2 +- .../gat562_mesh_evb_pro/GAT562EVBProBoard.h | 2 +- .../GAT562MeshTrackerProBoard.h | 2 +- .../GAT56MeshWatch13Board.h | 2 +- variants/heltec_t096/T096Board.cpp | 10 ++----- variants/heltec_t1/T1Board.cpp | 30 +------------------ variants/heltec_t114/T114Board.h | 7 ++--- variants/keepteen_lt1/KeepteenLT1Board.h | 4 --- .../lilygo_t_impulse_plus/TImpulsePlusBoard.h | 6 ++-- variants/lilygo_techo/TechoBoard.h | 2 +- variants/lilygo_techo_card/TechoCardBoard.cpp | 2 +- variants/lilygo_techo_lite/TechoBoard.h | 3 +- variants/mesh_pocket/MeshPocket.h | 4 --- variants/meshtiny/MeshtinyBoard.h | 2 +- .../MinewsemiME25LS01Board.h | 2 +- variants/nano_g2_ultra/nano-g2.h | 2 +- variants/promicro/PromicroBoard.h | 4 --- variants/rak_wismesh_tag/RAKWismeshTagBoard.h | 2 +- variants/sensecap_solar/SenseCapSolarBoard.h | 2 +- variants/t1000-e/T1000eBoard.h | 2 +- variants/thinknode_m1/ThinkNodeM1Board.h | 3 +- variants/thinknode_m3/ThinkNodeM3Board.h | 2 +- variants/thinknode_m6/ThinkNodeM6Board.h | 2 +- variants/wio-tracker-l1/WioTrackerL1Board.h | 2 +- variants/xiao_nrf52/XiaoNrf52Board.h | 2 +- 25 files changed, 27 insertions(+), 76 deletions(-) diff --git a/variants/gat562_30s_mesh_kit/GAT56230SMeshKitBoard.h b/variants/gat562_30s_mesh_kit/GAT56230SMeshKitBoard.h index ab7ecc24..0b0f701c 100644 --- a/variants/gat562_30s_mesh_kit/GAT56230SMeshKitBoard.h +++ b/variants/gat562_30s_mesh_kit/GAT56230SMeshKitBoard.h @@ -47,7 +47,7 @@ public: uint32_t button_pin = PIN_BUTTON1; nrf_gpio_cfg_input(button_pin, NRF_GPIO_PIN_PULLUP); nrf_gpio_cfg_sense_set(button_pin, NRF_GPIO_PIN_SENSE_LOW); - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/gat562_mesh_evb_pro/GAT562EVBProBoard.h b/variants/gat562_mesh_evb_pro/GAT562EVBProBoard.h index 33c3d05f..66c77099 100644 --- a/variants/gat562_mesh_evb_pro/GAT562EVBProBoard.h +++ b/variants/gat562_mesh_evb_pro/GAT562EVBProBoard.h @@ -47,7 +47,7 @@ public: uint32_t button_pin = PIN_BUTTON1; nrf_gpio_cfg_input(button_pin, NRF_GPIO_PIN_PULLUP); nrf_gpio_cfg_sense_set(button_pin, NRF_GPIO_PIN_SENSE_LOW); - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/gat562_mesh_tracker_pro/GAT562MeshTrackerProBoard.h b/variants/gat562_mesh_tracker_pro/GAT562MeshTrackerProBoard.h index aa1772e4..39b55911 100644 --- a/variants/gat562_mesh_tracker_pro/GAT562MeshTrackerProBoard.h +++ b/variants/gat562_mesh_tracker_pro/GAT562MeshTrackerProBoard.h @@ -47,7 +47,7 @@ public: uint32_t button_pin = PIN_BUTTON1; nrf_gpio_cfg_input(button_pin, NRF_GPIO_PIN_PULLUP); nrf_gpio_cfg_sense_set(button_pin, NRF_GPIO_PIN_SENSE_LOW); - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/gat562_mesh_watch13/GAT56MeshWatch13Board.h b/variants/gat562_mesh_watch13/GAT56MeshWatch13Board.h index da792b78..805ca67f 100644 --- a/variants/gat562_mesh_watch13/GAT56MeshWatch13Board.h +++ b/variants/gat562_mesh_watch13/GAT56MeshWatch13Board.h @@ -38,7 +38,7 @@ public: uint32_t button_pin = PIN_BUTTON1; nrf_gpio_cfg_input(button_pin, NRF_GPIO_PIN_PULLUP); nrf_gpio_cfg_sense_set(button_pin, NRF_GPIO_PIN_SENSE_LOW); - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/heltec_t096/T096Board.cpp b/variants/heltec_t096/T096Board.cpp index 54425145..78af529d 100644 --- a/variants/heltec_t096/T096Board.cpp +++ b/variants/heltec_t096/T096Board.cpp @@ -112,13 +112,9 @@ void T096Board::variant_shutdown() { } void T096Board::powerOff() { -#if ENV_INCLUDE_GPS == 1 - pinMode(PIN_GPS_EN, OUTPUT); - digitalWrite(PIN_GPS_EN, !PIN_GPS_EN_ACTIVE); -#endif - loRaFEMControl.setSleepModeEnable(); - variant_shutdown(); - sd_power_system_off(); + loRaFEMControl.setSleepModeEnable(); + nrf_gpio_cfg_default(PIN_GPS_EN); // 363uA down to 39uA + NRF52Board::powerOff(); } const char* T096Board::getManufacturerName() const { diff --git a/variants/heltec_t1/T1Board.cpp b/variants/heltec_t1/T1Board.cpp index 490f8678..1ef5b766 100644 --- a/variants/heltec_t1/T1Board.cpp +++ b/variants/heltec_t1/T1Board.cpp @@ -81,34 +81,6 @@ uint16_t T1Board::getBattMilliVolts() { } void T1Board::variant_shutdown() { - nrf_gpio_cfg_default(PIN_TFT_CS); - nrf_gpio_cfg_default(PIN_TFT_DC); - nrf_gpio_cfg_default(PIN_TFT_SDA); - nrf_gpio_cfg_default(PIN_TFT_SCL); - nrf_gpio_cfg_default(PIN_TFT_RST); - nrf_gpio_cfg_default(PIN_TFT_LEDA_CTL); - nrf_gpio_cfg_default(PIN_TFT_VDD_CTL); - - nrf_gpio_cfg_default(PIN_WIRE_SDA); - nrf_gpio_cfg_default(PIN_WIRE_SCL); - - nrf_gpio_cfg_default(LORA_CS); - nrf_gpio_cfg_default(SX126X_DIO1); - nrf_gpio_cfg_default(SX126X_BUSY); - nrf_gpio_cfg_default(SX126X_RESET); - nrf_gpio_cfg_default(PIN_SPI_MISO); - nrf_gpio_cfg_default(PIN_SPI_MOSI); - nrf_gpio_cfg_default(PIN_SPI_SCK); - - nrf_gpio_cfg_default(PIN_SPI1_MOSI); - nrf_gpio_cfg_default(PIN_SPI1_SCK); - - nrf_gpio_cfg_default(PIN_GPS_RESET); - nrf_gpio_cfg_default(PIN_GPS_EN); - nrf_gpio_cfg_default(PIN_GPS_PPS); - nrf_gpio_cfg_default(PIN_GPS_RX); - nrf_gpio_cfg_default(PIN_GPS_TX); - nrf_gpio_cfg_default(PIN_BUZZER_VOLTAGE_MULTIPLIER_1); nrf_gpio_cfg_default(PIN_BUZZER_VOLTAGE_MULTIPLIER_2); @@ -127,7 +99,7 @@ void T1Board::variant_shutdown() { void T1Board::powerOff() { variant_shutdown(); - sd_power_system_off(); + NRF52Board::powerOff(); } const char* T1Board::getManufacturerName() const { diff --git a/variants/heltec_t114/T114Board.h b/variants/heltec_t114/T114Board.h index f27dc291..bd76d3de 100644 --- a/variants/heltec_t114/T114Board.h +++ b/variants/heltec_t114/T114Board.h @@ -50,10 +50,7 @@ public: #ifdef LED_PIN digitalWrite(LED_PIN, HIGH); #endif -#if ENV_INCLUDE_GPS == 1 - pinMode(GPS_EN, OUTPUT); - digitalWrite(GPS_EN, LOW); -#endif - sd_power_system_off(); + + NRF52Board::powerOff(); } }; diff --git a/variants/keepteen_lt1/KeepteenLT1Board.h b/variants/keepteen_lt1/KeepteenLT1Board.h index 752b27e7..25d4d867 100644 --- a/variants/keepteen_lt1/KeepteenLT1Board.h +++ b/variants/keepteen_lt1/KeepteenLT1Board.h @@ -37,8 +37,4 @@ public: digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off } #endif - - void powerOff() override { - sd_power_system_off(); - } }; diff --git a/variants/lilygo_t_impulse_plus/TImpulsePlusBoard.h b/variants/lilygo_t_impulse_plus/TImpulsePlusBoard.h index ea1782cf..5fafcad8 100644 --- a/variants/lilygo_t_impulse_plus/TImpulsePlusBoard.h +++ b/variants/lilygo_t_impulse_plus/TImpulsePlusBoard.h @@ -48,12 +48,10 @@ public: } void powerOff() override { + // power off system + NRF52Board::powerOff(); // turn off 3.3v digitalWrite(RT9080_EN, LOW); - - // power off system - sd_power_system_off(); - } }; diff --git a/variants/lilygo_techo/TechoBoard.h b/variants/lilygo_techo/TechoBoard.h index e560cd14..e957d2e5 100644 --- a/variants/lilygo_techo/TechoBoard.h +++ b/variants/lilygo_techo/TechoBoard.h @@ -24,6 +24,7 @@ public: } void powerOff() override { + NRF52Board::powerOff(); #ifdef LED_RED digitalWrite(LED_RED, HIGH); #endif @@ -39,6 +40,5 @@ public: #ifdef PIN_PWR_EN digitalWrite(PIN_PWR_EN, LOW); #endif - sd_power_system_off(); } }; diff --git a/variants/lilygo_techo_card/TechoCardBoard.cpp b/variants/lilygo_techo_card/TechoCardBoard.cpp index f0dcef31..8143587d 100644 --- a/variants/lilygo_techo_card/TechoCardBoard.cpp +++ b/variants/lilygo_techo_card/TechoCardBoard.cpp @@ -91,7 +91,7 @@ void TechoCardBoard::powerOff() { nrf_gpio_cfg_sense_input(BUTTON_PIN, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW); turnOffLeds(); digitalWrite(PIN_PWR_EN, LOW); - sd_power_system_off(); + NRF52Board::powerOff(); } #endif diff --git a/variants/lilygo_techo_lite/TechoBoard.h b/variants/lilygo_techo_lite/TechoBoard.h index 7a43fd83..f4c16016 100644 --- a/variants/lilygo_techo_lite/TechoBoard.h +++ b/variants/lilygo_techo_lite/TechoBoard.h @@ -22,6 +22,8 @@ public: } void powerOff() override { + NRF52Board::powerOff(); + digitalWrite(PIN_VBAT_MEAS_EN, LOW); #ifdef LED_RED digitalWrite(LED_RED, LOW); @@ -38,6 +40,5 @@ public: #ifdef PIN_PWR_EN digitalWrite(PIN_PWR_EN, LOW); #endif - sd_power_system_off(); } }; \ No newline at end of file diff --git a/variants/mesh_pocket/MeshPocket.h b/variants/mesh_pocket/MeshPocket.h index 478bd56d..e215eb77 100644 --- a/variants/mesh_pocket/MeshPocket.h +++ b/variants/mesh_pocket/MeshPocket.h @@ -32,8 +32,4 @@ public: const char* getManufacturerName() const override { return "Heltec MeshPocket"; } - - void powerOff() override { - sd_power_system_off(); - } }; diff --git a/variants/meshtiny/MeshtinyBoard.h b/variants/meshtiny/MeshtinyBoard.h index b69c0e41..67a521fe 100644 --- a/variants/meshtiny/MeshtinyBoard.h +++ b/variants/meshtiny/MeshtinyBoard.h @@ -60,7 +60,7 @@ public: nrf_gpio_cfg_sense_input(g_ADigitalPinMap[PIN_USER_BTN], NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW); #endif - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h b/variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h index 4fa5cd41..450a3d13 100644 --- a/variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h +++ b/variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h @@ -65,7 +65,7 @@ public: #ifdef BUTTON_PIN nrf_gpio_cfg_sense_input(digitalPinToInterrupt(BUTTON_PIN), NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW); #endif - sd_power_system_off(); + NRF52Board::powerOff(); } #if defined(P_LORA_TX_LED) diff --git a/variants/nano_g2_ultra/nano-g2.h b/variants/nano_g2_ultra/nano-g2.h index cf771efe..bf9543b0 100644 --- a/variants/nano_g2_ultra/nano-g2.h +++ b/variants/nano_g2_ultra/nano-g2.h @@ -52,6 +52,6 @@ public: nrf_gpio_cfg_sense_input(digitalPinToInterrupt(PIN_USER_BTN), NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_LOW); - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/promicro/PromicroBoard.h b/variants/promicro/PromicroBoard.h index 7b6afb1b..b190c47c 100644 --- a/variants/promicro/PromicroBoard.h +++ b/variants/promicro/PromicroBoard.h @@ -72,8 +72,4 @@ public: #endif return 0; } - - void powerOff() override { - sd_power_system_off(); - } }; diff --git a/variants/rak_wismesh_tag/RAKWismeshTagBoard.h b/variants/rak_wismesh_tag/RAKWismeshTagBoard.h index cc5aa06f..daa90d74 100644 --- a/variants/rak_wismesh_tag/RAKWismeshTagBoard.h +++ b/variants/rak_wismesh_tag/RAKWismeshTagBoard.h @@ -69,6 +69,6 @@ public: nrf_gpio_cfg_sense_input(digitalPinToInterrupt(BUTTON_PIN), NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW); #endif - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/sensecap_solar/SenseCapSolarBoard.h b/variants/sensecap_solar/SenseCapSolarBoard.h index 6799a5e9..5a8861c2 100644 --- a/variants/sensecap_solar/SenseCapSolarBoard.h +++ b/variants/sensecap_solar/SenseCapSolarBoard.h @@ -54,7 +54,7 @@ public: #ifdef NRF52_POWER_MANAGEMENT initiateShutdown(SHUTDOWN_REASON_USER); #else - sd_power_system_off(); + NRF52Board::powerOff(); #endif } }; diff --git a/variants/t1000-e/T1000eBoard.h b/variants/t1000-e/T1000eBoard.h index e7653fb2..1111d3c4 100644 --- a/variants/t1000-e/T1000eBoard.h +++ b/variants/t1000-e/T1000eBoard.h @@ -88,6 +88,6 @@ public: nrf_gpio_cfg_sense_input(BUTTON_PIN, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_HIGH); #endif - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/thinknode_m1/ThinkNodeM1Board.h b/variants/thinknode_m1/ThinkNodeM1Board.h index ebc46e6e..94224d9c 100644 --- a/variants/thinknode_m1/ThinkNodeM1Board.h +++ b/variants/thinknode_m1/ThinkNodeM1Board.h @@ -40,7 +40,6 @@ public: #endif // power off board - sd_power_system_off(); - + NRF52Board::powerOff(); } }; diff --git a/variants/thinknode_m3/ThinkNodeM3Board.h b/variants/thinknode_m3/ThinkNodeM3Board.h index 1435d31d..396d80d1 100644 --- a/variants/thinknode_m3/ThinkNodeM3Board.h +++ b/variants/thinknode_m3/ThinkNodeM3Board.h @@ -49,6 +49,6 @@ public: #endif // power off board - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/thinknode_m6/ThinkNodeM6Board.h b/variants/thinknode_m6/ThinkNodeM6Board.h index 32baa2a0..78815e2c 100644 --- a/variants/thinknode_m6/ThinkNodeM6Board.h +++ b/variants/thinknode_m6/ThinkNodeM6Board.h @@ -44,6 +44,6 @@ public: #endif // power off board - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/wio-tracker-l1/WioTrackerL1Board.h b/variants/wio-tracker-l1/WioTrackerL1Board.h index 052238e6..e376e066 100644 --- a/variants/wio-tracker-l1/WioTrackerL1Board.h +++ b/variants/wio-tracker-l1/WioTrackerL1Board.h @@ -35,6 +35,6 @@ public: } void powerOff() override { - sd_power_system_off(); + NRF52Board::powerOff(); } }; diff --git a/variants/xiao_nrf52/XiaoNrf52Board.h b/variants/xiao_nrf52/XiaoNrf52Board.h index 2790dbad..b2638a44 100644 --- a/variants/xiao_nrf52/XiaoNrf52Board.h +++ b/variants/xiao_nrf52/XiaoNrf52Board.h @@ -50,7 +50,7 @@ public: nrf_gpio_cfg_sense_input(digitalPinToInterrupt(g_ADigitalPinMap[PIN_USER_BTN]), NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_LOW); #endif - sd_power_system_off(); + NRF52Board::powerOff(); } }; From c2697269f69a31bbf02cd02864c7d25aeb1cb337 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Sun, 5 Jul 2026 22:14:08 +0700 Subject: [PATCH 204/214] Fixed Poweroff for TImpulsePlusBoard --- variants/lilygo_t_impulse_plus/TImpulsePlusBoard.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/variants/lilygo_t_impulse_plus/TImpulsePlusBoard.h b/variants/lilygo_t_impulse_plus/TImpulsePlusBoard.h index 6cee1255..5fafcad8 100644 --- a/variants/lilygo_t_impulse_plus/TImpulsePlusBoard.h +++ b/variants/lilygo_t_impulse_plus/TImpulsePlusBoard.h @@ -48,12 +48,10 @@ public: } void powerOff() override { - - // turn off 3.3v - digitalWrite(RT9080_EN, LOW); - // power off system NRF52Board::powerOff(); + // turn off 3.3v + digitalWrite(RT9080_EN, LOW); } }; From 6b205da4e9935266b23c30a794045e1ebe4688c2 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Sun, 5 Jul 2026 23:33:56 +0700 Subject: [PATCH 205/214] Added missing driver/rtc_io.h --- src/helpers/ESP32Board.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index 45d7761b..d7eb5fee 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -14,6 +14,7 @@ #include #include "soc/rtc.h" #include "esp_system.h" +#include class ESP32Board : public mesh::MainBoard { protected: From 35f654ced36a39152606a2aa77d7c95c90e457cd Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Sun, 5 Jul 2026 23:47:27 +0700 Subject: [PATCH 206/214] Fixed hibernate/powerOff for ESP32 boards to stay at uA --- src/helpers/MeshadventurerBoard.h | 30 ---------- variants/heltec_e213/HeltecE213Board.cpp | 27 --------- variants/heltec_e213/HeltecE213Board.h | 3 - variants/heltec_e290/HeltecE290Board.cpp | 27 --------- variants/heltec_e290/HeltecE290Board.h | 3 - variants/heltec_t190/HeltecT190Board.cpp | 27 --------- variants/heltec_t190/HeltecT190Board.h | 3 - .../HeltecTrackerV2Board.cpp | 31 ++-------- .../heltec_tracker_v2/HeltecTrackerV2Board.h | 2 - variants/heltec_v2/HeltecV2Board.h | 25 -------- variants/heltec_v3/HeltecV3Board.h | 29 ---------- variants/heltec_v4/HeltecV4Board.cpp | 31 ++-------- variants/heltec_v4/HeltecV4Board.h | 2 - variants/lilygo_tdeck/TDeckBoard.h | 24 -------- variants/rak3112/RAK3112Board.h | 29 ---------- variants/station_g2/StationG2Board.h | 24 -------- variants/station_g3_esp32/StationG3Board.cpp | 15 +++++ variants/station_g3_esp32/StationG3Board.h | 31 +--------- variants/thinknode_m2/ThinknodeM2Board.cpp | 58 ++++++++----------- variants/thinknode_m2/ThinknodeM2Board.h | 4 -- variants/thinknode_m5/ThinknodeM5Board.cpp | 8 --- variants/thinknode_m5/ThinknodeM5Board.h | 3 - variants/xiao_c3/XiaoC3Board.h | 32 ---------- 23 files changed, 50 insertions(+), 418 deletions(-) create mode 100644 variants/station_g3_esp32/StationG3Board.cpp diff --git a/src/helpers/MeshadventurerBoard.h b/src/helpers/MeshadventurerBoard.h index 65e11102..0325161d 100644 --- a/src/helpers/MeshadventurerBoard.h +++ b/src/helpers/MeshadventurerBoard.h @@ -15,8 +15,6 @@ #include "ESP32Board.h" -#include - class MeshadventurerBoard : public ESP32Board { public: @@ -35,34 +33,6 @@ public: } } - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are held on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - - void powerOff() override { - // TODO: re-enable this when there is a definite wake-up source pin: - // enterDeepSleep(0); - } - uint16_t getBattMilliVolts() override { analogReadResolution(12); diff --git a/variants/heltec_e213/HeltecE213Board.cpp b/variants/heltec_e213/HeltecE213Board.cpp index af115318..88737c4d 100644 --- a/variants/heltec_e213/HeltecE213Board.cpp +++ b/variants/heltec_e213/HeltecE213Board.cpp @@ -20,33 +20,6 @@ void HeltecE213Board::begin() { } } - void HeltecE213Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - - void HeltecE213Board::powerOff() { - enterDeepSleep(0); - } - uint16_t HeltecE213Board::getBattMilliVolts() { analogReadResolution(10); digitalWrite(PIN_ADC_CTRL, HIGH); diff --git a/variants/heltec_e213/HeltecE213Board.h b/variants/heltec_e213/HeltecE213Board.h index 2192c141..fadc038f 100644 --- a/variants/heltec_e213/HeltecE213Board.h +++ b/variants/heltec_e213/HeltecE213Board.h @@ -3,7 +3,6 @@ #include #include #include -#include class HeltecE213Board : public ESP32Board { @@ -13,8 +12,6 @@ public: HeltecE213Board() : periph_power(PIN_VEXT_EN,PIN_VEXT_EN_ACTIVE) { } void begin(); - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); - void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; }; diff --git a/variants/heltec_e290/HeltecE290Board.cpp b/variants/heltec_e290/HeltecE290Board.cpp index 3994a206..96ec59c9 100644 --- a/variants/heltec_e290/HeltecE290Board.cpp +++ b/variants/heltec_e290/HeltecE290Board.cpp @@ -20,33 +20,6 @@ void HeltecE290Board::begin() { } } - void HeltecE290Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - - void HeltecE290Board::powerOff() { - enterDeepSleep(0); - } - uint16_t HeltecE290Board::getBattMilliVolts() { analogReadResolution(10); digitalWrite(PIN_ADC_CTRL, HIGH); diff --git a/variants/heltec_e290/HeltecE290Board.h b/variants/heltec_e290/HeltecE290Board.h index 645ec348..f287227c 100644 --- a/variants/heltec_e290/HeltecE290Board.h +++ b/variants/heltec_e290/HeltecE290Board.h @@ -3,7 +3,6 @@ #include #include #include -#include class HeltecE290Board : public ESP32Board { @@ -13,8 +12,6 @@ public: HeltecE290Board() : periph_power(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE) { } void begin(); - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); - void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; diff --git a/variants/heltec_t190/HeltecT190Board.cpp b/variants/heltec_t190/HeltecT190Board.cpp index 4f35be40..0a16b52b 100644 --- a/variants/heltec_t190/HeltecT190Board.cpp +++ b/variants/heltec_t190/HeltecT190Board.cpp @@ -20,33 +20,6 @@ void HeltecT190Board::begin() { } } - void HeltecT190Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - - void HeltecT190Board::powerOff() { - enterDeepSleep(0); - } - uint16_t HeltecT190Board::getBattMilliVolts() { analogReadResolution(10); digitalWrite(PIN_ADC_CTRL, HIGH); diff --git a/variants/heltec_t190/HeltecT190Board.h b/variants/heltec_t190/HeltecT190Board.h index bc38c1e0..557c070e 100644 --- a/variants/heltec_t190/HeltecT190Board.h +++ b/variants/heltec_t190/HeltecT190Board.h @@ -3,7 +3,6 @@ #include #include #include -#include class HeltecT190Board : public ESP32Board { @@ -13,8 +12,6 @@ public: HeltecT190Board() : periph_power(PIN_VEXT_EN,PIN_VEXT_EN_ACTIVE) { } void begin(); - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); - void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; diff --git a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp index f182c905..99b1cdfe 100644 --- a/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp +++ b/variants/heltec_tracker_v2/HeltecTrackerV2Board.cpp @@ -35,33 +35,12 @@ void HeltecTrackerV2Board::begin() { loRaFEMControl.setRxModeEnable(); } - void HeltecTrackerV2Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + void HeltecTrackerV2Board::powerOff() { + // Turn off PA + digitalWrite(P_LORA_PA_POWER, LOW); + rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_POWER); - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - loRaFEMControl.setRxModeEnableWhenMCUSleep();//It also needs to be enabled in receive mode - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - - void HeltecTrackerV2Board::powerOff() { - enterDeepSleep(0); + ESP32Board::powerOff(); } uint16_t HeltecTrackerV2Board::getBattMilliVolts() { diff --git a/variants/heltec_tracker_v2/HeltecTrackerV2Board.h b/variants/heltec_tracker_v2/HeltecTrackerV2Board.h index ccbecc7a..2bd6a025 100644 --- a/variants/heltec_tracker_v2/HeltecTrackerV2Board.h +++ b/variants/heltec_tracker_v2/HeltecTrackerV2Board.h @@ -3,7 +3,6 @@ #include #include #include -#include #include "LoRaFEMControl.h" class HeltecTrackerV2Board : public ESP32Board { @@ -17,7 +16,6 @@ public: void begin(); void onBeforeTransmit(void) override; void onAfterTransmit(void) override; - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; diff --git a/variants/heltec_v2/HeltecV2Board.h b/variants/heltec_v2/HeltecV2Board.h index fe800890..9b08fe94 100644 --- a/variants/heltec_v2/HeltecV2Board.h +++ b/variants/heltec_v2/HeltecV2Board.h @@ -7,8 +7,6 @@ #define PIN_VBAT_READ 37 #define PIN_LED_BUILTIN 25 -#include - class HeltecV2Board : public ESP32Board { public: void begin() { @@ -26,29 +24,6 @@ public: } } - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_0, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_0); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_0), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_0) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - uint16_t getBattMilliVolts() override { analogReadResolution(10); diff --git a/variants/heltec_v3/HeltecV3Board.h b/variants/heltec_v3/HeltecV3Board.h index ba22a7f2..7e7abe31 100644 --- a/variants/heltec_v3/HeltecV3Board.h +++ b/variants/heltec_v3/HeltecV3Board.h @@ -17,8 +17,6 @@ #define PIN_ADC_CTRL_ACTIVE LOW #define PIN_ADC_CTRL_INACTIVE HIGH -#include - class HeltecV3Board : public ESP32Board { private: bool adc_active_state; @@ -52,33 +50,6 @@ public: } } - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - - void powerOff() override { - enterDeepSleep(0); - } - uint16_t getBattMilliVolts() override { analogReadResolution(10); digitalWrite(PIN_ADC_CTRL, adc_active_state); diff --git a/variants/heltec_v4/HeltecV4Board.cpp b/variants/heltec_v4/HeltecV4Board.cpp index 3978c51e..3f13f41f 100644 --- a/variants/heltec_v4/HeltecV4Board.cpp +++ b/variants/heltec_v4/HeltecV4Board.cpp @@ -32,33 +32,12 @@ void HeltecV4Board::begin() { loRaFEMControl.setRxModeEnable(); } - void HeltecV4Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + void HeltecV4Board::powerOff() { + // Turn off PA + digitalWrite(P_LORA_PA_POWER, LOW); + rtc_gpio_hold_en((gpio_num_t)P_LORA_PA_POWER); - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - loRaFEMControl.setRxModeEnableWhenMCUSleep();//It also needs to be enabled in receive mode - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - - void HeltecV4Board::powerOff() { - enterDeepSleep(0); + ESP32Board::powerOff(); } uint16_t HeltecV4Board::getBattMilliVolts() { diff --git a/variants/heltec_v4/HeltecV4Board.h b/variants/heltec_v4/HeltecV4Board.h index fc37b9f6..55166bb3 100644 --- a/variants/heltec_v4/HeltecV4Board.h +++ b/variants/heltec_v4/HeltecV4Board.h @@ -3,7 +3,6 @@ #include #include #include -#include #include "LoRaFEMControl.h" #ifndef ADC_MULTIPLIER @@ -23,7 +22,6 @@ public: void begin(); void onBeforeTransmit(void) override; void onAfterTransmit(void) override; - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); void powerOff() override; bool setLoRaFemLnaEnabled(bool enable) override; bool canControlLoRaFemLna() const override; diff --git a/variants/lilygo_tdeck/TDeckBoard.h b/variants/lilygo_tdeck/TDeckBoard.h index 7ed007af..e2844360 100644 --- a/variants/lilygo_tdeck/TDeckBoard.h +++ b/variants/lilygo_tdeck/TDeckBoard.h @@ -3,7 +3,6 @@ #include #include #include "helpers/ESP32Board.h" -#include #define PIN_VBAT_READ 4 #define BATTERY_SAMPLES 8 @@ -23,29 +22,6 @@ public: } #endif - void enterDeepSleep(uint32_t secs, int pin_wake_btn) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - uint16_t getBattMilliVolts() { #if defined(PIN_VBAT_READ) && defined(ADC_MULTIPLIER) analogReadResolution(12); diff --git a/variants/rak3112/RAK3112Board.h b/variants/rak3112/RAK3112Board.h index 8ba3197c..704162b8 100644 --- a/variants/rak3112/RAK3112Board.h +++ b/variants/rak3112/RAK3112Board.h @@ -16,8 +16,6 @@ #define ADC_MULTIPLIER (3 * 1.73 * 1.187 * 1000) #define BATTERY_SAMPLES 8 -#include - class RAK3112Board : public ESP32Board { private: bool adc_active_state; @@ -51,33 +49,6 @@ public: } } - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - - void powerOff() override { - enterDeepSleep(0); - } - uint16_t getBattMilliVolts() override { analogReadResolution(12); diff --git a/variants/station_g2/StationG2Board.h b/variants/station_g2/StationG2Board.h index a905682c..d1989ee0 100644 --- a/variants/station_g2/StationG2Board.h +++ b/variants/station_g2/StationG2Board.h @@ -2,7 +2,6 @@ #include #include -#include class StationG2Board : public ESP32Board { public: @@ -21,29 +20,6 @@ public: } } - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - // Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet - } else { - esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - uint16_t getBattMilliVolts() override { return 0; } diff --git a/variants/station_g3_esp32/StationG3Board.cpp b/variants/station_g3_esp32/StationG3Board.cpp new file mode 100644 index 00000000..4a498311 --- /dev/null +++ b/variants/station_g3_esp32/StationG3Board.cpp @@ -0,0 +1,15 @@ +#include "StationG3Board.h" + +void StationG3Board::powerOff() { +#ifdef P_PA1_EN + setPAModeHigh(false); + rtc_gpio_hold_en((gpio_num_t)P_PA1_EN); +#endif + +#ifdef P_PRIMARY_LNA_EN + setPrimaryLNAControl(true); + rtc_gpio_hold_en((gpio_num_t)P_PRIMARY_LNA_EN); +#endif + + ESP32Board::powerOff(); +} diff --git a/variants/station_g3_esp32/StationG3Board.h b/variants/station_g3_esp32/StationG3Board.h index dc440d95..4b1fb81c 100644 --- a/variants/station_g3_esp32/StationG3Board.h +++ b/variants/station_g3_esp32/StationG3Board.h @@ -73,36 +73,7 @@ public: setPrimaryLNAControl(true); } - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - -#ifdef P_PA1_EN - setPAModeHigh(false); - rtc_gpio_hold_en((gpio_num_t)P_PA1_EN); -#endif - -#ifdef P_PRIMARY_LNA_EN - setPrimaryLNAControl(true); - rtc_gpio_hold_en((gpio_num_t)P_PRIMARY_LNA_EN); -#endif - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup((1ULL << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); - } else { - esp_sleep_enable_ext1_wakeup((1ULL << P_LORA_DIO_1) | (1ULL << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - esp_deep_sleep_start(); - } + void powerOff() override; uint16_t getBattMilliVolts() override { return 0; diff --git a/variants/thinknode_m2/ThinknodeM2Board.cpp b/variants/thinknode_m2/ThinknodeM2Board.cpp index 05965103..8d68006d 100644 --- a/variants/thinknode_m2/ThinknodeM2Board.cpp +++ b/variants/thinknode_m2/ThinknodeM2Board.cpp @@ -1,40 +1,30 @@ #include "ThinknodeM2Board.h" - - void ThinknodeM2Board::begin() { - pinMode(PIN_VEXT_EN, OUTPUT); - digitalWrite(PIN_VEXT_EN, !PIN_VEXT_EN_ACTIVE); // force power cycle - delay(20); // allow power rail to discharge - digitalWrite(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE); // turn backlight back on - delay(120); // give display time to bias on cold boot - ESP32Board::begin(); - pinMode(PIN_STATUS_LED, OUTPUT); // init power led - } - - void ThinknodeM2Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { - esp_deep_sleep_start(); - } - - void ThinknodeM2Board::powerOff() { - enterDeepSleep(0); - } - - uint16_t ThinknodeM2Board::getBattMilliVolts() { - analogReadResolution(12); - analogSetPinAttenuation(PIN_VBAT_READ, ADC_11db); - - uint32_t mv = 0; - for (int i = 0; i < 8; ++i) { - mv += analogReadMilliVolts(PIN_VBAT_READ); - delayMicroseconds(200); - } - mv /= 8; - - analogReadResolution(10); - return static_cast(mv * ADC_MULTIPLIER ); + pinMode(PIN_VEXT_EN, OUTPUT); + digitalWrite(PIN_VEXT_EN, !PIN_VEXT_EN_ACTIVE); // force power cycle + delay(20); // allow power rail to discharge + digitalWrite(PIN_VEXT_EN, PIN_VEXT_EN_ACTIVE); // turn backlight back on + delay(120); // give display time to bias on cold boot + ESP32Board::begin(); + pinMode(PIN_STATUS_LED, OUTPUT); // init power led } - const char* ThinknodeM2Board::getManufacturerName() const { - return "Elecrow ThinkNode M2"; +uint16_t ThinknodeM2Board::getBattMilliVolts() { + analogReadResolution(12); + analogSetPinAttenuation(PIN_VBAT_READ, ADC_11db); + + uint32_t mv = 0; + for (int i = 0; i < 8; ++i) { + mv += analogReadMilliVolts(PIN_VBAT_READ); + delayMicroseconds(200); } + mv /= 8; + + analogReadResolution(10); + return static_cast(mv * ADC_MULTIPLIER); +} + +const char *ThinknodeM2Board::getManufacturerName() const { + return "Elecrow ThinkNode M2"; +} diff --git a/variants/thinknode_m2/ThinknodeM2Board.h b/variants/thinknode_m2/ThinknodeM2Board.h index 8011fae6..02556777 100644 --- a/variants/thinknode_m2/ThinknodeM2Board.h +++ b/variants/thinknode_m2/ThinknodeM2Board.h @@ -3,15 +3,11 @@ #include #include #include -#include class ThinknodeM2Board : public ESP32Board { public: - void begin(); - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); - void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; diff --git a/variants/thinknode_m5/ThinknodeM5Board.cpp b/variants/thinknode_m5/ThinknodeM5Board.cpp index c4de538c..2cb138e6 100644 --- a/variants/thinknode_m5/ThinknodeM5Board.cpp +++ b/variants/thinknode_m5/ThinknodeM5Board.cpp @@ -19,14 +19,6 @@ void ThinknodeM5Board::begin() { ESP32Board::begin(); } - void ThinknodeM5Board::enterDeepSleep(uint32_t secs, int pin_wake_btn) { - esp_deep_sleep_start(); - } - - void ThinknodeM5Board::powerOff() { - enterDeepSleep(0); - } - uint16_t ThinknodeM5Board::getBattMilliVolts() { analogReadResolution(12); analogSetPinAttenuation(PIN_VBAT_READ, ADC_11db); diff --git a/variants/thinknode_m5/ThinknodeM5Board.h b/variants/thinknode_m5/ThinknodeM5Board.h index 3c120027..57ff0d00 100644 --- a/variants/thinknode_m5/ThinknodeM5Board.h +++ b/variants/thinknode_m5/ThinknodeM5Board.h @@ -3,7 +3,6 @@ #include #include #include -#include #include extern PCA9557 expander; @@ -13,8 +12,6 @@ class ThinknodeM5Board : public ESP32Board { public: void begin(); - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); - void powerOff() override; uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override ; diff --git a/variants/xiao_c3/XiaoC3Board.h b/variants/xiao_c3/XiaoC3Board.h index 6ea1c15f..c5700c68 100644 --- a/variants/xiao_c3/XiaoC3Board.h +++ b/variants/xiao_c3/XiaoC3Board.h @@ -3,7 +3,6 @@ #include #include -#include #include class XiaoC3Board : public ESP32Board { @@ -40,37 +39,6 @@ public: #endif } - void enterDeepSleep(uint32_t secs, int8_t wake_pin = -1) { - gpio_set_direction(gpio_num_t(P_LORA_DIO_1), GPIO_MODE_INPUT); - if (wake_pin >= 0) { - gpio_set_direction((gpio_num_t)wake_pin, GPIO_MODE_INPUT); - } - - //hold disable, isolate and power domain config functions may be unnecessary - //gpio_deep_sleep_hold_dis(); - //esp_sleep_config_gpio_isolate(); - gpio_deep_sleep_hold_en(); - -#if defined(LORA_TX_BOOST_PIN) - gpio_hold_en((gpio_num_t) LORA_TX_BOOST_PIN); - gpio_deep_sleep_hold_en(); -#endif - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - if (wake_pin >= 0) { - esp_deep_sleep_enable_gpio_wakeup((1 << P_LORA_DIO_1) | (1 << wake_pin), ESP_GPIO_WAKEUP_GPIO_HIGH); - } else { - esp_deep_sleep_enable_gpio_wakeup(1 << P_LORA_DIO_1, ESP_GPIO_WAKEUP_GPIO_HIGH); - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - // Finally set ESP32 into sleep - esp_deep_sleep_start(); // CPU halts here and never returns! - } - #if defined(LORA_TX_BOOST_PIN) || defined(P_LORA_TX_LED) void onBeforeTransmit() override { #if defined(P_LORA_TX_LED) From 46c729b2e93072dcd994d6864830ae38f9f4f80a Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Mon, 6 Jul 2026 09:16:50 +0700 Subject: [PATCH 207/214] Fixed powerOff at uA for Esp32C6 and station g3 --- src/helpers/ESP32Board.cpp | 2 +- variants/station_g3_esp32/StationG3Board.cpp | 15 ++++++++++ variants/station_g3_esp32/StationG3Board.h | 31 +------------------- 3 files changed, 17 insertions(+), 31 deletions(-) create mode 100644 variants/station_g3_esp32/StationG3Board.cpp diff --git a/src/helpers/ESP32Board.cpp b/src/helpers/ESP32Board.cpp index 7da2c7ac..a55abb26 100644 --- a/src/helpers/ESP32Board.cpp +++ b/src/helpers/ESP32Board.cpp @@ -60,7 +60,7 @@ void ESP32Board::enterDeepSleep(uint32_t secs) { // Keep LoRa inactive during deepsleep digitalWrite(P_LORA_NSS, HIGH); -#if CONFIG_IDF_TARGET_ESP32C3 +#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) gpio_hold_en((gpio_num_t)P_LORA_NSS); #else rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); diff --git a/variants/station_g3_esp32/StationG3Board.cpp b/variants/station_g3_esp32/StationG3Board.cpp new file mode 100644 index 00000000..4a498311 --- /dev/null +++ b/variants/station_g3_esp32/StationG3Board.cpp @@ -0,0 +1,15 @@ +#include "StationG3Board.h" + +void StationG3Board::powerOff() { +#ifdef P_PA1_EN + setPAModeHigh(false); + rtc_gpio_hold_en((gpio_num_t)P_PA1_EN); +#endif + +#ifdef P_PRIMARY_LNA_EN + setPrimaryLNAControl(true); + rtc_gpio_hold_en((gpio_num_t)P_PRIMARY_LNA_EN); +#endif + + ESP32Board::powerOff(); +} diff --git a/variants/station_g3_esp32/StationG3Board.h b/variants/station_g3_esp32/StationG3Board.h index dc440d95..4b1fb81c 100644 --- a/variants/station_g3_esp32/StationG3Board.h +++ b/variants/station_g3_esp32/StationG3Board.h @@ -73,36 +73,7 @@ public: setPrimaryLNAControl(true); } - void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) { - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); - - rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1); - - rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS); - -#ifdef P_PA1_EN - setPAModeHigh(false); - rtc_gpio_hold_en((gpio_num_t)P_PA1_EN); -#endif - -#ifdef P_PRIMARY_LNA_EN - setPrimaryLNAControl(true); - rtc_gpio_hold_en((gpio_num_t)P_PRIMARY_LNA_EN); -#endif - - if (pin_wake_btn < 0) { - esp_sleep_enable_ext1_wakeup((1ULL << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); - } else { - esp_sleep_enable_ext1_wakeup((1ULL << P_LORA_DIO_1) | (1ULL << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); - } - - if (secs > 0) { - esp_sleep_enable_timer_wakeup(secs * 1000000); - } - - esp_deep_sleep_start(); - } + void powerOff() override; uint16_t getBattMilliVolts() override { return 0; From 3ee2f778771a14d1606eb711b8030033cfca4a6f Mon Sep 17 00:00:00 2001 From: Florent Date: Mon, 6 Jul 2026 08:46:57 -0400 Subject: [PATCH 208/214] restore display and radio poweroff in ui --- examples/companion_radio/ui-new/UITask.cpp | 3 +++ examples/companion_radio/ui-orig/UITask.cpp | 2 ++ examples/companion_radio/ui-tiny/UITask.cpp | 2 ++ 3 files changed, 7 insertions(+) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 403ea463..28591cc1 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -697,6 +697,9 @@ void UITask::shutdown(bool restart){ if (restart) { _board->reboot(); } else { + // still necessary until all boards are refactored to use poweroff + _display->turnOff(); + radio_driver.powerOff(); // Power off board including radio, display, GPS and components _board->powerOff(); } diff --git a/examples/companion_radio/ui-orig/UITask.cpp b/examples/companion_radio/ui-orig/UITask.cpp index b48f6412..34a7342b 100644 --- a/examples/companion_radio/ui-orig/UITask.cpp +++ b/examples/companion_radio/ui-orig/UITask.cpp @@ -307,6 +307,8 @@ void UITask::shutdown(bool restart){ if (restart) { _board->reboot(); } else { + _display->turnOff(); + radio_driver.powerOff(); // Power off board including radio, display, GPS and components _board->powerOff(); } diff --git a/examples/companion_radio/ui-tiny/UITask.cpp b/examples/companion_radio/ui-tiny/UITask.cpp index 452c02d4..a6cbe9de 100644 --- a/examples/companion_radio/ui-tiny/UITask.cpp +++ b/examples/companion_radio/ui-tiny/UITask.cpp @@ -566,6 +566,8 @@ void UITask::shutdown(bool restart){ if (restart) { _board->reboot(); } else { + _display->turnOff(); + radio_driver.powerOff(); // Power off board including radio, display, GPS and components _board->powerOff(); } From fec88e13009fe215e1bebcc09a6c85196ff6ac0c Mon Sep 17 00:00:00 2001 From: Florent Date: Mon, 6 Jul 2026 12:04:42 -0400 Subject: [PATCH 209/214] fix tenstar c3 repeater build --- variants/tenstar_c3/target.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/tenstar_c3/target.h b/variants/tenstar_c3/target.h index b3ee6d17..7839fa97 100644 --- a/variants/tenstar_c3/target.h +++ b/variants/tenstar_c3/target.h @@ -3,7 +3,7 @@ #define RADIOLIB_STATIC_ONLY 1 #include #include -#include +#include <../variants/xiao_c3/XiaoC3Board.h> #include #include #include From f747f967396ef38443329a451d06133daec77909 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 6 Jul 2026 15:54:08 -0700 Subject: [PATCH 210/214] Add flood channel controls and cascade prompt details --- build.sh | 2 +- docs/cli_commands.md | 68 +++++ examples/simple_repeater/MyMesh.cpp | 328 +++++++++++++++++++++++++ examples/simple_repeater/MyMesh.h | 22 ++ examples/simple_room_server/MyMesh.cpp | 1 + examples/simple_sensor/SensorMesh.cpp | 1 + src/helpers/CommonCLI.cpp | 165 ++++++++++++- src/helpers/CommonCLI.h | 27 ++ 8 files changed, 611 insertions(+), 3 deletions(-) diff --git a/build.sh b/build.sh index 0370063a..1697e0c4 100755 --- a/build.sh +++ b/build.sh @@ -527,7 +527,7 @@ prompt_for_radio_build_settings() { prompt_for_firmware_profile_settings() { local -a options=( "Keep target defaults" - "Cascade: path.hash.mode=2 / loop.detect=minimal / rxdelay=2 / agc.reset.interval=8 / advert.interval=0 / flood.advert.interval=83 / multi.acks=1" + "Cascade: path.hash.mode=2 / loop.detect=minimal / rxdelay=2 / agc.reset.interval=8 / advert.interval=0 / flood.advert.interval=83 / multi.acks=1 / companion.manual.add=1 / companion.autoadd=0" ) clear_firmware_profile_overrides diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 48b76245..1971709a 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -768,6 +768,74 @@ send text.flood checking ridge link --- +#### Forward flood group data packets on repeaters +**Usage:** +- `get flood.channel.data` +- `set flood.channel.data ` + +**Parameters:** +- `on`: Retransmit received flood `GRP_DATA` channel packets. +- `off`: Do not retransmit received flood `GRP_DATA` channel packets. + +**Default:** `on` + +**Forwarding behavior:** Repeater firmware only. The repeater still receives and +logs the packet when logging is enabled; this only blocks retransmission. +This is checked before `flood.channel.block` and applies to every flood +`GRP_DATA` packet regardless of channel key. Flood group text (`GRP_TXT`) is +unaffected. + +--- + +#### Block selected flood channel packets on repeaters +**Usage:** +- `get flood.channel.block` +- `get flood.channel.block.` +- `get flood.channel.block ` +- `set flood.channel.block ` +- `set flood.channel.block. ` +- `set flood.channel.block #channel` +- `set flood.channel.block. #channel` +- `del flood.channel.block.` +- `del flood.channel.block ` + +**Parameters:** +- `n`: Slot number from `1` to `15`. +- `key`: 128-bit or 256-bit channel key as hex. +- `#channel`: Public hashtag channel name; derives the 128-bit channel key from the hashtag and is stored as the row name. +- `name`: Local label for hex-key rows. Not needed for `#channel`; any extra text after `#channel` is ignored. +- `8_hex_prefix`: First 4 bytes of the derived channel hash, shown by single-entry `get`. + +**Slot behavior:** Without `.n`, `set flood.channel.block` updates an existing +row with the same derived channel prefix or name, otherwise it uses the next +empty slot. If all 15 slots are full, the command fails. With `.n`, the command +writes that slot. + +**Forwarding behavior:** Repeater firmware only. This only affects received +flood `GRP_TXT` and `GRP_DATA` channel packets. The repeater still receives and +logs the packet, but it does not retransmit it when a configured block entry can +validate/decode it. If `flood.channel.data` is `off`, all flood `GRP_DATA` +packets are blocked before this per-channel check runs. + +**Matching behavior:** Each block entry stores the first 4 bytes of the derived +channel hash for display and lookup. Current group packets carry only the first +channel-hash byte, so that byte is used as a cheap prefilter. Only entries whose +first hash byte matches the packet try MAC/decrypt with their stored key. If +multiple blocked channels share the same first byte, the repeater tries each +matching key until one validates; the packet is blocked only after a successful +MAC/decrypt. + +**Examples:** +``` +set flood.channel.block #test +set flood.channel.block.2 9cd8fcf22a47333b591d96a2b848b73f #test +get flood.channel.block +get flood.channel.block #test +del flood.channel.block.2 +``` + +--- + ### ACL #### Add, update or remove permissions for a companion diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 30e437b6..e3054035 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -82,6 +82,8 @@ #define LAZY_CONTACTS_WRITE_DELAY 5000 +#define FLOOD_CHANNEL_BLOCK_FILE "/flood_ch_block" + #ifndef REPEATERS_CHANNEL_KEY_HEX #define REPEATERS_CHANNEL_KEY_HEX "89db441e2814dccf0dbd2e8cc5f501a3" #endif @@ -194,6 +196,25 @@ static bool buildRepeatersChannel(mesh::GroupChannel& channel) { return true; } +static File openFloodChannelBlockRead(FILESYSTEM* fs, const char* filename) { +#if defined(RP2040_PLATFORM) + return fs->open(filename, "r"); +#else + return fs->open(filename); +#endif +} + +static File openFloodChannelBlockWrite(FILESYSTEM* fs, const char* filename) { +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + fs->remove(filename); + return fs->open(filename, FILE_O_WRITE); +#elif defined(RP2040_PLATFORM) + return fs->open(filename, "w"); +#else + return fs->open(filename, "w", true); +#endif +} + static uint8_t batteryPercentFromMilliVolts(uint16_t batt_mv) { const int min_mv = BATT_MIN_MILLIVOLTS; const int max_mv = BATT_MAX_MILLIVOLTS; @@ -603,12 +624,46 @@ void MyMesh::sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, ui } } +bool MyMesh::floodChannelBlockMatches(const FloodChannelBlockEntry& entry, const mesh::Packet* packet) const { + if (!entry.active || packet == NULL || !packet->isRouteFlood()) { + return false; + } + uint8_t type = packet->getPayloadType(); + if (type != PAYLOAD_TYPE_GRP_TXT && type != PAYLOAD_TYPE_GRP_DATA) { + return false; + } + if (packet->payload_len <= PATH_HASH_SIZE + CIPHER_MAC_SIZE || packet->payload[0] != entry.hash_prefix[0]) { + return false; + } + + uint8_t data[MAX_PACKET_PAYLOAD]; + int len = mesh::Utils::MACThenDecrypt(entry.secret, data, &packet->payload[PATH_HASH_SIZE], + packet->payload_len - PATH_HASH_SIZE); + return len > 0; +} + +bool MyMesh::shouldBlockFloodChannelForward(const mesh::Packet* packet) const { + for (int i = 0; i < FLOOD_CHANNEL_BLOCK_SLOTS; i++) { + if (floodChannelBlockMatches(flood_channel_blocks[i], packet)) { + MESH_DEBUG_PRINTLN("allowPacketForward: flood.channel.block matched slot=%d name=%s", + i + 1, flood_channel_blocks[i].name); + return true; + } + } + return false; +} + bool MyMesh::allowPacketForward(const mesh::Packet *packet) { if (_prefs.disable_fwd) return false; if (packet->isRouteFlood()) { if (packet->getPathHashCount() >= _prefs.flood_max) return false; if (packet->getRouteType() == ROUTE_TYPE_FLOOD && packet->getPathHashCount() >= _prefs.flood_max_unscoped) return false; if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->getPathHashCount() >= _prefs.flood_max_advert) return false; + if (!_prefs.flood_channel_data_enabled && packet->getPayloadType() == PAYLOAD_TYPE_GRP_DATA) { + MESH_DEBUG_PRINTLN("allowPacketForward: flood.channel.data off, blocking GRP_DATA"); + return false; + } + if (shouldBlockFloodChannelForward(packet)) return false; } if (packet->isRouteFlood() && recv_pkt_region == NULL) { MESH_DEBUG_PRINTLN("allowPacketForward: unknown transport code, or wildcard not allowed for FLOOD packet"); @@ -2066,6 +2121,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc region_load_active = false; memset(flood_retry_bridge_states, 0, sizeof(flood_retry_bridge_states)); recv_pkt_region = NULL; + memset(flood_channel_blocks, 0, sizeof(flood_channel_blocks)); #if MAX_NEIGHBOURS memset(neighbours, 0, sizeof(neighbours)); @@ -2115,6 +2171,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_retry_max_path = FLOOD_RETRY_ROOFTOP_MAX_PATH; _prefs.flood_retry_bridge_enabled = 0; _prefs.flood_retry_advert_enabled = FLOOD_RETRY_ADVERT_DEFAULT; + _prefs.flood_channel_data_enabled = 1; _prefs.battery_alert_enabled = 0; _prefs.battery_alert_low_percent = BATTERY_ALERT_LOW_PERCENT_DEFAULT; _prefs.battery_alert_critical_percent = BATTERY_ALERT_CRITICAL_PERCENT_DEFAULT; @@ -2158,6 +2215,7 @@ void MyMesh::begin(FILESYSTEM *fs) { acl.load(_fs, self_id); // TODO: key_store.begin(); region_map.load(_fs); + loadFloodChannelBlocks(); // establish default-scope { @@ -2930,6 +2988,276 @@ void MyMesh::onDefaultRegionChanged(const RegionEntry* r) { } } +void MyMesh::clearFloodChannelBlockEntry(FloodChannelBlockEntry& entry) { + memset(&entry, 0, sizeof(entry)); +} + +void MyMesh::deriveFloodChannelBlockPrefix(const uint8_t* secret, uint8_t key_len, + uint8_t prefix[FLOOD_CHANNEL_BLOCK_PREFIX_LEN]) const { + mesh::Utils::sha256(prefix, FLOOD_CHANNEL_BLOCK_PREFIX_LEN, secret, key_len); +} + +void MyMesh::loadFloodChannelBlocks() { + memset(flood_channel_blocks, 0, sizeof(flood_channel_blocks)); + if (_fs == NULL || !_fs->exists(FLOOD_CHANNEL_BLOCK_FILE)) { + return; + } + + File file = openFloodChannelBlockRead(_fs, FLOOD_CHANNEL_BLOCK_FILE); + if (!file) { + return; + } + + uint8_t magic[4]; + uint8_t count = 0; + bool success = file.read(magic, sizeof(magic)) == sizeof(magic) + && memcmp(magic, "FCB1", sizeof(magic)) == 0 + && file.read(&count, sizeof(count)) == sizeof(count); + + for (int i = 0; success && i < count && i < FLOOD_CHANNEL_BLOCK_SLOTS; i++) { + uint8_t active = 0; + uint8_t key_len = 0; + uint8_t hash_prefix[FLOOD_CHANNEL_BLOCK_PREFIX_LEN]; + uint8_t secret[PUB_KEY_SIZE]; + char name[FLOOD_CHANNEL_BLOCK_NAME_LEN]; + + success = file.read(&active, sizeof(active)) == sizeof(active); + success = success && file.read(&key_len, sizeof(key_len)) == sizeof(key_len); + success = success && file.read(hash_prefix, sizeof(hash_prefix)) == sizeof(hash_prefix); + success = success && file.read(secret, sizeof(secret)) == sizeof(secret); + success = success && file.read((uint8_t*)name, sizeof(name)) == sizeof(name); + if (!success) { + break; + } + + name[sizeof(name) - 1] = 0; + if (active && (key_len == CIPHER_KEY_SIZE || key_len == PUB_KEY_SIZE) && name[0] != 0) { + auto& entry = flood_channel_blocks[i]; + entry.active = true; + entry.key_len = key_len; + memcpy(entry.secret, secret, sizeof(entry.secret)); + if (entry.key_len == CIPHER_KEY_SIZE) { + memset(&entry.secret[CIPHER_KEY_SIZE], 0, PUB_KEY_SIZE - CIPHER_KEY_SIZE); + } + deriveFloodChannelBlockPrefix(entry.secret, entry.key_len, entry.hash_prefix); + StrHelper::strncpy(entry.name, name, sizeof(entry.name)); + } + } + + file.close(); +} + +bool MyMesh::saveFloodChannelBlocks() { + if (_fs == NULL) { + return false; + } + + File file = openFloodChannelBlockWrite(_fs, FLOOD_CHANNEL_BLOCK_FILE); + if (!file) { + return false; + } + + const uint8_t magic[4] = {'F', 'C', 'B', '1'}; + uint8_t count = FLOOD_CHANNEL_BLOCK_SLOTS; + bool success = file.write(magic, sizeof(magic)) == sizeof(magic); + success = success && file.write(&count, sizeof(count)) == sizeof(count); + + for (int i = 0; success && i < FLOOD_CHANNEL_BLOCK_SLOTS; i++) { + const auto& entry = flood_channel_blocks[i]; + uint8_t active = entry.active ? 1 : 0; + success = file.write(&active, sizeof(active)) == sizeof(active); + success = success && file.write(&entry.key_len, sizeof(entry.key_len)) == sizeof(entry.key_len); + success = success && file.write(entry.hash_prefix, sizeof(entry.hash_prefix)) == sizeof(entry.hash_prefix); + success = success && file.write(entry.secret, sizeof(entry.secret)) == sizeof(entry.secret); + success = success && file.write((const uint8_t*)entry.name, sizeof(entry.name)) == sizeof(entry.name); + } + + file.close(); + return success; +} + +static void trimFloodChannelBlockSelector(const char* selector, char* dest, size_t dest_len) { + selector = skipLocalSpaces(selector); + StrHelper::strncpy(dest, selector == NULL ? "" : selector, dest_len); + size_t len = strlen(dest); + while (len > 0 && dest[len - 1] == ' ') { + dest[--len] = 0; + } +} + +static bool parseFloodChannelBlockPrefixSelector(const char* selector, + uint8_t prefix[FLOOD_CHANNEL_BLOCK_PREFIX_LEN]) { + char text[16]; + trimFloodChannelBlockSelector(selector, text, sizeof(text)); + if (strlen(text) != FLOOD_CHANNEL_BLOCK_PREFIX_LEN * 2) { + return false; + } + for (int i = 0; i < FLOOD_CHANNEL_BLOCK_PREFIX_LEN * 2; i++) { + if (!mesh::Utils::isHexChar(text[i])) { + return false; + } + } + return mesh::Utils::fromHex(prefix, FLOOD_CHANNEL_BLOCK_PREFIX_LEN, text); +} + +int MyMesh::findFloodChannelBlockBySelector(const char* selector) const { + uint8_t prefix[FLOOD_CHANNEL_BLOCK_PREFIX_LEN]; + if (parseFloodChannelBlockPrefixSelector(selector, prefix)) { + for (int i = 0; i < FLOOD_CHANNEL_BLOCK_SLOTS; i++) { + const auto& entry = flood_channel_blocks[i]; + if (entry.active && memcmp(entry.hash_prefix, prefix, sizeof(entry.hash_prefix)) == 0) { + return i; + } + } + return -1; + } + + int index = 0; + if (parsePositiveSelector(selector, index)) { + return (index >= 1 && index <= FLOOD_CHANNEL_BLOCK_SLOTS) ? index - 1 : -1; + } + + char name[FLOOD_CHANNEL_BLOCK_NAME_LEN]; + trimFloodChannelBlockSelector(selector, name, sizeof(name)); + if (name[0] == 0) { + return -1; + } + for (int i = 0; i < FLOOD_CHANNEL_BLOCK_SLOTS; i++) { + const auto& entry = flood_channel_blocks[i]; + if (entry.active && strcmp(entry.name, name) == 0) { + return i; + } + } + return -1; +} + +int MyMesh::findFloodChannelBlockSlot(const uint8_t prefix[FLOOD_CHANNEL_BLOCK_PREFIX_LEN], const char* name) const { + int free_slot = -1; + for (int i = 0; i < FLOOD_CHANNEL_BLOCK_SLOTS; i++) { + const auto& entry = flood_channel_blocks[i]; + if (entry.active) { + if (memcmp(entry.hash_prefix, prefix, FLOOD_CHANNEL_BLOCK_PREFIX_LEN) == 0 || strcmp(entry.name, name) == 0) { + return i; + } + } else if (free_slot < 0) { + free_slot = i; + } + } + return free_slot; +} + +void MyMesh::formatFloodChannelBlockDetail(char* reply, int idx) const { + if (idx < 0 || idx >= FLOOD_CHANNEL_BLOCK_SLOTS) { + strcpy(reply, "Err - not found"); + return; + } + + const auto& entry = flood_channel_blocks[idx]; + if (!entry.active) { + snprintf(reply, 160, "> %d empty", idx + 1); + return; + } + + char prefix[FLOOD_CHANNEL_BLOCK_PREFIX_LEN * 2 + 1]; + mesh::Utils::toHex(prefix, entry.hash_prefix, FLOOD_CHANNEL_BLOCK_PREFIX_LEN); + snprintf(reply, 160, "> %d %s %u %s", idx + 1, prefix, (unsigned int)entry.key_len * 8, entry.name); +} + +void MyMesh::setFloodChannelBlock(int index, const uint8_t* secret, uint8_t key_len, + const char* name, char* reply) { + if ((key_len != CIPHER_KEY_SIZE && key_len != PUB_KEY_SIZE) || secret == NULL || name == NULL || name[0] == 0) { + strcpy(reply, "Err - bad params"); + return; + } + if (index < 0 || index > FLOOD_CHANNEL_BLOCK_SLOTS) { + snprintf(reply, 160, "Err - index 1-%d", FLOOD_CHANNEL_BLOCK_SLOTS); + return; + } + + uint8_t prefix[FLOOD_CHANNEL_BLOCK_PREFIX_LEN]; + deriveFloodChannelBlockPrefix(secret, key_len, prefix); + int slot = index > 0 ? index - 1 : findFloodChannelBlockSlot(prefix, name); + if (slot < 0 || slot >= FLOOD_CHANNEL_BLOCK_SLOTS) { + strcpy(reply, "Err - block list full"); + return; + } + + auto& entry = flood_channel_blocks[slot]; + clearFloodChannelBlockEntry(entry); + entry.active = true; + entry.key_len = key_len; + memcpy(entry.secret, secret, PUB_KEY_SIZE); + if (entry.key_len == CIPHER_KEY_SIZE) { + memset(&entry.secret[CIPHER_KEY_SIZE], 0, PUB_KEY_SIZE - CIPHER_KEY_SIZE); + } + deriveFloodChannelBlockPrefix(entry.secret, entry.key_len, entry.hash_prefix); + StrHelper::strncpy(entry.name, name, sizeof(entry.name)); + + if (!saveFloodChannelBlocks()) { + strcpy(reply, "Err - save failed"); + return; + } + formatFloodChannelBlockDetail(reply, slot); +} + +void MyMesh::formatFloodChannelBlocks(const char* selector, char* reply) { + if (!selectorIsEmpty(selector)) { + int idx = findFloodChannelBlockBySelector(selector); + if (idx < 0) { + strcpy(reply, "Err - not found"); + } else { + formatFloodChannelBlockDetail(reply, idx); + } + return; + } + + char* out = reply; + size_t remaining = 160; + int written = snprintf(out, remaining, ">"); + if (written < 0 || (size_t)written >= remaining) { + reply[0] = 0; + return; + } + out += written; + remaining -= written; + + for (int i = 0; i < FLOOD_CHANNEL_BLOCK_SLOTS && remaining > 1; i++) { + char display[8]; + const auto& entry = flood_channel_blocks[i]; + if (!entry.active) { + strcpy(display, "-"); + } else { + StrHelper::strncpy(display, entry.name, sizeof(display)); + if (strlen(entry.name) >= sizeof(display)) { + display[sizeof(display) - 2] = '~'; + display[sizeof(display) - 1] = 0; + } + } + written = snprintf(out, remaining, " %d:%s", i + 1, display); + if (written < 0 || (size_t)written >= remaining) { + out[remaining - 1] = 0; + break; + } + out += written; + remaining -= written; + } +} + +void MyMesh::deleteFloodChannelBlock(const char* selector, char* reply) { + int idx = findFloodChannelBlockBySelector(selector); + if (idx < 0 || idx >= FLOOD_CHANNEL_BLOCK_SLOTS || !flood_channel_blocks[idx].active) { + strcpy(reply, "Err - not found"); + return; + } + + clearFloodChannelBlockEntry(flood_channel_blocks[idx]); + if (!saveFloodChannelBlocks()) { + strcpy(reply, "Err - save failed"); + return; + } + strcpy(reply, "OK"); +} + void MyMesh::formatStatsReply(char *reply) { StatsFormatHelper::formatCoreStats(reply, board, *_ms, _err_flags, _mgr); } diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index c521c80c..83485b25 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -132,7 +132,15 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { uint8_t progress_marker; bool active; }; + struct FloodChannelBlockEntry { + bool active; + uint8_t key_len; + uint8_t hash_prefix[FLOOD_CHANNEL_BLOCK_PREFIX_LEN]; + uint8_t secret[PUB_KEY_SIZE]; + char name[FLOOD_CHANNEL_BLOCK_NAME_LEN]; + }; mutable FloodRetryBridgeState flood_retry_bridge_states[MAX_FLOOD_RETRY_SLOTS]; + FloodChannelBlockEntry flood_channel_blocks[FLOOD_CHANNEL_BLOCK_SLOTS]; uint32_t pending_discover_tag; unsigned long pending_discover_until; bool region_load_active; @@ -193,6 +201,16 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { void applySavedRadioParams(); void processScheduledRadioSettings(); bool isMillisTimerDue(unsigned long timestamp) const; + void loadFloodChannelBlocks(); + bool saveFloodChannelBlocks(); + void clearFloodChannelBlockEntry(FloodChannelBlockEntry& entry); + void deriveFloodChannelBlockPrefix(const uint8_t* secret, uint8_t key_len, + uint8_t prefix[FLOOD_CHANNEL_BLOCK_PREFIX_LEN]) const; + bool floodChannelBlockMatches(const FloodChannelBlockEntry& entry, const mesh::Packet* packet) const; + bool shouldBlockFloodChannelForward(const mesh::Packet* packet) const; + int findFloodChannelBlockBySelector(const char* selector) const; + int findFloodChannelBlockSlot(const uint8_t prefix[FLOOD_CHANNEL_BLOCK_PREFIX_LEN], const char* name) const; + void formatFloodChannelBlockDetail(char* reply, int idx) const; bool hasScheduledRadioWorkDue() const; uint32_t limitSleepToMillisTimer(unsigned long timestamp, uint32_t sleep_secs) const; uint32_t limitSleepToRtcTime(uint32_t timestamp, uint32_t sleep_secs) const; @@ -323,6 +341,10 @@ public: void startRegionsLoad() override; bool saveRegions() override; void onDefaultRegionChanged(const RegionEntry* r) override; + void setFloodChannelBlock(int index, const uint8_t* secret, uint8_t key_len, + const char* name, char* reply) override; + void formatFloodChannelBlocks(const char* selector, char* reply) override; + void deleteFloodChannelBlock(const char* selector, char* reply) override; mesh::LocalIdentity& getSelfId() override { return self_id; } diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index dbb7eefa..b97228f3 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -648,6 +648,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_max = 64; _prefs.flood_max_unscoped = 64; _prefs.flood_max_advert = 8; + _prefs.flood_channel_data_enabled = 1; _prefs.interference_threshold = 0; // disabled _prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on') #ifdef ROOM_PASSWORD diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index 59c9aa09..99e10bc8 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -728,6 +728,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise _prefs.flood_advert_interval = 0; // disabled _prefs.disable_fwd = true; _prefs.flood_max = 64; + _prefs.flood_channel_data_enabled = 1; _prefs.interference_threshold = 0; // disabled _prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on') diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 9c312deb..c43b2948 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -380,6 +380,69 @@ static bool parseFloodRetryPrefixList(uint8_t dest[][FLOOD_RETRY_PREFIX_LEN], ui return true; } +static bool parseFloodChannelBlockKey(const char* text, uint8_t secret[PUB_KEY_SIZE], uint8_t& key_len) { + if (text == NULL || text[0] == 0) { + return false; + } + + memset(secret, 0, PUB_KEY_SIZE); + if (text[0] == '#') { + if (!isValidName(text)) { + return false; + } + mesh::Utils::sha256(secret, CIPHER_KEY_SIZE, (const uint8_t*)text, strlen(text)); + key_len = CIPHER_KEY_SIZE; + return true; + } + + size_t hex_len = strlen(text); + if (!(hex_len == CIPHER_KEY_SIZE * 2 || hex_len == PUB_KEY_SIZE * 2)) { + return false; + } + for (size_t i = 0; i < hex_len; i++) { + if (!mesh::Utils::isHexChar(text[i])) { + return false; + } + } + + key_len = (uint8_t)(hex_len / 2); + return mesh::Utils::fromHex(secret, key_len, text); +} + +static bool parseFloodChannelBlockDotIndex(const char*& cursor, int& index) { + if (*cursor != '.') { + index = 0; + return true; + } + + cursor++; + if (*cursor < '0' || *cursor > '9') { + return false; + } + int value = 0; + while (*cursor >= '0' && *cursor <= '9') { + value = (value * 10) + (*cursor - '0'); + if (value > FLOOD_CHANNEL_BLOCK_SLOTS) { + return false; + } + cursor++; + } + if (value < 1) { + return false; + } + index = value; + return true; +} + +static void copyTrimmedFloodChannelBlockName(char* dest, size_t dest_len, const char* src) { + src = skipSpacesConst(src); + StrHelper::strncpy(dest, src, dest_len); + size_t len = strlen(dest); + while (len > 0 && dest[len - 1] == ' ') { + dest[--len] = 0; + } +} + static void applyDirectRetryPreset(NodePrefs* prefs, uint8_t preset) { prefs->retry_preset = preset; if (preset == RETRY_PRESET_INFRA) { @@ -547,6 +610,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->battery_alert_low_percent = BATTERY_ALERT_LOW_PERCENT_DEFAULT; _prefs->battery_alert_critical_percent = BATTERY_ALERT_CRITICAL_PERCENT_DEFAULT; _prefs->direct_retry_recent_enabled = DIRECT_RETRY_RECENT_DEFAULT; + _prefs->flood_channel_data_enabled = 1; bool has_flood_retry_prefs = file.available() >= 2; if (has_flood_retry_prefs) { file.read((uint8_t *)&_prefs->flood_retry_attempts, sizeof(_prefs->flood_retry_attempts)); // 311 @@ -578,8 +642,11 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { if (file.available() >= (int)sizeof(_prefs->direct_retry_recent_enabled)) { file.read((uint8_t *)&_prefs->direct_retry_recent_enabled, sizeof(_prefs->direct_retry_recent_enabled)); } + if (file.available() >= (int)sizeof(_prefs->flood_channel_data_enabled)) { + file.read((uint8_t *)&_prefs->flood_channel_data_enabled, sizeof(_prefs->flood_channel_data_enabled)); + } } - // next: 672 + // next: 673 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -639,6 +706,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->flood_retry_advert_enabled = constrain(_prefs->flood_retry_advert_enabled, 0, 1); _prefs->battery_alert_enabled = constrain(_prefs->battery_alert_enabled, 0, 1); _prefs->direct_retry_recent_enabled = constrain(_prefs->direct_retry_recent_enabled, 0, 1); + _prefs->flood_channel_data_enabled = constrain(_prefs->flood_channel_data_enabled, 0, 1); if (_prefs->battery_alert_low_percent < 1 || _prefs->battery_alert_low_percent > 100 || _prefs->battery_alert_critical_percent >= _prefs->battery_alert_low_percent) { @@ -736,7 +804,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->battery_alert_low_percent, sizeof(_prefs->battery_alert_low_percent)); file.write((uint8_t *)&_prefs->battery_alert_critical_percent, sizeof(_prefs->battery_alert_critical_percent)); file.write((uint8_t *)&_prefs->direct_retry_recent_enabled, sizeof(_prefs->direct_retry_recent_enabled)); - // next: 672 + file.write((uint8_t *)&_prefs->flood_channel_data_enabled, sizeof(_prefs->flood_channel_data_enabled)); + // next: 673 file.close(); } @@ -1380,6 +1449,54 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { strcpy(reply, "Error, must be 0-5000 ms"); } + } else if (memcmp(config, "flood.channel.data ", 19) == 0) { + if (strcmp(&config[19], "on") == 0) { + _prefs->flood_channel_data_enabled = 1; + savePrefs(); + strcpy(reply, "OK"); + } else if (strcmp(&config[19], "off") == 0) { + _prefs->flood_channel_data_enabled = 0; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be on or off"); + } + } else if (memcmp(config, "flood.channel.block", 19) == 0 + && (config[19] == ' ' || config[19] == '.')) { + const char* cursor = &config[19]; + int index = 0; + if (!parseFloodChannelBlockDotIndex(cursor, index)) { + sprintf(reply, "Error, index 1-%d", FLOOD_CHANNEL_BLOCK_SLOTS); + return; + } + cursor = skipSpacesConst(cursor); + + const char* key_start = cursor; + while (*cursor && *cursor != ' ') cursor++; + size_t key_len_text = cursor - key_start; + char key_text[PUB_KEY_SIZE * 2 + 1]; + if (key_len_text == 0 || key_len_text >= sizeof(key_text)) { + strcpy(reply, "Error, use: set flood.channel.block[.n] |#channel"); + return; + } + memcpy(key_text, key_start, key_len_text); + key_text[key_len_text] = 0; + + char name[FLOOD_CHANNEL_BLOCK_NAME_LEN]; + if (key_text[0] == '#') { + StrHelper::strncpy(name, key_text, sizeof(name)); + } else { + copyTrimmedFloodChannelBlockName(name, sizeof(name), cursor); + } + uint8_t secret[PUB_KEY_SIZE]; + uint8_t decoded_key_len = 0; + if (!parseFloodChannelBlockKey(key_text, secret, decoded_key_len)) { + strcpy(reply, "Error, key must be 128/256-bit hex or #channel"); + } else if (name[0] == 0 || !isValidName(name)) { + strcpy(reply, "Error, bad name"); + } else { + _callbacks->setFloodChannelBlock(index, secret, decoded_key_len, name, reply); + } } else if (memcmp(config, "flood.retry.count ", 18) == 0) { int attempts = looksUnsignedInteger(&config[18]) ? _atoi(&config[18]) : -1; if (attempts >= 0 && attempts <= 15) { @@ -1702,6 +1819,28 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "> %s", StrHelper::ftoa(_prefs->rx_delay_base)); } else if (memcmp(config, "txdelay", 7) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor)); + } else if (memcmp(config, "flood.channel.data", 18) == 0) { + sprintf(reply, "> %s", _prefs->flood_channel_data_enabled ? "on" : "off"); + } else if (memcmp(config, "flood.channel.block", 19) == 0 + && (config[19] == 0 || config[19] == ' ' || config[19] == '.')) { + const char* cursor = &config[19]; + int index = 0; + if (!parseFloodChannelBlockDotIndex(cursor, index)) { + sprintf(reply, "Error, index 1-%d", FLOOD_CHANNEL_BLOCK_SLOTS); + return; + } + cursor = skipSpacesConst(cursor); + char selector[8]; + if (index > 0 && *cursor != 0) { + strcpy(reply, "Error, use index or selector"); + return; + } + if (index > 0) { + snprintf(selector, sizeof(selector), "%d", index); + _callbacks->formatFloodChannelBlocks(selector, reply); + } else { + _callbacks->formatFloodChannelBlocks(cursor, reply); + } } else if (memcmp(config, "flood.max.advert", 16) == 0) { sprintf(reply, "> %d", (uint32_t)_prefs->flood_max_advert); } else if (memcmp(config, "flood.max.unscoped", 18) == 0) { @@ -1880,6 +2019,28 @@ void CommonCLI::handleDelCmd(char* command, char* reply) { _callbacks->deleteScheduledRadioParams(true, skipSpacesConst(&config[11]), reply); } else if (memcmp(config, "radioat", 7) == 0 && (config[7] == 0 || config[7] == ' ')) { _callbacks->deleteScheduledRadioParams(false, skipSpacesConst(&config[7]), reply); + } else if (memcmp(config, "flood.channel.block", 19) == 0 + && (config[19] == ' ' || config[19] == '.')) { + const char* cursor = &config[19]; + int index = 0; + if (!parseFloodChannelBlockDotIndex(cursor, index)) { + sprintf(reply, "Error, index 1-%d", FLOOD_CHANNEL_BLOCK_SLOTS); + return; + } + cursor = skipSpacesConst(cursor); + char selector[8]; + if (index > 0 && *cursor != 0) { + strcpy(reply, "Error, use index or selector"); + return; + } + if (index > 0) { + snprintf(selector, sizeof(selector), "%d", index); + _callbacks->deleteFloodChannelBlock(selector, reply); + } else if (*cursor != 0) { + _callbacks->deleteFloodChannelBlock(cursor, reply); + } else { + strcpy(reply, "Error, use: del flood.channel.block "); + } } else { strcpy(reply, "unknown del: "); StrHelper::strncpy(&reply[13], config, 160 - 14); diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 38019a8f..7cfef4d4 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -78,6 +78,16 @@ #define COMMON_CLI_TMP_LEN ((FLOOD_RETRY_LIST_TEXT_MAX > (PRV_KEY_SIZE * 2 + 4)) ? FLOOD_RETRY_LIST_TEXT_MAX : (PRV_KEY_SIZE * 2 + 4)) #endif +#ifndef FLOOD_CHANNEL_BLOCK_SLOTS + #define FLOOD_CHANNEL_BLOCK_SLOTS 15 +#endif +#ifndef FLOOD_CHANNEL_BLOCK_NAME_LEN + #define FLOOD_CHANNEL_BLOCK_NAME_LEN 32 +#endif +#ifndef FLOOD_CHANNEL_BLOCK_PREFIX_LEN + #define FLOOD_CHANNEL_BLOCK_PREFIX_LEN 4 +#endif + #define DIRECT_RETRY_CR4_MIN_SNR_X4_DEFAULT 40 #define DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT 30 #define DIRECT_RETRY_CR7_MIN_SNR_X4_DEFAULT 10 @@ -156,6 +166,7 @@ struct NodePrefs { // persisted to file uint8_t battery_alert_low_percent; uint8_t battery_alert_critical_percent; uint8_t direct_retry_recent_enabled; + uint8_t flood_channel_data_enabled; }; class CommonCLICallbacks { @@ -216,6 +227,22 @@ public: (void)selector; strcpy(reply, "Error: unsupported"); } + virtual void setFloodChannelBlock(int index, const uint8_t* secret, uint8_t key_len, + const char* name, char* reply) { + (void)index; + (void)secret; + (void)key_len; + (void)name; + strcpy(reply, "Error: unsupported"); + } + virtual void formatFloodChannelBlocks(const char* selector, char* reply) { + (void)selector; + strcpy(reply, "Error: unsupported"); + } + virtual void deleteFloodChannelBlock(const char* selector, char* reply) { + (void)selector; + strcpy(reply, "Error: unsupported"); + } virtual void startRegionsLoad() { // no op by default From 9908d2b73ed4f696cd8c19f23d46481f5cde87c6 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Mon, 6 Jul 2026 17:10:53 -0700 Subject: [PATCH 211/214] Add altpath replies and flood channel hop gates --- docs/cli_commands.md | 48 +++++-- docs/halo_keymind_settings.md | 19 ++- examples/simple_repeater/MyMesh.cpp | 201 +++++++++++++++++++++------- examples/simple_repeater/MyMesh.h | 6 +- src/helpers/CommonCLI.cpp | 135 ++++++++++++++++++- src/helpers/CommonCLI.h | 6 +- 6 files changed, 349 insertions(+), 66 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 1971709a..da39fca2 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -781,9 +781,11 @@ send text.flood checking ridge link **Forwarding behavior:** Repeater firmware only. The repeater still receives and logs the packet when logging is enabled; this only blocks retransmission. -This is checked before `flood.channel.block` and applies to every flood -`GRP_DATA` packet regardless of channel key. Flood group text (`GRP_TXT`) is -unaffected. +This is checked before `flood.channel.block` and applies to flood `GRP_DATA` +packets regardless of channel key, subject to `flood.channel.block.hops`. +Flood group text (`GRP_TXT`) is unaffected by this setting. + +`get flood.channel.data` includes the active hop gate as `h=all` or `h>N`. --- @@ -792,10 +794,12 @@ unaffected. - `get flood.channel.block` - `get flood.channel.block.` - `get flood.channel.block ` -- `set flood.channel.block ` -- `set flood.channel.block. ` -- `set flood.channel.block #channel` -- `set flood.channel.block. #channel` +- `get flood.channel.block.hops` +- `set flood.channel.block [h=]` +- `set flood.channel.block. [h=]` +- `set flood.channel.block #channel [h=]` +- `set flood.channel.block. #channel [h=]` +- `set flood.channel.block.hops ` - `del flood.channel.block.` - `del flood.channel.block ` @@ -803,8 +807,11 @@ unaffected. - `n`: Slot number from `1` to `15`. - `key`: 128-bit or 256-bit channel key as hex. - `#channel`: Public hashtag channel name; derives the 128-bit channel key from the hashtag and is stored as the row name. -- `name`: Local label for hex-key rows. Not needed for `#channel`; any extra text after `#channel` is ignored. +- `name`: Local label for hex-key rows. Not needed for `#channel`; extra text after `#channel` is ignored unless it is a hop setting. - `8_hex_prefix`: First 4 bytes of the derived channel hash, shown by single-entry `get`. +- `all`: Block matching flood channel packets at any received flood hop count. +- `1-7`: Maximum received flood path hash count to repeat. Matching packets over this hop count are blocked. +- `default`: Row inherits the global `flood.channel.block.hops` setting. **Slot behavior:** Without `.n`, `set flood.channel.block` updates an existing row with the same derived channel prefix or name, otherwise it uses the next @@ -815,7 +822,26 @@ writes that slot. flood `GRP_TXT` and `GRP_DATA` channel packets. The repeater still receives and logs the packet, but it does not retransmit it when a configured block entry can validate/decode it. If `flood.channel.data` is `off`, all flood `GRP_DATA` -packets are blocked before this per-channel check runs. +packets over the hop gate are blocked before this per-channel check runs. + +**Hop gate:** `flood.channel.block.hops` defaults to `all`, which preserves the +original behavior. When set to `N` from `1` to `7`, both `flood.channel.data off` +and block rows that inherit the global setting only block packets whose received +flood path hash count is greater than `N`; packets at `N` hops or lower can +still repeat. For example, `set flood.channel.block.hops 1` repeats zero-hop and +one-hop matches but blocks two-hop and longer matches. + +Each block row can override the global hop gate with `h=`. +For example, `set flood.channel.block #wardriving h=3` blocks `#wardriving` +matches above three hops, while `set flood.channel.block #bot h=7` blocks +`#bot` matches above seven hops. Use `h=default` to make the row inherit the +global setting again. + +`get flood.channel.block` includes the global default first, then adds per-row +overrides as `/h>N` or `/h=all`; inherited rows do not show a suffix. Single-row +`get` replies include that row's stored hop mode as `h=def`, `h=all`, or `h>N`. +List replies truncate displayed row names only when the full list would exceed +the remote-management response limit. **Matching behavior:** Each block entry stores the first 4 bytes of the derived channel hash for display and lookup. Current group packets carry only the first @@ -829,7 +855,11 @@ MAC/decrypt. ``` set flood.channel.block #test set flood.channel.block.2 9cd8fcf22a47333b591d96a2b848b73f #test +set flood.channel.block.hops 3 +set flood.channel.block #wardriving h=3 +set flood.channel.block #bot h=7 get flood.channel.block +get flood.channel.block.hops get flood.channel.block #test del flood.channel.block.2 ``` diff --git a/docs/halo_keymind_settings.md b/docs/halo_keymind_settings.md index 826753a2..1e1b0d04 100644 --- a/docs/halo_keymind_settings.md +++ b/docs/halo_keymind_settings.md @@ -77,7 +77,11 @@ set flood.retry.ignore none | `battery.alert.low` | Warning threshold percentage. Must be greater than `battery.alert.critical`. | `get battery.alert.low`, `set battery.alert.low <1-100>` | `set battery.alert.low 20` | | `battery.alert.critical` | Critical threshold percentage. Critical warnings repeat more often. | `get battery.alert.critical`, `set battery.alert.critical <0-99>` | `set battery.alert.critical 10` | | `recent.repeater` | Shows, seeds, or clears the recent repeater prefix/SNR table used by direct retry and bridge freshness checks. | `get recent.repeater`, `get recent.repeater `, `set recent.repeater `, `clear recent.repeater` | `set recent.repeater A1B2C3 -8.5` | +| `flood.channel.data` | Turns forwarding of flood `GRP_DATA` channel packets on or off, subject to the channel block hop gate. | `get flood.channel.data`, `set flood.channel.data on/off` | `set flood.channel.data off` | +| `flood.channel.block` | Blocks selected flood `GRP_TXT`/`GRP_DATA` channels when the key validates the packet. Add `h=` for a per-channel hop override. | `get flood.channel.block`, `set flood.channel.block[.n] [name] [h=...]`, `del flood.channel.block[.n]` | `set flood.channel.block #wardriving h=3` | +| `flood.channel.block.hops` | Limits channel/data forwarding to short flood paths. `all` blocks matching packets at any hop count; `1`-`7` repeats packets at that hop count or lower and blocks longer matches. | `get flood.channel.block.hops`, `set flood.channel.block.hops ` | `set flood.channel.block.hops 3` | | `outpath` | Overrides the primary direct route used for replies to the current remote client. | `get outpath`, `set outpath `, `set outpath direct`, `set outpath clear`, `set outpath flood` | `set outpath A1B2C3,D4E5F6` | +| `altpath` | Adds a secondary direct route for repeater replies to the current remote client. | `get altpath`, `set altpath `, `set altpath direct`, `set altpath clear`, `set altpath flood` | `set altpath 71CE82,BA09F0` | ## Other Keymind Commands @@ -146,8 +150,8 @@ Serial CLI pages contain up to `128` rows. Remote LoRa CLI pages contain up to ## Direct Path Overrides -`outpath` applies to the current remote client ACL entry. It needs remote -client context, so it is not useful from the local serial CLI. +`outpath` and `altpath` apply to the current remote client ACL entry. They need +remote client context, so they are not useful from the local serial CLI. Set paths with comma-separated hop hashes. Each hop must be `2`, `4`, or `6` hex characters, and all hops in one path must use the same width. @@ -158,6 +162,9 @@ set outpath A1B2C3,D4E5F6 set outpath direct set outpath clear set outpath flood +get altpath +set altpath 71CE82,BA09F0 +set altpath clear ``` `set outpath direct` sets a zero-hop direct route for a client reachable without @@ -165,6 +172,14 @@ repeaters. `set outpath clear` forgets the override and lets normal path discovery fill it again. `set outpath flood` forces replies to use flood packets until the client logs in again. +When `outpath` is a valid direct path and `altpath` is also a valid, different +direct path, repeater DM replies send two packets: one on `outpath` and one on +`altpath`. The secondary `altpath` copy does not create its own direct-retry +state, so retry tracking stays attached to the primary `outpath` packet. +`altpath clear` disables the secondary direct reply. `altpath flood` is accepted +for command symmetry, but it does not create a second flood reply; only a valid +direct `altpath` sends the second packet. + ## Direct Retry Settings Direct retry applies to direct-routed packets. A queued resend is canceled when the next-hop echo is heard. diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index e3054035..165aee10 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -624,10 +624,64 @@ void MyMesh::sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, ui } } +static bool directPathsEqual(const uint8_t* a_path, uint8_t a_len, const uint8_t* b_path, uint8_t b_len) { + if (!mesh::Packet::isValidPathLen(a_len) || !mesh::Packet::isValidPathLen(b_len) || a_len != b_len) { + return false; + } + uint8_t hash_count = a_len & 63; + uint8_t hash_size = (a_len >> 6) + 1; + uint8_t byte_len = hash_count * hash_size; + return byte_len == 0 || memcmp(a_path, b_path, byte_len) == 0; +} + +void MyMesh::sendClientReply(ClientInfo* client, mesh::Packet* packet, unsigned long delay_millis, uint8_t path_hash_size) { + if (packet == NULL) { + return; + } + if (client == NULL || !mesh::Packet::isValidPathLen(client->out_path_len)) { + sendFloodReply(packet, delay_millis, path_hash_size); + return; + } + + mesh::Packet* alt = NULL; + if (mesh::Packet::isValidPathLen(client->alt_path_len) + && !directPathsEqual(client->out_path, client->out_path_len, client->alt_path, client->alt_path_len)) { + alt = obtainNewPacket(); + if (alt != NULL) { + *alt = *packet; + } else { + MESH_DEBUG_PRINTLN("sendClientReply: altpath packet pool empty"); + } + } + + sendDirect(packet, client->out_path, client->out_path_len, delay_millis); + if (alt != NULL) { + uint8_t direct_retry_enabled = _prefs.direct_retry_enabled; + _prefs.direct_retry_enabled = 0; + sendDirect(alt, client->alt_path, client->alt_path_len, delay_millis); + _prefs.direct_retry_enabled = direct_retry_enabled; + } +} + +uint8_t MyMesh::resolveFloodChannelBlockHops(uint8_t max_hops) const { + return max_hops == FLOOD_CHANNEL_BLOCK_HOPS_INHERIT ? _prefs.flood_channel_block_max_hops : max_hops; +} + +bool MyMesh::floodChannelBlockHopApplies(const mesh::Packet* packet, uint8_t max_hops) const { + if (packet == NULL) { + return false; + } + max_hops = resolveFloodChannelBlockHops(max_hops); + return max_hops == FLOOD_CHANNEL_BLOCK_HOPS_ALL || packet->getPathHashCount() > max_hops; +} + bool MyMesh::floodChannelBlockMatches(const FloodChannelBlockEntry& entry, const mesh::Packet* packet) const { if (!entry.active || packet == NULL || !packet->isRouteFlood()) { return false; } + if (!floodChannelBlockHopApplies(packet, entry.max_hops)) { + return false; + } uint8_t type = packet->getPayloadType(); if (type != PAYLOAD_TYPE_GRP_TXT && type != PAYLOAD_TYPE_GRP_DATA) { return false; @@ -645,8 +699,8 @@ bool MyMesh::floodChannelBlockMatches(const FloodChannelBlockEntry& entry, const bool MyMesh::shouldBlockFloodChannelForward(const mesh::Packet* packet) const { for (int i = 0; i < FLOOD_CHANNEL_BLOCK_SLOTS; i++) { if (floodChannelBlockMatches(flood_channel_blocks[i], packet)) { - MESH_DEBUG_PRINTLN("allowPacketForward: flood.channel.block matched slot=%d name=%s", - i + 1, flood_channel_blocks[i].name); + MESH_DEBUG_PRINTLN("allowPacketForward: flood.channel.block matched slot=%d name=%s hops=%d", + i + 1, flood_channel_blocks[i].name, packet->getPathHashCount()); return true; } } @@ -659,8 +713,11 @@ bool MyMesh::allowPacketForward(const mesh::Packet *packet) { if (packet->getPathHashCount() >= _prefs.flood_max) return false; if (packet->getRouteType() == ROUTE_TYPE_FLOOD && packet->getPathHashCount() >= _prefs.flood_max_unscoped) return false; if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->getPathHashCount() >= _prefs.flood_max_advert) return false; - if (!_prefs.flood_channel_data_enabled && packet->getPayloadType() == PAYLOAD_TYPE_GRP_DATA) { - MESH_DEBUG_PRINTLN("allowPacketForward: flood.channel.data off, blocking GRP_DATA"); + if (!_prefs.flood_channel_data_enabled + && packet->getPayloadType() == PAYLOAD_TYPE_GRP_DATA + && floodChannelBlockHopApplies(packet, _prefs.flood_channel_block_max_hops)) { + MESH_DEBUG_PRINTLN("allowPacketForward: flood.channel.data off, blocking GRP_DATA hops=%d", + packet->getPathHashCount()); return false; } if (shouldBlockFloodChannelForward(packet)) return false; @@ -1919,13 +1976,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, } else { mesh::Packet *reply = createDatagram(PAYLOAD_TYPE_RESPONSE, client->id, secret, reply_data, reply_len); - if (reply) { - if (mesh::Packet::isValidPathLen(client->out_path_len)) { // we have an out_path, so send DIRECT - sendDirect(reply, client->out_path, client->out_path_len, SERVER_RESPONSE_DELAY); - } else { - sendFloodReply(reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize()); - } - } + sendClientReply(client, reply, SERVER_RESPONSE_DELAY, packet->getPathHashSize()); } } else { MESH_DEBUG_PRINTLN("onPeerDataRecv: possible replay attack detected"); @@ -1952,13 +2003,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, PUB_KEY_SIZE); mesh::Packet *ack = createAck(ack_hash); - if (ack) { - if (mesh::Packet::isValidPathLen(client->out_path_len)) { - sendDirect(ack, client->out_path, client->out_path_len, TXT_ACK_DELAY); - } else { - sendFloodReply(ack, TXT_ACK_DELAY, packet->getPathHashSize()); - } - } + sendClientReply(client, ack, TXT_ACK_DELAY, packet->getPathHashSize()); } uint8_t temp[166]; @@ -1980,13 +2025,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx, temp[4] = (TXT_TYPE_CLI_DATA << 2); // NOTE: legacy was: TXT_TYPE_PLAIN auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len); - if (reply) { - if (mesh::Packet::isValidPathLen(client->out_path_len)) { - sendDirect(reply, client->out_path, client->out_path_len, CLI_REPLY_DELAY_MILLIS); - } else { - sendFloodReply(reply, CLI_REPLY_DELAY_MILLIS, packet->getPathHashSize()); - } - } + sendClientReply(client, reply, CLI_REPLY_DELAY_MILLIS, packet->getPathHashSize()); } } else { MESH_DEBUG_PRINTLN("onPeerDataRecv: possible replay attack detected"); @@ -2172,6 +2211,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_retry_bridge_enabled = 0; _prefs.flood_retry_advert_enabled = FLOOD_RETRY_ADVERT_DEFAULT; _prefs.flood_channel_data_enabled = 1; + _prefs.flood_channel_block_max_hops = FLOOD_CHANNEL_BLOCK_HOPS_ALL; _prefs.battery_alert_enabled = 0; _prefs.battery_alert_low_percent = BATTERY_ALERT_LOW_PERCENT_DEFAULT; _prefs.battery_alert_critical_percent = BATTERY_ALERT_CRITICAL_PERCENT_DEFAULT; @@ -3011,18 +3051,20 @@ void MyMesh::loadFloodChannelBlocks() { uint8_t magic[4]; uint8_t count = 0; bool success = file.read(magic, sizeof(magic)) == sizeof(magic) - && memcmp(magic, "FCB1", sizeof(magic)) == 0 + && memcmp(magic, "FCB2", sizeof(magic)) == 0 && file.read(&count, sizeof(count)) == sizeof(count); for (int i = 0; success && i < count && i < FLOOD_CHANNEL_BLOCK_SLOTS; i++) { uint8_t active = 0; uint8_t key_len = 0; + uint8_t max_hops = FLOOD_CHANNEL_BLOCK_HOPS_INHERIT; uint8_t hash_prefix[FLOOD_CHANNEL_BLOCK_PREFIX_LEN]; uint8_t secret[PUB_KEY_SIZE]; char name[FLOOD_CHANNEL_BLOCK_NAME_LEN]; success = file.read(&active, sizeof(active)) == sizeof(active); success = success && file.read(&key_len, sizeof(key_len)) == sizeof(key_len); + success = success && file.read(&max_hops, sizeof(max_hops)) == sizeof(max_hops); success = success && file.read(hash_prefix, sizeof(hash_prefix)) == sizeof(hash_prefix); success = success && file.read(secret, sizeof(secret)) == sizeof(secret); success = success && file.read((uint8_t*)name, sizeof(name)) == sizeof(name); @@ -3035,6 +3077,9 @@ void MyMesh::loadFloodChannelBlocks() { auto& entry = flood_channel_blocks[i]; entry.active = true; entry.key_len = key_len; + entry.max_hops = (max_hops == FLOOD_CHANNEL_BLOCK_HOPS_ALL + || max_hops == FLOOD_CHANNEL_BLOCK_HOPS_INHERIT + || (max_hops >= 1 && max_hops <= 7)) ? max_hops : FLOOD_CHANNEL_BLOCK_HOPS_INHERIT; memcpy(entry.secret, secret, sizeof(entry.secret)); if (entry.key_len == CIPHER_KEY_SIZE) { memset(&entry.secret[CIPHER_KEY_SIZE], 0, PUB_KEY_SIZE - CIPHER_KEY_SIZE); @@ -3057,7 +3102,7 @@ bool MyMesh::saveFloodChannelBlocks() { return false; } - const uint8_t magic[4] = {'F', 'C', 'B', '1'}; + const uint8_t magic[4] = {'F', 'C', 'B', '2'}; uint8_t count = FLOOD_CHANNEL_BLOCK_SLOTS; bool success = file.write(magic, sizeof(magic)) == sizeof(magic); success = success && file.write(&count, sizeof(count)) == sizeof(count); @@ -3065,8 +3110,10 @@ bool MyMesh::saveFloodChannelBlocks() { for (int i = 0; success && i < FLOOD_CHANNEL_BLOCK_SLOTS; i++) { const auto& entry = flood_channel_blocks[i]; uint8_t active = entry.active ? 1 : 0; + uint8_t max_hops = entry.active ? entry.max_hops : FLOOD_CHANNEL_BLOCK_HOPS_INHERIT; success = file.write(&active, sizeof(active)) == sizeof(active); success = success && file.write(&entry.key_len, sizeof(entry.key_len)) == sizeof(entry.key_len); + success = success && file.write(&max_hops, sizeof(max_hops)) == sizeof(max_hops); success = success && file.write(entry.hash_prefix, sizeof(entry.hash_prefix)) == sizeof(entry.hash_prefix); success = success && file.write(entry.secret, sizeof(entry.secret)) == sizeof(entry.secret); success = success && file.write((const uint8_t*)entry.name, sizeof(entry.name)) == sizeof(entry.name); @@ -3100,6 +3147,16 @@ static bool parseFloodChannelBlockPrefixSelector(const char* selector, return mesh::Utils::fromHex(prefix, FLOOD_CHANNEL_BLOCK_PREFIX_LEN, text); } +static void formatFloodChannelBlockHops(char* dest, uint8_t max_hops) { + if (max_hops == FLOOD_CHANNEL_BLOCK_HOPS_ALL) { + strcpy(dest, "h=all"); + } else if (max_hops == FLOOD_CHANNEL_BLOCK_HOPS_INHERIT) { + strcpy(dest, "h=def"); + } else { + sprintf(dest, "h>%u", (unsigned int)max_hops); + } +} + int MyMesh::findFloodChannelBlockBySelector(const char* selector) const { uint8_t prefix[FLOOD_CHANNEL_BLOCK_PREFIX_LEN]; if (parseFloodChannelBlockPrefixSelector(selector, prefix)) { @@ -3154,17 +3211,19 @@ void MyMesh::formatFloodChannelBlockDetail(char* reply, int idx) const { const auto& entry = flood_channel_blocks[idx]; if (!entry.active) { - snprintf(reply, 160, "> %d empty", idx + 1); + snprintf(reply, 150, "> %d empty", idx + 1); return; } char prefix[FLOOD_CHANNEL_BLOCK_PREFIX_LEN * 2 + 1]; + char hops[8]; mesh::Utils::toHex(prefix, entry.hash_prefix, FLOOD_CHANNEL_BLOCK_PREFIX_LEN); - snprintf(reply, 160, "> %d %s %u %s", idx + 1, prefix, (unsigned int)entry.key_len * 8, entry.name); + formatFloodChannelBlockHops(hops, entry.max_hops); + snprintf(reply, 150, "> %d %s %u %s %s", idx + 1, prefix, (unsigned int)entry.key_len * 8, hops, entry.name); } void MyMesh::setFloodChannelBlock(int index, const uint8_t* secret, uint8_t key_len, - const char* name, char* reply) { + const char* name, uint8_t max_hops, char* reply) { if ((key_len != CIPHER_KEY_SIZE && key_len != PUB_KEY_SIZE) || secret == NULL || name == NULL || name[0] == 0) { strcpy(reply, "Err - bad params"); return; @@ -3173,6 +3232,12 @@ void MyMesh::setFloodChannelBlock(int index, const uint8_t* secret, uint8_t key_ snprintf(reply, 160, "Err - index 1-%d", FLOOD_CHANNEL_BLOCK_SLOTS); return; } + if (max_hops != FLOOD_CHANNEL_BLOCK_HOPS_ALL + && max_hops != FLOOD_CHANNEL_BLOCK_HOPS_INHERIT + && (max_hops < 1 || max_hops > 7)) { + strcpy(reply, "Err - bad hops"); + return; + } uint8_t prefix[FLOOD_CHANNEL_BLOCK_PREFIX_LEN]; deriveFloodChannelBlockPrefix(secret, key_len, prefix); @@ -3186,6 +3251,7 @@ void MyMesh::setFloodChannelBlock(int index, const uint8_t* secret, uint8_t key_ clearFloodChannelBlockEntry(entry); entry.active = true; entry.key_len = key_len; + entry.max_hops = max_hops; memcpy(entry.secret, secret, PUB_KEY_SIZE); if (entry.key_len == CIPHER_KEY_SIZE) { memset(&entry.secret[CIPHER_KEY_SIZE], 0, PUB_KEY_SIZE - CIPHER_KEY_SIZE); @@ -3212,8 +3278,24 @@ void MyMesh::formatFloodChannelBlocks(const char* selector, char* reply) { } char* out = reply; - size_t remaining = 160; - int written = snprintf(out, remaining, ">"); + const size_t reply_limit = 150; + size_t remaining = reply_limit; + char hops[8]; + formatFloodChannelBlockHops(hops, _prefs.flood_channel_block_max_hops); + size_t full_len = 2 + strlen(hops); + for (int i = 0; i < FLOOD_CHANNEL_BLOCK_SLOTS; i++) { + const auto& entry = flood_channel_blocks[i]; + size_t display_len = entry.active ? strlen(entry.name) : 1; + full_len += 1 + (i + 1 >= 10 ? 2 : 1) + 1 + display_len; + if (entry.active && entry.max_hops != FLOOD_CHANNEL_BLOCK_HOPS_INHERIT) { + char row_hops[8]; + formatFloodChannelBlockHops(row_hops, entry.max_hops); + full_len += 1 + strlen(row_hops); + } + } + bool trim_names = full_len >= reply_limit; + + int written = snprintf(out, remaining, "> %s", hops); if (written < 0 || (size_t)written >= remaining) { reply[0] = 0; return; @@ -3222,18 +3304,30 @@ void MyMesh::formatFloodChannelBlocks(const char* selector, char* reply) { remaining -= written; for (int i = 0; i < FLOOD_CHANNEL_BLOCK_SLOTS && remaining > 1; i++) { - char display[8]; + const char* display = "-"; + char short_display[6]; const auto& entry = flood_channel_blocks[i]; - if (!entry.active) { - strcpy(display, "-"); - } else { - StrHelper::strncpy(display, entry.name, sizeof(display)); - if (strlen(entry.name) >= sizeof(display)) { - display[sizeof(display) - 2] = '~'; - display[sizeof(display) - 1] = 0; + if (entry.active) { + char row_hops[8]; + row_hops[0] = 0; + if (entry.max_hops != FLOOD_CHANNEL_BLOCK_HOPS_INHERIT) { + formatFloodChannelBlockHops(row_hops, entry.max_hops); } + if (trim_names) { + StrHelper::strncpy(short_display, entry.name, sizeof(short_display)); + if (strlen(entry.name) >= sizeof(short_display)) { + short_display[sizeof(short_display) - 2] = '~'; + short_display[sizeof(short_display) - 1] = 0; + } + display = short_display; + } else { + display = entry.name; + } + written = snprintf(out, remaining, " %d:%s%s%s", i + 1, display, + row_hops[0] ? "/" : "", row_hops); + } else { + written = snprintf(out, remaining, " %d:%s", i + 1, display); } - written = snprintf(out, remaining, " %d:%s", i + 1, display); if (written < 0 || (size_t)written >= remaining) { out[remaining - 1] = 0; break; @@ -3478,14 +3572,23 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char * reply[0] = 0; } else if (strcmp(command, "get outpath") == 0 || strcmp(command, "set outpath") == 0 - || strncmp(command, "set outpath ", 12) == 0) { + || strncmp(command, "set outpath ", 12) == 0 + || strcmp(command, "get altpath") == 0 + || strcmp(command, "set altpath") == 0 + || strncmp(command, "set altpath ", 12) == 0) { bool is_get = strncmp(command, "get ", 4) == 0; + bool is_alt = strncmp(command + 4, "altpath", 7) == 0; if (sender == NULL) { strcpy(reply, "Err - command needs remote client context"); - } else if (is_get) { - formatPathReply(sender->out_path, sender->out_path_len, reply, 160); } else { - char* spec = command + 11; // length of "set outpath" + uint8_t* stored_path = is_alt ? sender->alt_path : sender->out_path; + uint8_t* stored_path_len = is_alt ? &sender->alt_path_len : &sender->out_path_len; + if (is_get) { + formatPathReply(stored_path, *stored_path_len, reply, 160); + return; + } + + char* spec = command + 11; // length of "set outpath" or "set altpath" if (*spec == ' ') spec++; uint8_t path[MAX_PATH_SIZE]; @@ -3495,13 +3598,13 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char * strcpy(reply, err ? err : "Err - invalid path"); } else { if (path_len == OUT_PATH_UNKNOWN || path_len == OUT_PATH_FORCE_FLOOD) { - memset(sender->out_path, 0, sizeof(sender->out_path)); - sender->out_path_len = path_len; + memset(stored_path, 0, MAX_PATH_SIZE); + *stored_path_len = path_len; } else { - sender->out_path_len = mesh::Packet::copyPath(sender->out_path, path, path_len); + *stored_path_len = mesh::Packet::copyPath(stored_path, path, path_len); } dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); - formatPathReply(sender->out_path, sender->out_path_len, reply, 160); + formatPathReply(stored_path, *stored_path_len, reply, 160); } } } else if (strncmp(command, "send text.flood ", 16) == 0) { diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 83485b25..536fc951 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -135,6 +135,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { struct FloodChannelBlockEntry { bool active; uint8_t key_len; + uint8_t max_hops; uint8_t hash_prefix[FLOOD_CHANNEL_BLOCK_PREFIX_LEN]; uint8_t secret[PUB_KEY_SIZE]; char name[FLOOD_CHANNEL_BLOCK_NAME_LEN]; @@ -206,6 +207,8 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { void clearFloodChannelBlockEntry(FloodChannelBlockEntry& entry); void deriveFloodChannelBlockPrefix(const uint8_t* secret, uint8_t key_len, uint8_t prefix[FLOOD_CHANNEL_BLOCK_PREFIX_LEN]) const; + uint8_t resolveFloodChannelBlockHops(uint8_t max_hops) const; + bool floodChannelBlockHopApplies(const mesh::Packet* packet, uint8_t max_hops) const; bool floodChannelBlockMatches(const FloodChannelBlockEntry& entry, const mesh::Packet* packet) const; bool shouldBlockFloodChannelForward(const mesh::Packet* packet) const; int findFloodChannelBlockBySelector(const char* selector) const; @@ -291,6 +294,7 @@ protected: void onControlDataRecv(mesh::Packet* packet) override; void sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, uint8_t path_hash_size); + void sendClientReply(ClientInfo* client, mesh::Packet* packet, unsigned long delay_millis, uint8_t path_hash_size); public: MyMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables); @@ -342,7 +346,7 @@ public: bool saveRegions() override; void onDefaultRegionChanged(const RegionEntry* r) override; void setFloodChannelBlock(int index, const uint8_t* secret, uint8_t key_len, - const char* name, char* reply) override; + const char* name, uint8_t max_hops, char* reply) override; void formatFloodChannelBlocks(const char* selector, char* reply) override; void deleteFloodChannelBlock(const char* selector, char* reply) override; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index c43b2948..3127a0ba 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -325,6 +325,98 @@ static void formatFloodRetryPathGate(char* dest, uint8_t path_gate) { } } +static bool parseFloodChannelBlockHops(const char* value, uint8_t& max_hops) { + if (value == NULL) { + return false; + } + value = skipSpacesConst(value); + if (strcmp(value, "all") == 0) { + max_hops = FLOOD_CHANNEL_BLOCK_HOPS_ALL; + return true; + } + return parseUint8Strict(value, 1, 7, max_hops); +} + +static bool parseFloodChannelBlockRowHops(const char* value, uint8_t& max_hops) { + if (value == NULL) { + return false; + } + value = skipSpacesConst(value); + if (strcmp(value, "default") == 0 || strcmp(value, "def") == 0 || strcmp(value, "inherit") == 0) { + max_hops = FLOOD_CHANNEL_BLOCK_HOPS_INHERIT; + return true; + } + return parseFloodChannelBlockHops(value, max_hops); +} + +static bool parseFloodChannelBlockHopAssignment(const char* text, bool allow_bare, uint8_t& max_hops) { + char token[16]; + text = skipSpacesConst(text); + if (text == NULL || *text == 0) { + return false; + } + + size_t len = 0; + while (text[len] && text[len] != ' ' && len + 1 < sizeof(token)) { + token[len] = text[len]; + len++; + } + token[len] = 0; + + const char* value = NULL; + if (strncmp(token, "h=", 2) == 0) { + value = token + 2; + } else if (strncmp(token, "hops=", 5) == 0) { + value = token + 5; + } else if (allow_bare) { + value = token; + } else { + return false; + } + return parseFloodChannelBlockRowHops(value, max_hops); +} + +static bool looksFloodChannelBlockHopAssignment(const char* text) { + text = skipSpacesConst(text); + if (text == NULL || *text == 0) { + return false; + } + return (*text >= '0' && *text <= '9') + || strncmp(text, "h=", 2) == 0 + || strncmp(text, "hops=", 5) == 0 + || strncmp(text, "all", 3) == 0 + || strncmp(text, "def", 3) == 0 + || strncmp(text, "default", 7) == 0 + || strncmp(text, "inherit", 7) == 0; +} + +static bool trimFloodChannelBlockHopSuffix(char* name, uint8_t& max_hops) { + size_t len = strlen(name); + while (len > 0 && name[len - 1] == ' ') { + name[--len] = 0; + } + char* token = strrchr(name, ' '); + if (token == NULL) { + return true; + } + if (strncmp(token + 1, "h=", 2) != 0 && strncmp(token + 1, "hops=", 5) != 0) { + return true; + } + if (!parseFloodChannelBlockHopAssignment(token + 1, false, max_hops)) { + return false; + } + *token = 0; + return strlen(name) > 0; +} + +static void formatFloodChannelBlockHops(char* dest, uint8_t max_hops) { + if (max_hops == FLOOD_CHANNEL_BLOCK_HOPS_ALL) { + strcpy(dest, "h=all"); + } else { + sprintf(dest, "h>%u", (unsigned int)max_hops); + } +} + static void formatFloodRetryPrefixList(char* dest, const uint8_t prefixes[][FLOOD_RETRY_PREFIX_LEN], uint8_t max_prefixes) { char* out = dest; @@ -611,6 +703,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->battery_alert_critical_percent = BATTERY_ALERT_CRITICAL_PERCENT_DEFAULT; _prefs->direct_retry_recent_enabled = DIRECT_RETRY_RECENT_DEFAULT; _prefs->flood_channel_data_enabled = 1; + _prefs->flood_channel_block_max_hops = FLOOD_CHANNEL_BLOCK_HOPS_ALL; bool has_flood_retry_prefs = file.available() >= 2; if (has_flood_retry_prefs) { file.read((uint8_t *)&_prefs->flood_retry_attempts, sizeof(_prefs->flood_retry_attempts)); // 311 @@ -645,8 +738,11 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { if (file.available() >= (int)sizeof(_prefs->flood_channel_data_enabled)) { file.read((uint8_t *)&_prefs->flood_channel_data_enabled, sizeof(_prefs->flood_channel_data_enabled)); } + if (file.available() >= (int)sizeof(_prefs->flood_channel_block_max_hops)) { + file.read((uint8_t *)&_prefs->flood_channel_block_max_hops, sizeof(_prefs->flood_channel_block_max_hops)); + } } - // next: 673 + // next: 674 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -707,6 +803,10 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->battery_alert_enabled = constrain(_prefs->battery_alert_enabled, 0, 1); _prefs->direct_retry_recent_enabled = constrain(_prefs->direct_retry_recent_enabled, 0, 1); _prefs->flood_channel_data_enabled = constrain(_prefs->flood_channel_data_enabled, 0, 1); + if (_prefs->flood_channel_block_max_hops != FLOOD_CHANNEL_BLOCK_HOPS_ALL + && (_prefs->flood_channel_block_max_hops < 1 || _prefs->flood_channel_block_max_hops > 7)) { + _prefs->flood_channel_block_max_hops = FLOOD_CHANNEL_BLOCK_HOPS_ALL; + } if (_prefs->battery_alert_low_percent < 1 || _prefs->battery_alert_low_percent > 100 || _prefs->battery_alert_critical_percent >= _prefs->battery_alert_low_percent) { @@ -805,7 +905,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->battery_alert_critical_percent, sizeof(_prefs->battery_alert_critical_percent)); file.write((uint8_t *)&_prefs->direct_retry_recent_enabled, sizeof(_prefs->direct_retry_recent_enabled)); file.write((uint8_t *)&_prefs->flood_channel_data_enabled, sizeof(_prefs->flood_channel_data_enabled)); - // next: 673 + file.write((uint8_t *)&_prefs->flood_channel_block_max_hops, sizeof(_prefs->flood_channel_block_max_hops)); + // next: 674 file.close(); } @@ -1461,6 +1562,15 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { strcpy(reply, "Error, must be on or off"); } + } else if (memcmp(config, "flood.channel.block.hops ", 25) == 0) { + uint8_t max_hops; + if (parseFloodChannelBlockHops(&config[25], max_hops)) { + _prefs->flood_channel_block_max_hops = max_hops; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be all or 1-7"); + } } else if (memcmp(config, "flood.channel.block", 19) == 0 && (config[19] == ' ' || config[19] == '.')) { const char* cursor = &config[19]; @@ -1483,10 +1593,21 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep key_text[key_len_text] = 0; char name[FLOOD_CHANNEL_BLOCK_NAME_LEN]; + uint8_t block_hops = FLOOD_CHANNEL_BLOCK_HOPS_INHERIT; if (key_text[0] == '#') { StrHelper::strncpy(name, key_text, sizeof(name)); + const char* extra = skipSpacesConst(cursor); + if (*extra && looksFloodChannelBlockHopAssignment(extra) + && !parseFloodChannelBlockHopAssignment(extra, true, block_hops)) { + strcpy(reply, "Error, hops must be all, default, or 1-7"); + return; + } } else { copyTrimmedFloodChannelBlockName(name, sizeof(name), cursor); + if (!trimFloodChannelBlockHopSuffix(name, block_hops)) { + strcpy(reply, "Error, bad name or hops"); + return; + } } uint8_t secret[PUB_KEY_SIZE]; uint8_t decoded_key_len = 0; @@ -1495,7 +1616,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else if (name[0] == 0 || !isValidName(name)) { strcpy(reply, "Error, bad name"); } else { - _callbacks->setFloodChannelBlock(index, secret, decoded_key_len, name, reply); + _callbacks->setFloodChannelBlock(index, secret, decoded_key_len, name, block_hops, reply); } } else if (memcmp(config, "flood.retry.count ", 18) == 0) { int attempts = looksUnsignedInteger(&config[18]) ? _atoi(&config[18]) : -1; @@ -1820,7 +1941,13 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep } else if (memcmp(config, "txdelay", 7) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor)); } else if (memcmp(config, "flood.channel.data", 18) == 0) { - sprintf(reply, "> %s", _prefs->flood_channel_data_enabled ? "on" : "off"); + char hops[8]; + formatFloodChannelBlockHops(hops, _prefs->flood_channel_block_max_hops); + sprintf(reply, "> %s %s", _prefs->flood_channel_data_enabled ? "on" : "off", hops); + } else if (memcmp(config, "flood.channel.block.hops", 24) == 0) { + char hops[8]; + formatFloodChannelBlockHops(hops, _prefs->flood_channel_block_max_hops); + sprintf(reply, "> %s", hops); } else if (memcmp(config, "flood.channel.block", 19) == 0 && (config[19] == 0 || config[19] == ' ' || config[19] == '.')) { const char* cursor = &config[19]; diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 7cfef4d4..b9943481 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -87,6 +87,8 @@ #ifndef FLOOD_CHANNEL_BLOCK_PREFIX_LEN #define FLOOD_CHANNEL_BLOCK_PREFIX_LEN 4 #endif +#define FLOOD_CHANNEL_BLOCK_HOPS_ALL 0xFF +#define FLOOD_CHANNEL_BLOCK_HOPS_INHERIT 0xFE #define DIRECT_RETRY_CR4_MIN_SNR_X4_DEFAULT 40 #define DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT 30 @@ -167,6 +169,7 @@ struct NodePrefs { // persisted to file uint8_t battery_alert_critical_percent; uint8_t direct_retry_recent_enabled; uint8_t flood_channel_data_enabled; + uint8_t flood_channel_block_max_hops; }; class CommonCLICallbacks { @@ -228,11 +231,12 @@ public: strcpy(reply, "Error: unsupported"); } virtual void setFloodChannelBlock(int index, const uint8_t* secret, uint8_t key_len, - const char* name, char* reply) { + const char* name, uint8_t max_hops, char* reply) { (void)index; (void)secret; (void)key_len; (void)name; + (void)max_hops; strcpy(reply, "Error: unsupported"); } virtual void formatFloodChannelBlocks(const char* selector, char* reply) { From 0613b2dcc29918a192480bccc0022b3ae64014b6 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 7 Jul 2026 00:47:19 -0700 Subject: [PATCH 212/214] Tune repeater flood controls and retries --- docs/cli_commands.md | 53 +++++++++++++++++++++-------- docs/halo_keymind_settings.md | 11 +++--- examples/simple_repeater/MyMesh.cpp | 49 ++++++++++++++++++++++++-- examples/simple_repeater/MyMesh.h | 2 ++ src/Mesh.cpp | 2 +- src/helpers/CommonCLI.cpp | 26 ++++++++++++-- src/helpers/CommonCLI.h | 1 + variants/wio-e5-dev/platformio.ini | 2 ++ 8 files changed, 121 insertions(+), 25 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index da39fca2..78c73f8d 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -531,7 +531,7 @@ send text.flood checking ridge link **Parameters:** - `state`: `on`|`off` -**Default:** `on` +**Default:** `flood.channel.data on`; `flood.channel.data.hops h=all` --- @@ -771,19 +771,28 @@ send text.flood checking ridge link #### Forward flood group data packets on repeaters **Usage:** - `get flood.channel.data` +- `get flood.channel.data.hops` - `set flood.channel.data ` +- `set flood.channel.data.hops ` **Parameters:** - `on`: Retransmit received flood `GRP_DATA` channel packets. - `off`: Do not retransmit received flood `GRP_DATA` channel packets. +- `all`: When `flood.channel.data` is `off`, block `GRP_DATA` at any received flood hop count. +- `1-7`: When `flood.channel.data` is `off`, repeat `GRP_DATA` at this hop count or lower and block longer paths. -**Default:** `on` +**Default:** `flood.channel.data on`; `flood.channel.data.hops h=all` **Forwarding behavior:** Repeater firmware only. The repeater still receives and logs the packet when logging is enabled; this only blocks retransmission. This is checked before `flood.channel.block` and applies to flood `GRP_DATA` -packets regardless of channel key, subject to `flood.channel.block.hops`. -Flood group text (`GRP_TXT`) is unaffected by this setting. +packets regardless of channel key. Flood group text (`GRP_TXT`) is unaffected by +this setting. + +`flood.channel.data.hops` is separate from `flood.channel.block.hops`. +`flood.channel.block.hops` does not restrict unkeyed `GRP_DATA` packets. With +the default `flood.channel.data on`, `GRP_DATA` repeats normally even when +`flood.channel.block.hops` is set for keyed channel blocks. `get flood.channel.data` includes the active hop gate as `h=all` or `h>N`. @@ -818,22 +827,29 @@ row with the same derived channel prefix or name, otherwise it uses the next empty slot. If all 15 slots are full, the command fails. With `.n`, the command writes that slot. +**Default row:** Repeater firmware seeds a new block list with +`#wardriving h=4` in slot 1. This is a normal row, so it can be changed with +`set flood.channel.block #wardriving h=` or removed with +`del flood.channel.block #wardriving`. Once the block list has been saved, the +firmware uses the saved list and does not recreate the default after deletion. + **Forwarding behavior:** Repeater firmware only. This only affects received flood `GRP_TXT` and `GRP_DATA` channel packets. The repeater still receives and logs the packet, but it does not retransmit it when a configured block entry can -validate/decode it. If `flood.channel.data` is `off`, all flood `GRP_DATA` -packets over the hop gate are blocked before this per-channel check runs. +validate/decode it. If `flood.channel.data` is `off`, `GRP_DATA` packets are +checked against the separate `flood.channel.data.hops` gate before this +per-channel check runs. **Hop gate:** `flood.channel.block.hops` defaults to `all`, which preserves the -original behavior. When set to `N` from `1` to `7`, both `flood.channel.data off` -and block rows that inherit the global setting only block packets whose received -flood path hash count is greater than `N`; packets at `N` hops or lower can -still repeat. For example, `set flood.channel.block.hops 1` repeats zero-hop and -one-hop matches but blocks two-hop and longer matches. +original behavior. When set to `N` from `1` to `7`, block rows that inherit the +global setting only block packets whose received flood path hash count is +greater than `N`; packets at `N` hops or lower can still repeat. For example, +`set flood.channel.block.hops 1` repeats zero-hop and one-hop matches but blocks +two-hop and longer matches. Each block row can override the global hop gate with `h=`. -For example, `set flood.channel.block #wardriving h=3` blocks `#wardriving` -matches above three hops, while `set flood.channel.block #bot h=7` blocks +For example, the seeded `#wardriving h=4` row blocks `#wardriving` matches +above four hops, while `set flood.channel.block #bot h=7` blocks `#bot` matches above seven hops. Use `h=default` to make the row inherit the global setting again. @@ -856,7 +872,7 @@ MAC/decrypt. set flood.channel.block #test set flood.channel.block.2 9cd8fcf22a47333b591d96a2b848b73f #test set flood.channel.block.hops 3 -set flood.channel.block #wardriving h=3 +set flood.channel.block #wardriving h=4 set flood.channel.block #bot h=7 get flood.channel.block get flood.channel.block.hops @@ -1383,6 +1399,9 @@ set flood.retry.bucket 2 none **Default:** `15` with the `rooftop` preset +**Note:** Direct-routed type 2 text packets always use 21 retry attempts, +regardless of this setting or the short-path cap. + **Examples:** ``` get direct.retry.count @@ -1405,6 +1424,9 @@ set direct.retry.count 15 **Explanation:** - The first retry waits `base` milliseconds after the failed echo window. +- The failed echo window includes a packet-length add-on. TRACE and + ANON_REQ/type 7 packets keep the existing 4x line-time add-on. TXT_MSG/type 2 + packets use 7x. Other direct retry packets use 6x. - For non-TRACE direct paths shorter than 6 remaining hops, the effective wait is scaled by `hops / 6`. - Non-TRACE direct paths with 6 or more remaining hops use the configured value unchanged. - TRACE retries shorter than 16 remaining hops use `hops / 16`; 16 or more remaining hops use the configured value unchanged. @@ -1433,6 +1455,9 @@ set direct.retry.base 500 **Explanation:** - Retry delay is `base + attempt_index * step`. +- This is added after the failed echo window. TRACE and ANON_REQ/type 7 packets + keep the existing 4x packet-length add-on. TXT_MSG/type 2 packets use 7x. + Other direct retry packets use 6x. - For non-TRACE direct paths shorter than 6 remaining hops, that computed delay is scaled by `hops / 6`. - Non-TRACE direct paths with 6 or more remaining hops use the computed delay unchanged. - TRACE retries shorter than 16 remaining hops use `hops / 16`; 16 or more remaining hops use the computed delay unchanged. diff --git a/docs/halo_keymind_settings.md b/docs/halo_keymind_settings.md index 1e1b0d04..2ee56723 100644 --- a/docs/halo_keymind_settings.md +++ b/docs/halo_keymind_settings.md @@ -77,9 +77,10 @@ set flood.retry.ignore none | `battery.alert.low` | Warning threshold percentage. Must be greater than `battery.alert.critical`. | `get battery.alert.low`, `set battery.alert.low <1-100>` | `set battery.alert.low 20` | | `battery.alert.critical` | Critical threshold percentage. Critical warnings repeat more often. | `get battery.alert.critical`, `set battery.alert.critical <0-99>` | `set battery.alert.critical 10` | | `recent.repeater` | Shows, seeds, or clears the recent repeater prefix/SNR table used by direct retry and bridge freshness checks. | `get recent.repeater`, `get recent.repeater `, `set recent.repeater `, `clear recent.repeater` | `set recent.repeater A1B2C3 -8.5` | -| `flood.channel.data` | Turns forwarding of flood `GRP_DATA` channel packets on or off, subject to the channel block hop gate. | `get flood.channel.data`, `set flood.channel.data on/off` | `set flood.channel.data off` | -| `flood.channel.block` | Blocks selected flood `GRP_TXT`/`GRP_DATA` channels when the key validates the packet. Add `h=` for a per-channel hop override. | `get flood.channel.block`, `set flood.channel.block[.n] [name] [h=...]`, `del flood.channel.block[.n]` | `set flood.channel.block #wardriving h=3` | -| `flood.channel.block.hops` | Limits channel/data forwarding to short flood paths. `all` blocks matching packets at any hop count; `1`-`7` repeats packets at that hop count or lower and blocks longer matches. | `get flood.channel.block.hops`, `set flood.channel.block.hops ` | `set flood.channel.block.hops 3` | +| `flood.channel.data` | Turns forwarding of flood `GRP_DATA` channel packets on or off. With the default `on`, `GRP_DATA` repeats normally even when `flood.channel.block.hops` is set. | `get flood.channel.data`, `set flood.channel.data on/off` | `set flood.channel.data off` | +| `flood.channel.data.hops` | Separate hop gate used only when `flood.channel.data` is `off`; `all` blocks `GRP_DATA` at any hop count, `1`-`7` repeats at that hop count or lower and blocks longer paths. | `get flood.channel.data.hops`, `set flood.channel.data.hops ` | `set flood.channel.data.hops 7` | +| `flood.channel.block` | Blocks selected flood `GRP_TXT`/`GRP_DATA` channels when the key validates the packet. New repeater block lists start with editable/deletable `#wardriving h=4`. Add `h=` for a per-channel hop override. | `get flood.channel.block`, `set flood.channel.block[.n] [name] [h=...]`, `del flood.channel.block[.n]` | `set flood.channel.block #wardriving h=4` | +| `flood.channel.block.hops` | Limits keyed channel-block matches to short flood paths. `all` blocks matching packets at any hop count; `1`-`7` repeats packets at that hop count or lower and blocks longer matches. This does not restrict unkeyed `GRP_DATA`; use `flood.channel.data.hops` for that. | `get flood.channel.block.hops`, `set flood.channel.block.hops ` | `set flood.channel.block.hops 3` | | `outpath` | Overrides the primary direct route used for replies to the current remote client. | `get outpath`, `set outpath `, `set outpath direct`, `set outpath clear`, `set outpath flood` | `set outpath A1B2C3,D4E5F6` | | `altpath` | Adds a secondary direct route for repeater replies to the current remote client. | `get altpath`, `set altpath `, `set altpath direct`, `set altpath clear`, `set altpath flood` | `set altpath 71CE82,BA09F0` | @@ -189,8 +190,8 @@ Direct retry applies to direct-routed packets. A queued resend is canceled when | `retry.preset` | Applies shared direct and flood retry defaults. Values: `infra`, `rooftop`, `mobile` or `0`, `1`, `2`. | `get retry.preset`, `set retry.preset ` | `set retry.preset rooftop` | | `direct.retry.heard` | Uses the recent repeater table as the direct retry eligibility gate. | `get direct.retry.heard`, `set direct.retry.heard on/off` | `set direct.retry.heard on` | | `direct.retry.margin` | SNR margin in dB above the SF-specific receive floor. | `get direct.retry.margin`, `set direct.retry.margin <0-40>` | `set direct.retry.margin 5` | -| `direct.retry.count` | Maximum direct retry attempts after initial TX. | `get direct.retry.count`, `set direct.retry.count <1-15>` | `set direct.retry.count 15` | -| `direct.retry.base` | Base wait in milliseconds before retry; non-TRACE paths under 6 remaining hops scale by `hops / 6`, TRACE paths under 16 by `hops / 16`. | `get direct.retry.base`, `set direct.retry.base <10-5000>` | `set direct.retry.base 175` | +| `direct.retry.count` | Maximum direct retry attempts after initial TX. Direct-routed type 2 text packets always use 21 attempts regardless of this setting or the short-path cap. | `get direct.retry.count`, `set direct.retry.count <1-15>` | `set direct.retry.count 15` | +| `direct.retry.base` | Base wait in milliseconds before retry; packet-length add-on is 4x for TRACE and ANON_REQ/type 7, 7x for TXT_MSG/type 2, and 6x for other direct retry packets. Non-TRACE paths under 6 remaining hops scale by `hops / 6`, TRACE paths under 16 by `hops / 16`. | `get direct.retry.base`, `set direct.retry.base <10-5000>` | `set direct.retry.base 175` | | `direct.retry.step` | Milliseconds added per retry attempt before the same short-path scaling. | `get direct.retry.step`, `set direct.retry.step <0-5000>` | `set direct.retry.step 100` | | `direct.retry.cr` | Adaptive coding-rate thresholds for repeater direct retry packets. Repeaters use `CR4`, `CR5`, `CR7`, or `CR8`, then escalate by attempt: CR4, CR5, CR7, CR7, then CR8 from a CR4 start; CR5, CR7, CR7, then CR8 from a CR5 start. Non-repeaters start at the current radio CR and follow the same escalation pattern, clamped at CR8. | `get direct.retry.cr`, `set direct.retry.cr ,,,`, `set direct.retry.cr off` | `set direct.retry.cr 10.0,7.5,2.5,0` | diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 03e3f355..6809014d 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -83,6 +83,10 @@ #define LAZY_CONTACTS_WRITE_DELAY 5000 #define FLOOD_CHANNEL_BLOCK_FILE "/flood_ch_block" +#define DEFAULT_FLOOD_CHANNEL_BLOCK_NAME "#wardriving" +#ifndef DEFAULT_FLOOD_CHANNEL_BLOCK_HOPS + #define DEFAULT_FLOOD_CHANNEL_BLOCK_HOPS 4 +#endif #ifndef REPEATERS_CHANNEL_KEY_HEX #define REPEATERS_CHANNEL_KEY_HEX "89db441e2814dccf0dbd2e8cc5f501a3" @@ -675,6 +679,14 @@ bool MyMesh::floodChannelBlockHopApplies(const mesh::Packet* packet, uint8_t max return max_hops == FLOOD_CHANNEL_BLOCK_HOPS_ALL || packet->getPathHashCount() > max_hops; } +bool MyMesh::floodChannelDataHopApplies(const mesh::Packet* packet) const { + if (packet == NULL) { + return false; + } + uint8_t max_hops = _prefs.flood_channel_data_max_hops; + return max_hops == FLOOD_CHANNEL_BLOCK_HOPS_ALL || packet->getPathHashCount() > max_hops; +} + bool MyMesh::floodChannelBlockMatches(const FloodChannelBlockEntry& entry, const mesh::Packet* packet) const { if (!entry.active || packet == NULL || !packet->isRouteFlood()) { return false; @@ -715,7 +727,7 @@ bool MyMesh::allowPacketForward(const mesh::Packet *packet) { if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->getPathHashCount() >= _prefs.flood_max_advert) return false; if (!_prefs.flood_channel_data_enabled && packet->getPayloadType() == PAYLOAD_TYPE_GRP_DATA - && floodChannelBlockHopApplies(packet, _prefs.flood_channel_block_max_hops)) { + && floodChannelDataHopApplies(packet)) { MESH_DEBUG_PRINTLN("allowPacketForward: flood.channel.data off, blocking GRP_DATA hops=%d", packet->getPathHashCount()); return false; @@ -992,7 +1004,14 @@ uint32_t MyMesh::getDirectRetryEchoDelay(const mesh::Packet* packet) const { // Wait roughly long enough for our TX, the next hop's receive/forward window, and its echo back. uint32_t bits = ((uint32_t)packet->getRawLength()) * 8; - uint32_t scaled_wait_millis = (uint32_t)((((float)bits) * 4.0f) / kbps); + uint8_t payload_type = packet->getPayloadType(); + float length_factor = 6.0f; + if (payload_type == PAYLOAD_TYPE_TRACE || payload_type == PAYLOAD_TYPE_ANON_REQ) { + length_factor = 4.0f; + } else if (payload_type == PAYLOAD_TYPE_TXT_MSG) { + length_factor = 7.0f; + } + uint32_t scaled_wait_millis = (uint32_t)((((float)bits) * length_factor) / kbps); return base_wait_millis + scaled_wait_millis; } @@ -1011,6 +1030,10 @@ static uint8_t decodeDirectRetryTraceHashSize(uint8_t flags, uint8_t route_bytes } uint8_t MyMesh::getDirectRetryMaxAttempts(const mesh::Packet* packet) const { + if (packet != NULL && packet->getPayloadType() == PAYLOAD_TYPE_TXT_MSG) { + return 21; + } + uint8_t configured_attempts = getDirectRetryConfiguredMaxAttempts(); uint8_t total_hops = 0; @@ -2212,6 +2235,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_retry_advert_enabled = FLOOD_RETRY_ADVERT_DEFAULT; _prefs.flood_channel_data_enabled = 1; _prefs.flood_channel_block_max_hops = FLOOD_CHANNEL_BLOCK_HOPS_ALL; + _prefs.flood_channel_data_max_hops = FLOOD_CHANNEL_BLOCK_HOPS_ALL; _prefs.battery_alert_enabled = 0; _prefs.battery_alert_low_percent = BATTERY_ALERT_LOW_PERCENT_DEFAULT; _prefs.battery_alert_critical_percent = BATTERY_ALERT_CRITICAL_PERCENT_DEFAULT; @@ -3037,9 +3061,28 @@ void MyMesh::deriveFloodChannelBlockPrefix(const uint8_t* secret, uint8_t key_le mesh::Utils::sha256(prefix, FLOOD_CHANNEL_BLOCK_PREFIX_LEN, secret, key_len); } +void MyMesh::seedDefaultFloodChannelBlocks() { + auto& entry = flood_channel_blocks[0]; + clearFloodChannelBlockEntry(entry); + entry.active = true; + entry.key_len = CIPHER_KEY_SIZE; + entry.max_hops = DEFAULT_FLOOD_CHANNEL_BLOCK_HOPS; + mesh::Utils::sha256(entry.secret, CIPHER_KEY_SIZE, + (const uint8_t*)DEFAULT_FLOOD_CHANNEL_BLOCK_NAME, + strlen(DEFAULT_FLOOD_CHANNEL_BLOCK_NAME)); + memset(&entry.secret[CIPHER_KEY_SIZE], 0, PUB_KEY_SIZE - CIPHER_KEY_SIZE); + deriveFloodChannelBlockPrefix(entry.secret, entry.key_len, entry.hash_prefix); + StrHelper::strncpy(entry.name, DEFAULT_FLOOD_CHANNEL_BLOCK_NAME, sizeof(entry.name)); +} + void MyMesh::loadFloodChannelBlocks() { memset(flood_channel_blocks, 0, sizeof(flood_channel_blocks)); - if (_fs == NULL || !_fs->exists(FLOOD_CHANNEL_BLOCK_FILE)) { + if (_fs == NULL) { + return; + } + if (!_fs->exists(FLOOD_CHANNEL_BLOCK_FILE)) { + seedDefaultFloodChannelBlocks(); + saveFloodChannelBlocks(); return; } diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 536fc951..f52afa69 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -204,11 +204,13 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { bool isMillisTimerDue(unsigned long timestamp) const; void loadFloodChannelBlocks(); bool saveFloodChannelBlocks(); + void seedDefaultFloodChannelBlocks(); void clearFloodChannelBlockEntry(FloodChannelBlockEntry& entry); void deriveFloodChannelBlockPrefix(const uint8_t* secret, uint8_t key_len, uint8_t prefix[FLOOD_CHANNEL_BLOCK_PREFIX_LEN]) const; uint8_t resolveFloodChannelBlockHops(uint8_t max_hops) const; bool floodChannelBlockHopApplies(const mesh::Packet* packet, uint8_t max_hops) const; + bool floodChannelDataHopApplies(const mesh::Packet* packet) const; bool floodChannelBlockMatches(const FloodChannelBlockEntry& entry, const mesh::Packet* packet) const; bool shouldBlockFloodChannelForward(const mesh::Packet* packet) const; int findFloodChannelBlockBySelector(const char* selector) const; diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 9af12b53..7f9cba6d 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -4,7 +4,7 @@ namespace mesh { static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT = 15; -static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX = 15; +static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX = 21; static const uint8_t FLOOD_RETRY_MAX_ATTEMPTS_DEFAULT = 15; static const uint8_t FLOOD_RETRY_MAX_ATTEMPTS_HARD_MAX = 15; static const uint8_t FLOOD_RETRY_MAX_PATH_DEFAULT = 1; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 3127a0ba..20ff7ed7 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -704,6 +704,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->direct_retry_recent_enabled = DIRECT_RETRY_RECENT_DEFAULT; _prefs->flood_channel_data_enabled = 1; _prefs->flood_channel_block_max_hops = FLOOD_CHANNEL_BLOCK_HOPS_ALL; + _prefs->flood_channel_data_max_hops = FLOOD_CHANNEL_BLOCK_HOPS_ALL; bool has_flood_retry_prefs = file.available() >= 2; if (has_flood_retry_prefs) { file.read((uint8_t *)&_prefs->flood_retry_attempts, sizeof(_prefs->flood_retry_attempts)); // 311 @@ -741,6 +742,9 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { if (file.available() >= (int)sizeof(_prefs->flood_channel_block_max_hops)) { file.read((uint8_t *)&_prefs->flood_channel_block_max_hops, sizeof(_prefs->flood_channel_block_max_hops)); } + if (file.available() >= (int)sizeof(_prefs->flood_channel_data_max_hops)) { + file.read((uint8_t *)&_prefs->flood_channel_data_max_hops, sizeof(_prefs->flood_channel_data_max_hops)); + } } // next: 674 @@ -807,6 +811,10 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { && (_prefs->flood_channel_block_max_hops < 1 || _prefs->flood_channel_block_max_hops > 7)) { _prefs->flood_channel_block_max_hops = FLOOD_CHANNEL_BLOCK_HOPS_ALL; } + if (_prefs->flood_channel_data_max_hops != FLOOD_CHANNEL_BLOCK_HOPS_ALL + && (_prefs->flood_channel_data_max_hops < 1 || _prefs->flood_channel_data_max_hops > 7)) { + _prefs->flood_channel_data_max_hops = FLOOD_CHANNEL_BLOCK_HOPS_ALL; + } if (_prefs->battery_alert_low_percent < 1 || _prefs->battery_alert_low_percent > 100 || _prefs->battery_alert_critical_percent >= _prefs->battery_alert_low_percent) { @@ -906,6 +914,7 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->direct_retry_recent_enabled, sizeof(_prefs->direct_retry_recent_enabled)); file.write((uint8_t *)&_prefs->flood_channel_data_enabled, sizeof(_prefs->flood_channel_data_enabled)); file.write((uint8_t *)&_prefs->flood_channel_block_max_hops, sizeof(_prefs->flood_channel_block_max_hops)); + file.write((uint8_t *)&_prefs->flood_channel_data_max_hops, sizeof(_prefs->flood_channel_data_max_hops)); // next: 674 file.close(); @@ -1550,6 +1559,15 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { strcpy(reply, "Error, must be 0-5000 ms"); } + } else if (memcmp(config, "flood.channel.data.hops ", 24) == 0) { + uint8_t max_hops; + if (parseFloodChannelBlockHops(&config[24], max_hops)) { + _prefs->flood_channel_data_max_hops = max_hops; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be all or 1-7"); + } } else if (memcmp(config, "flood.channel.data ", 19) == 0) { if (strcmp(&config[19], "on") == 0) { _prefs->flood_channel_data_enabled = 1; @@ -1940,9 +1958,13 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "> %s", StrHelper::ftoa(_prefs->rx_delay_base)); } else if (memcmp(config, "txdelay", 7) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor)); - } else if (memcmp(config, "flood.channel.data", 18) == 0) { + } else if (strcmp(config, "flood.channel.data.hops") == 0) { char hops[8]; - formatFloodChannelBlockHops(hops, _prefs->flood_channel_block_max_hops); + formatFloodChannelBlockHops(hops, _prefs->flood_channel_data_max_hops); + sprintf(reply, "> %s", hops); + } else if (strcmp(config, "flood.channel.data") == 0) { + char hops[8]; + formatFloodChannelBlockHops(hops, _prefs->flood_channel_data_max_hops); sprintf(reply, "> %s %s", _prefs->flood_channel_data_enabled ? "on" : "off", hops); } else if (memcmp(config, "flood.channel.block.hops", 24) == 0) { char hops[8]; diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index b9943481..9179e811 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -170,6 +170,7 @@ struct NodePrefs { // persisted to file uint8_t direct_retry_recent_enabled; uint8_t flood_channel_data_enabled; uint8_t flood_channel_block_max_hops; + uint8_t flood_channel_data_max_hops; }; class CommonCLICallbacks { diff --git a/variants/wio-e5-dev/platformio.ini b/variants/wio-e5-dev/platformio.ini index 22bdc3c8..393df69a 100644 --- a/variants/wio-e5-dev/platformio.ini +++ b/variants/wio-e5-dev/platformio.ini @@ -28,6 +28,8 @@ build_src_filter = ${lora_e5.build_src_filter} [env:wio-e5-repeater_bridge_rs232] extends = lora_e5 build_flags = ${lora_e5.build_flags} + -flto + -D MESH_ENABLE_RECENT_REPEATERS=1 -D LORA_TX_POWER=22 -D ADVERT_NAME='"WIO-E5 Repeater"' -D ADMIN_PASSWORD='"password"' From ebd7da795c78748fb125784731818aee14471da6 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 7 Jul 2026 00:56:44 -0700 Subject: [PATCH 213/214] Share direct retry timing defaults --- docs/cli_commands.md | 8 ++++++-- docs/halo_keymind_settings.md | 2 +- examples/simple_repeater/MyMesh.cpp | 8 +------- src/Mesh.cpp | 30 ++++++++++++++++++++++++----- src/Mesh.h | 10 ++++++++++ 5 files changed, 43 insertions(+), 15 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 78c73f8d..8363f39e 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -1399,8 +1399,8 @@ set flood.retry.bucket 2 none **Default:** `15` with the `rooftop` preset -**Note:** Direct-routed type 2 text packets always use 21 retry attempts, -regardless of this setting or the short-path cap. +**Note:** Direct-routed type 2 text packets always use 21 retry attempts in the +shared retry logic, regardless of this setting or the repeater short-path cap. **Examples:** ``` @@ -1427,6 +1427,8 @@ set direct.retry.count 15 - The failed echo window includes a packet-length add-on. TRACE and ANON_REQ/type 7 packets keep the existing 4x line-time add-on. TXT_MSG/type 2 packets use 7x. Other direct retry packets use 6x. +- Non-repeater firmware uses the same packet-type add-ons with the shared + fixed base retry timing. - For non-TRACE direct paths shorter than 6 remaining hops, the effective wait is scaled by `hops / 6`. - Non-TRACE direct paths with 6 or more remaining hops use the configured value unchanged. - TRACE retries shorter than 16 remaining hops use `hops / 16`; 16 or more remaining hops use the configured value unchanged. @@ -1458,6 +1460,8 @@ set direct.retry.base 500 - This is added after the failed echo window. TRACE and ANON_REQ/type 7 packets keep the existing 4x packet-length add-on. TXT_MSG/type 2 packets use 7x. Other direct retry packets use 6x. +- Non-repeater firmware uses the same packet-type add-ons with the shared + fixed retry step. - For non-TRACE direct paths shorter than 6 remaining hops, that computed delay is scaled by `hops / 6`. - Non-TRACE direct paths with 6 or more remaining hops use the computed delay unchanged. - TRACE retries shorter than 16 remaining hops use `hops / 16`; 16 or more remaining hops use the computed delay unchanged. diff --git a/docs/halo_keymind_settings.md b/docs/halo_keymind_settings.md index 2ee56723..9ef40190 100644 --- a/docs/halo_keymind_settings.md +++ b/docs/halo_keymind_settings.md @@ -183,7 +183,7 @@ direct `altpath` sends the second packet. ## Direct Retry Settings -Direct retry applies to direct-routed packets. A queued resend is canceled when the next-hop echo is heard. +Direct retry applies to direct-routed packets. A queued resend is canceled when the next-hop echo is heard. Repeaters expose the settings below; non-repeater firmware uses the same packet-type timing rules with fixed shared base/step timing. | Setting | What it does | How to use | Example | | --- | --- | --- | --- | diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 6809014d..18a28301 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1004,13 +1004,7 @@ uint32_t MyMesh::getDirectRetryEchoDelay(const mesh::Packet* packet) const { // Wait roughly long enough for our TX, the next hop's receive/forward window, and its echo back. uint32_t bits = ((uint32_t)packet->getRawLength()) * 8; - uint8_t payload_type = packet->getPayloadType(); - float length_factor = 6.0f; - if (payload_type == PAYLOAD_TYPE_TRACE || payload_type == PAYLOAD_TYPE_ANON_REQ) { - length_factor = 4.0f; - } else if (payload_type == PAYLOAD_TYPE_TXT_MSG) { - length_factor = 7.0f; - } + float length_factor = (float)getDirectRetryPacketAirtimeFactor(packet); uint32_t scaled_wait_millis = (uint32_t)((((float)bits) * length_factor) / kbps); return base_wait_millis + scaled_wait_millis; } diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 7f9cba6d..0ecb2412 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -228,17 +228,37 @@ bool Mesh::allowDirectRetry(const Packet* packet, const uint8_t* next_hop_hash, (void)next_hop_hash_len; return true; } +uint8_t Mesh::getDirectRetryPacketAirtimeFactor(const Packet* packet) const { + if (packet == NULL) { + return 6; + } + + uint8_t payload_type = packet->getPayloadType(); + if (payload_type == PAYLOAD_TYPE_TRACE || payload_type == PAYLOAD_TYPE_ANON_REQ) { + return 4; + } + if (payload_type == PAYLOAD_TYPE_TXT_MSG) { + return 7; + } + return 6; +} +uint32_t Mesh::getDirectRetryPacketAirtimeDelay(const Packet* packet) const { + if (packet == NULL || _radio == NULL) { + return 0; + } + + return _radio->getEstAirtimeFor(packet->getRawLength()) * (uint32_t)getDirectRetryPacketAirtimeFactor(packet); +} uint32_t Mesh::getDirectRetryEchoDelay(const Packet* packet) const { - (void)packet; - // Keep the base fallback aligned with the repeater's minimum retry wait. - return 200; + return 200 + getDirectRetryPacketAirtimeDelay(packet); } uint8_t Mesh::getDirectRetryMaxAttempts(const Packet* packet) const { - (void)packet; + if (packet != NULL && packet->getPayloadType() == PAYLOAD_TYPE_TXT_MSG) { + return 21; + } return DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT; } uint32_t Mesh::getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx) { - (void)packet; uint32_t base = getDirectRetryEchoDelay(packet); // Keep the historical linear spacing while allowing the base wait to vary by platform/profile. return base + ((uint32_t)attempt_idx * 100UL); diff --git a/src/Mesh.h b/src/Mesh.h index 72fcfac1..8a4bdb6c 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -150,6 +150,16 @@ protected: */ virtual uint32_t getDirectRetryEchoDelay(const Packet* packet) const; + /** + * \returns packet-airtime multiplier used by the direct-retry echo window. + */ + uint8_t getDirectRetryPacketAirtimeFactor(const Packet* packet) const; + + /** + * \returns packet-airtime add-on used by the direct-retry echo window. + */ + uint32_t getDirectRetryPacketAirtimeDelay(const Packet* packet) const; + /** * \returns maximum number of retry transmissions after the initial direct TX. */ From 6681ef7915b32569dc1c8db94b49ce419dd556ca Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 7 Jul 2026 08:50:19 -0700 Subject: [PATCH 214/214] Fix Wio-E5 mini repeater size --- variants/wio-e5-mini/platformio.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/variants/wio-e5-mini/platformio.ini b/variants/wio-e5-mini/platformio.ini index 82f01331..bbd69a55 100644 --- a/variants/wio-e5-mini/platformio.ini +++ b/variants/wio-e5-mini/platformio.ini @@ -20,6 +20,8 @@ lib_deps = ${stm32_base.lib_deps} [env:wio-e5-mini_repeater] extends = lora_e5_mini build_flags = ${lora_e5_mini.build_flags} + -flto + -D MESH_ENABLE_RECENT_REPEATERS=1 -D LORA_TX_POWER=22 -D ADVERT_NAME='"wio-e5-mini Repeater"' -D ADMIN_PASSWORD='"password"'