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 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..b01ddc93 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.1.0 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 67468b56..84fbc195 100644 --- a/.github/workflows/pr-build-check.yml +++ b/.github/workflows/pr-build-check.yml @@ -46,7 +46,7 @@ jobs: steps: - name: Clone Repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Build Environment uses: ./.github/actions/setup-build-environment diff --git a/.github/workflows/stale-bot.yml b/.github/workflows/stale-bot.yml new file mode 100644 index 00000000..ec166587 --- /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 diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 080e70d0..00000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": [ - "platformio.platformio-ide" - ], - "unwantedRecommendations": [ - "ms-vscode.cpptools-extension-pack" - ] -} diff --git a/README.md b/README.md index 8b9abcb5..31693884 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,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. 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 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/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..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 @@ -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/build-iotthinks.sh b/build-iotthinks.sh new file mode 100644 index 00000000..a5ace33d --- /dev/null +++ b/build-iotthinks.sh @@ -0,0 +1,136 @@ +#!/usr/bin/env bash +# ./build-iotthinks.sh +export FIRMWARE_VERSION="PowerSaving16" + +############# Repeaters ############# +# Commonly-used boards +## ESP32 - 17 boards +./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 +./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 +./build.sh build-firmware \ +Heltec_v2_repeater \ +LilyGo_TLora_V2_1_1_6_repeater \ +Tbeam_SX1276_repeater + +############# Room Server ############# +# ESP32 - 7 boards +./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 +./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 +./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 +./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 ############# +./build.sh build-firmware \ +Heltec_t096_companion_radio_usb + +############# Sample builds ############# +# 14 boards +./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 8ea83b40..1697e0c4 100755 --- a/build.sh +++ b/build.sh @@ -1,18 +1,60 @@ -# cache project config json for use in get_platform_for_env() -PIO_CONFIG_JSON=$(pio project config --json-output) - #!/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="" +FIRMWARE_FILENAME_INFIX="" +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="${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" +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. 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 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. @@ -20,279 +62,1671 @@ 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, firmware profile, 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 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 -$ 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. + 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: $ 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 +} -# $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 +canonicalize_variant_suffix() { + local variant_suffix=$1 + + 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 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" + ) + + 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-firmwares-logging-matrix) + return 0 + ;; + 4) + 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 + ;; + 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}" +} + +is_logging_matrix_command() { + [ "$1" == "build-firmwares-logging-matrix" ] +} + +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 +} + +set_firmware_profile_override() { + FIRMWARE_PROFILE_OVERRIDE=$1 +} + +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.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: + 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.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.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" + 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 fetched_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 fetched_preset_rows <<< "$preset_output" + fi + for row in "${fetched_preset_rows[@]}"; do + if [ -z "$row" ]; then + continue + fi + preset_rows+=("$row") + done + else + echo "Could not fetch radio presets from ${RADIO_SETTINGS_API_URL}." + 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[@]} + + 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 +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 / companion.manual.add=1 / companion.autoadd=0" + ) + + 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 + 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 +} + +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 +} + +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 - printf '%s' "$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 -raw = sys.stdin.read() -data = json.loads(raw, strict=False) +data = json.load(sys.stdin) for section, options in data: if section == 'env:$env_name': 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)', str(flag)) + match = re.search(r'($SUPPORTED_PLATFORM_PATTERN)', flag) if match: print(match.group(1)) sys.exit(0) -" 2>/dev/null || true +" <<<"$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" + 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 } -# build firmware for the provided pio env in $1 -build_firmware() { - # get env platform for post build actions - ENV_PLATFORM=($(get_platform_for_env $1)) - - # get git commit sha - COMMIT_HASH=$(git rev-parse --short HEAD) - - # set firmware build date (e.g. "6 Jun 2026"; %-d drops the leading zero on GNU date / the Linux CI runner) - 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 - fi - - # set firmware version string (used for the output filename) - # e.g: v1.0.0-abcdef - FIRMWARE_VERSION_STRING="${FIRMWARE_VERSION}-${COMMIT_HASH}" - - # craft filename - # e.g: RAK_4631_Repeater-v1.0.0-SHA - FIRMWARE_FILENAME="$1-${FIRMWARE_VERSION_STRING}" - - # Tag the *embedded* version for observer builds, e.g. v1.0.0-observer-abcdef, - # so `ver`, the MQTT firmware_version/client_version, and SNMP all identify the - # fork. The filename above is intentionally left untagged: the env name already - # contains "observer", and the web flasher keys off that existing pattern. - VARIANT_TAG="" - case "$1" in - *observer*) VARIANT_TAG="-observer" ;; +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 - # Observer build number: when CI provides FIRMWARE_BUILD_NUMBER (the per-base - # published-build counter), append it as a 4th version component so the node - # reports e.g. v1.16.0.5-observer-abcdef and `ota check` can show how many - # builds behind it is. Local dev builds leave it unset → no 4th component. - # The *filename* (FIRMWARE_VERSION_STRING above) is deliberately left without - # the build number so assets stay -v-.bin. - BUILD_NUMBER_SUFFIX="" - if [ -n "$FIRMWARE_BUILD_NUMBER" ]; then - BUILD_NUMBER_SUFFIX=".${FIRMWARE_BUILD_NUMBER}" + 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 - EMBEDDED_VERSION_STRING="${FIRMWARE_VERSION}${BUILD_NUMBER_SUFFIX}${VARIANT_TAG}-${COMMIT_HASH}" +} - # add firmware version info to end of existing platformio build flags in environment vars. - # OTA_VARIANT is the env name ($1) — it selects this build's slim per-variant manifest - # (/.json) that the observer pull-OTA fetches. - export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DFIRMWARE_BUILD_DATE='\"${FIRMWARE_BUILD_DATE}\"' -DFIRMWARE_VERSION='\"${EMBEDDED_VERSION_STRING}\"' -DOTA_VARIANT='\"$1\"'" +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 -DDEFAULT_MANUAL_ADD_CONTACTS=1 -DDEFAULT_AUTOADD_CONFIG=0" + ;; + esac +} - # disable debug flags if requested +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 + + 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" "${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() { + 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" "${OUTPUT_DIR}/${firmware_filename}.uf2" || return $? + if [ -f ".pio/build/${env_name}/firmware.zip" ]; then + copy_build_output ".pio/build/${env_name}/firmware.zip" "${OUTPUT_DIR}/${firmware_filename}.zip" || return $? + fi +} + +collect_stm32_artifacts() { + local env_name=$1 + local firmware_filename=$2 + + 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" "${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() { + 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 +} + +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:-} + + if [ "$had_platformio_build_flags" -eq 1 ]; then + export PLATFORMIO_BUILD_FLAGS="$original_platformio_build_flags" + else + unset PLATFORMIO_BUILD_FLAGS + fi +} + +build_firmware() { + 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 + + 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 + + commit_hash=$(git rev-parse --short HEAD) + firmware_build_date=$(date '+%d-%b-%Y') + firmware_version=${FIRMWARE_VERSION:-} + + 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 + + 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 + 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 + apply_firmware_profile_overrides - # build firmware target - pio run -e $1 - - # Build merged binaries where supported (ESP32 targets). - pio run -t mergebin -e $1 >/dev/null 2>&1 || true - - # Generate UF2 from HEX when useful and UF2 is not already present. - if [ -f ".pio/build/$1/firmware.hex" ] && [ ! -f ".pio/build/$1/firmware.uf2" ]; then - python3 bin/uf2conv/uf2conv.py .pio/build/$1/firmware.hex -c -o .pio/build/$1/firmware.uf2 -f 0xADA52840 >/dev/null 2>&1 || true + print_build_flags "$env_name" + 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 - # Copy any produced artifacts to out folder. - 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.hex out/${FIRMWARE_FILENAME}.hex 2>/dev/null || true - 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" +} - # Emit the partition-table signature (ESP32) for OTA partition-compatibility - # checks. Keyed by env name so the slim-manifest generator can find it; the - # firmware computes the same signature at runtime from its flashed table. - if [ -f ".pio/build/$1/partitions.bin" ]; then - python3 scripts/partition_signature.py ".pio/build/$1/partitions.bin" > "out/$1.partsig" 2>/dev/null || true +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-firmwares-logging-matrix) + 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 + env_platform=$(get_platform_for_env "$env_name") + if ! is_supported_platform "$env_platform"; then + echo "Unsupported build target: $env_name" + 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 +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 + + 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 } -# 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 -} +prepare_output_dir() { + local output_dir="$OUTPUT_DIR" -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 " + + 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" +} + +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" +} + +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 + 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_FILENAME_INFIX="" + run_resolved_build_targets "${targets[@]}" + build_status=$? + + if [ "$build_status" -eq 0 ]; then + 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 + PACKET_LOGGING_OVERRIDE=$original_packet_logging_override + FIRMWARE_FILENAME_INFIX=$original_firmware_filename_infix + + 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_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 $? + 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 + 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[@]}" + 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 + if is_logging_matrix_command "$1"; then + prompt_for_logging_matrix_output_policy + else + RESUME_BUILD_OUTPUT=0 + fi + prepare_output_dir + run_command "$@" +} + +main "$@" 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/docs/cli_commands.md b/docs/cli_commands.md index b0badbcd..6f2b66a1 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 @@ -69,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` --- @@ -189,7 +213,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) @@ -222,8 +246,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) @@ -232,6 +256,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` @@ -261,6 +311,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 @@ -384,11 +448,56 @@ 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. Any `:` in the node name is sent as `;` so the prefix delimiter stays unambiguous. + +**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` --- +#### View this node's firmware version +**Usage:** `ver` + +--- + #### View this node's configured role **Usage:** `get role` @@ -406,7 +515,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. --- @@ -420,7 +529,7 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Parameters:** - `state`: `on`|`off` -**Default:** `on` +**Default:** `flood.channel.data on`; `flood.channel.data.hops h=all` --- @@ -557,6 +666,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` @@ -643,6 +766,131 @@ This document provides an overview of CLI commands that can be sent to MeshCore **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` + +--- + +#### 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:** `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. 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`. + +--- + +#### Block selected flood channel packets on repeaters +**Usage:** +- `get flood.channel.block` +- `get flood.channel.block.` +- `get flood.channel.block ` +- `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 ` + +**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`; 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 +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`, `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`, 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, 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. + +`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 +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 +set flood.channel.block.hops 3 +set flood.channel.block #wardriving h=4 +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 +``` --- @@ -931,6 +1179,442 @@ 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, ACK packets, multipart packets carrying ACK payloads, and TRACE packets. + +#### View or change direct retry state +**Usage:** +- `get direct.retry` +- `set direct.retry ` + +**Parameters:** +- `state`: `on`|`off` + +**Default:** `on` + +**Notes:** +- New installs and older preference files without direct retry settings default to `on` with the `rooftop` preset. + +**Examples:** +``` +get direct.retry +set direct.retry on +set direct.retry off +``` + +--- + +#### 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 ` + +**Parameters:** +- `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; 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:** +``` +get retry.preset +set retry.preset infra +set retry.preset rooftop +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` +- `set direct.retry.count ` + +**Parameters:** +- `count`: Maximum retry attempts after the original send, from `1` to `15`. + +**Default:** `15` with the `rooftop` preset + +**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:** +``` +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. +- 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. +- 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`. +- 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. +- 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. + +**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. +- 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. +- A failed unknown repeater is seeded at `+2.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.repeaters ` +- `set recent.repeater [snr_db]` +- `clear recent.repeater` + +**Parameters:** +- `prefix`: Repeater path-hash prefix as hex. +- `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. +- 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 [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:** +``` +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 +``` + --- ### GPS (When GPS support is compiled in) diff --git a/docs/faq.md b/docs/faq.md index f82d3c9f..5ebdb9b6 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). @@ -392,10 +391,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: - + ### 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. @@ -553,10 +549,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. diff --git a/docs/halo_keymind_settings.md b/docs/halo_keymind_settings.md new file mode 100644 index 00000000..9ef40190 --- /dev/null +++ b/docs/halo_keymind_settings.md @@ -0,0 +1,376 @@ +# 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` | +| `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` | + +## Other Keymind Commands + +| Command | What it does | How to use | Example | +| --- | --- | --- | --- | +| `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 + +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.repeaters 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 71CE82,BA09F0 +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. + +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. 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 | +| --- | --- | --- | --- | +| `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. 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` | + +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. 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: + +| 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 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` | 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 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` | +| `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` | `15` | `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/number_allocations.md b/docs/number_allocations.md index 857525d4..2ccf950f 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 | 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) diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index bf2f36c3..c8be97a6 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(); } @@ -545,7 +547,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/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 65edc93e..664dda9a 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 @@ -106,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 @@ -265,6 +280,9 @@ bool MyMesh::getCADEnabled() 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; @@ -858,7 +876,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; @@ -879,9 +897,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 @@ -890,6 +912,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) { @@ -937,8 +960,13 @@ 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 if (_prefs.ble_pin == 0) { @@ -968,6 +996,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"); // NOTE: no FEM LNA wiring here — companion has its own NodePrefs without @@ -1548,6 +1577,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; } @@ -1827,6 +1857,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]; @@ -1987,6 +2041,7 @@ void MyMesh::handleCmdFrame(size_t len) { sendPacket(pkt, priority, 0); writeOKFrame(); } else { + releasePacket(pkt); writeErrFrame(ERR_CODE_ILLEGAL_ARG); } } else { @@ -2192,15 +2247,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/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index f4190f30..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; @@ -166,7 +167,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 48c381ce..ecb117bd 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -29,9 +29,10 @@ 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 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/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 7c842019..28591cc1 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -697,8 +697,10 @@ 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 55290467..36d8ec94 100644 --- a/examples/companion_radio/ui-orig/UITask.cpp +++ b/examples/companion_radio/ui-orig/UITask.cpp @@ -307,7 +307,9 @@ 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(); } } @@ -453,4 +455,4 @@ void UITask::handleButtonLongPress() { } else { shutdown(); } -} \ No newline at end of file +} diff --git a/examples/companion_radio/ui-tiny/UITask.cpp b/examples/companion_radio/ui-tiny/UITask.cpp index 0119475e..a6cbe9de 100644 --- a/examples/companion_radio/ui-tiny/UITask.cpp +++ b/examples/companion_radio/ui-tiny/UITask.cpp @@ -568,6 +568,7 @@ void UITask::shutdown(bool restart){ } else { _display->turnOff(); radio_driver.powerOff(); + // Power off board including radio, display, GPS and components _board->powerOff(); } } diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 88a44fb5..35266576 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -21,6 +21,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 @@ -62,6 +84,186 @@ #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" +#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) + +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 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; + 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 @@ -91,7 +293,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 @@ -121,7 +323,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); @@ -138,7 +340,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 @@ -466,12 +668,111 @@ 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::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; + } + 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; + } + 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 hops=%d", + i + 1, flood_channel_blocks[i].name, packet->getPathHashCount()); + 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 + && floodChannelDataHopApplies(packet)) { + MESH_DEBUG_PRINTLN("allowPacketForward: flood.channel.data off, blocking GRP_DATA hops=%d", + packet->getPathHashCount()); + 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"); @@ -606,8 +907,1026 @@ 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()) +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; +} + +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; + 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_cr8_snr_x4) return 8; + if (snr_x4 >= _prefs.direct_retry_cr7_snr_x4) return 7; + return 7; +} + +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 (!_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; + } + 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 +3.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) { + 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]; + 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 = getDirectRetryCodingRateForAttempt(getDirectRetryCodingRateForSNR(snr_x4), retry_attempt); +} + +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) { + 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; + 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; +} + +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 { + if (packet != NULL && packet->getPayloadType() == PAYLOAD_TYPE_TXT_MSG) { + return 21; + } + + 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 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) { + 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; +} + +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]; + 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); + 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 cr=%u preamble_len=%u", + event ? event : "?", + (uint32_t)retry_attempt, + (unsigned long)delay_millis, + type_label, + route_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 cr=%u preamble_len=%u\n", + event ? event : "?", + (uint32_t)retry_attempt, + (unsigned long)delay_millis, + type_label, + route_label, + target_label, + (uint32_t)log_cr, + (uint32_t)log_preamble_len); + 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, 11); + } + } +} + +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); + } +} + +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); + } + uint8_t log_cr = getRetryLogCodingRate(packet, getDefaultTxCodingRate()); + uint16_t log_preamble_len = getRetryLogPreambleLength(packet, radio_driver.getDefaultPreambleLength()); + + 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(), + packet->isRouteDirect() ? "D" : "F", + (uint32_t)packet->payload_len, + (unsigned int)packet->getPathHashCount(), + path_log, + heard_suffix, + time_label, + (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, cr=%u, preamble_len=%u)\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, + (uint32_t)log_cr, + (uint32_t)log_preamble_len); + 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 = ""; + if (v < 0) { + sign = "-"; + 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; + } +} + +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) { + strcpy(reply, "Error: unsupported"); + return; + } + int count = tables->getRecentRepeaterCount(); + if (count <= 0) { + strcpy(reply, "> -none-"); + return; + } + + 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 start = (page - 1) * page_size; + for (int i = 0; i < page_size && len < 150; i++) { + const SimpleMeshTables::RecentRepeaterInfo* info = tables->getRecentRepeaterBySortedIdx(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, "\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); +} + +void MyMesh::clearRecentRepeaters() { + SimpleMeshTables* tables = static_cast(getTables()); + if (tables != NULL) { + tables->clearRecentRepeaters(); + } +} + +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) { @@ -619,8 +1938,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, @@ -732,18 +2050,12 @@ 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 (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); - } 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"); } - } 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 @@ -765,13 +2077,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 (client->out_path_len == OUT_PATH_UNKNOWN) { - sendFloodReply(ack, TXT_ACK_DELAY, packet->getPathHashSize()); - } else { - sendDirect(ack, client->out_path, client->out_path_len, TXT_ACK_DELAY); - } - } + sendClientReply(client, ack, TXT_ACK_DELAY, packet->getPathHashSize()); } uint8_t temp[166]; @@ -780,7 +2086,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) { @@ -793,13 +2099,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 (client->out_path_len == OUT_PATH_UNKNOWN) { - sendFloodReply(reply, CLI_REPLY_DELAY_MILLIS, packet->getPathHashSize()); - } else { - sendDirect(reply, client->out_path, client->out_path_len, CLI_REPLY_DELAY_MILLIS); - } - } + sendClientReply(client, reply, CLI_REPLY_DELAY_MILLIS, packet->getPathHashSize()); } } else { MESH_DEBUG_PRINTLN("onPeerDataRecv: possible replay attack detected"); @@ -817,7 +2117,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); @@ -919,10 +2221,19 @@ 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_bw = 0.0f; + 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)); + recv_pkt_region = NULL; + memset(flood_channel_blocks, 0, sizeof(flood_channel_blocks)); #if MAX_NEIGHBOURS memset(neighbours, 0, sizeof(neighbours)); @@ -930,8 +2241,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc // defaults memset(&_prefs, 0, sizeof(_prefs)); - _prefs.airtime_factor = 1.0; // one half - _prefs.rx_delay_base = 0.0f; // turn off by default, was 10.0; + _prefs.airtime_factor = 1.0; + _prefs.rx_delay_base = DEFAULT_RX_DELAY_BASE; // fork kept this off by default (macro defaults 0.0f) _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)); @@ -943,12 +2254,41 @@ 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; + _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_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; + _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.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; #ifdef WITH_MQTT_BRIDGE _prefs.agc_reset_interval = 7; // 28 seconds (secs/4) — prevents AGC drift on long-running observers #endif @@ -981,7 +2321,6 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc #endif #endif _prefs.radio_fem_rxgain = 1; // LoRa FEM RX gain on by default (FEM boards) - _prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on') pending_discover_tag = 0; pending_discover_until = 0; @@ -998,6 +2337,7 @@ void MyMesh::begin(FILESYSTEM *fs) { acl.load(_fs, self_id); // TODO: key_store.begin(); region_map.load(_fs); + loadFloodChannelBlocks(); // establish default-scope { @@ -1068,7 +2408,7 @@ void MyMesh::begin(FILESYSTEM *fs) { _alerter.setBridge(bridge); #endif - radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); + applySavedRadioParams(); radio_driver.setTxPower(_prefs.tx_power_dbm); radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); @@ -1116,14 +2456,580 @@ bool MyMesh::resolveAlertScope(TransportKey& dest) { return false; } -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; + 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: ", 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_bw = bw; + 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() { @@ -1188,11 +3094,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); +bool MyMesh::setRxBoostedGain(bool enable) { + return radio_driver.setRxBoostedGainMode(enable); } -#endif void MyMesh::formatNeighborsReply(char *reply) { char *dp = reply; @@ -1268,6 +3172,349 @@ 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::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) { + return; + } + if (!_fs->exists(FLOOD_CHANNEL_BLOCK_FILE)) { + seedDefaultFloodChannelBlocks(); + saveFloodChannelBlocks(); + 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, "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); + 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; + 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); + } + 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', '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); + + 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); + } + + 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); +} + +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)) { + 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, 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); + 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, 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; + } + if (index < 0 || index > FLOOD_CHANNEL_BLOCK_SLOTS) { + 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); + 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; + 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); + } + 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; + 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; + } + out += written; + remaining -= written; + + for (int i = 0; i < FLOOD_CHANNEL_BLOCK_SLOTS && remaining > 1; i++) { + const char* display = "-"; + char short_display[6]; + const auto& entry = flood_channel_blocks[i]; + 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); + } + 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); } @@ -1304,7 +3551,129 @@ 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); +} + +// 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(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 @@ -1347,6 +3716,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]; @@ -1370,6 +3748,12 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply 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(); + 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++) { @@ -1381,6 +3765,97 @@ 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 = strncmp(command + 4, "altpath", 7) == 0; + if (sender == NULL) { + strcpy(reply, "Err - command needs remote client context"); + } else { + 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]; + 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(stored_path, 0, MAX_PATH_SIZE); + *stored_path_len = path_len; + } else { + *stored_path_len = mesh::Packet::copyPath(stored_path, path, path_len); + } + dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); + formatPathReply(stored_path, *stored_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++; @@ -1399,6 +3874,7 @@ void MyMesh::loop() { // Check radio FIRST to ensure we don't miss incoming packets // MQTT processing runs in a separate FreeRTOS task on Core 0, so we don't call bridge.loop() here mesh::Mesh::loop(); + checkBatteryAlert(); #ifdef WITH_BRIDGE // bridge.loop() is now handled by FreeRTOS task on Core 0 - no need to call it here @@ -1418,17 +3894,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); - 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); - MESH_DEBUG_PRINTLN("Radio params restored"); - } + processScheduledRadioSettings(); #if defined(WITH_MQTT_BRIDGE) && defined(OTA_MANIFEST_BASE) if (_ota_update_at && millisHasNowPassed(_ota_update_at)) { // deferred `ota update` @@ -1487,5 +3953,9 @@ bool MyMesh::hasPendingWork() const { #if defined(WITH_BRIDGE) if (bridge && bridge->isRunning()) return true; // bridge needs WiFi radio, can't sleep #endif - return _mgr->getOutboundCount(0xFFFFFFFF) > 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 d2a45b81..f37c621f 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -6,6 +6,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) @@ -88,11 +92,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; @@ -107,6 +132,24 @@ 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; + }; + 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]; + }; + 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; @@ -115,12 +158,11 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { NeighbourInfo neighbours[MAX_NEIGHBOURS]; #endif CayenneLPP telemetry; - unsigned long set_radio_at, revert_radio_at; unsigned long _ota_update_at = 0; // deferred `ota update` fire time (0 = none scheduled) - float pending_freq; - float pending_bw; - uint8_t pending_sf; - uint8_t pending_cr; + 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]; int matching_peer_indexes[MAX_CLIENTS]; #if defined(WITH_RS232_BRIDGE) RS232Bridge bridge; @@ -136,6 +178,30 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { AlertReporter _alerter; #endif + 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 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); @@ -143,19 +209,50 @@ 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; + 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; + 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; + 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 { return _prefs.airtime_factor; } - bool getCADEnabled() const override { - return _prefs.cad_enabled; - } - bool allowPacketForward(const mesh::Packet* packet) override; const char* getLogDateTime() override; void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override; @@ -167,10 +264,31 @@ 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; + 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, + 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; } + bool getCADEnabled() const override { + return _prefs.cad_enabled; + } int getAGCResetInterval() const override { return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds } @@ -189,7 +307,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; @@ -200,6 +318,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); @@ -228,6 +347,10 @@ public: bool sendAlertText(const char* text) override { return _alerter.sendText(text); } #endif bool resolveAlertScope(TransportKey& dest) 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; @@ -247,17 +370,28 @@ public: void formatRadioStatsReply(char *reply) override; void formatRadioDiagReply(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; + void setFloodChannelBlock(int index, const uint8_t* secret, uint8_t key_len, + 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; mesh::LocalIdentity& getSelfId() override { return self_id; } 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(); + uint32_t getPowerSaveSleepSeconds(uint32_t max_secs) const; #if defined(WITH_BRIDGE) void setBridgeState(bool enable) override { @@ -353,7 +487,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 + bool setRxBoostedGain(bool enable) override; + }; diff --git a/examples/simple_repeater/UITask.cpp b/examples/simple_repeater/UITask.cpp index 713c5bbb..e648ef5f 100644 --- a/examples/simple_repeater/UITask.cpp +++ b/examples/simple_repeater/UITask.cpp @@ -45,7 +45,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_repeater/main.cpp b/examples/simple_repeater/main.cpp index c56c0257..9ac4054c 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -127,7 +127,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); } @@ -157,13 +157,21 @@ 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 } + + 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 81389ab1..47bcb81f 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -310,8 +310,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) { @@ -323,8 +322,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, @@ -650,6 +648,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)); @@ -672,6 +671,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.radio_fem_rxgain = 1; // LoRa FEM RX gain on by default (FEM boards) _prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on') @@ -683,6 +683,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; // Observer defaults (alert.*, etc.) moved to applyMQTTDefaults() — they live // in /mqtt_prefs now, not NodePrefs. @@ -1130,3 +1131,49 @@ void MyMesh::loop() { _alerter.onLoop(now); #endif } + +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 && bridge->isRunning()) return true; // bridge needs WiFi radio, can't sleep +#endif + 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 cd556be3..4fc60c01 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -143,10 +143,6 @@ protected: return _prefs.airtime_factor; } - bool getCADEnabled() const override { - return _prefs.cad_enabled; - } - void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override; void logRx(mesh::Packet* pkt, int len, float score) override; void logTx(mesh::Packet* pkt, int len) override; @@ -160,14 +156,20 @@ 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 } 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; + 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; @@ -324,4 +326,12 @@ public: return bridge->ntpDiag(reply, reply_size, verbose); } #endif + 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/UITask.cpp b/examples/simple_room_server/UITask.cpp index cad436e5..47d00afa 100644 --- a/examples/simple_room_server/UITask.cpp +++ b/examples/simple_room_server/UITask.cpp @@ -45,7 +45,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/main.cpp b/examples/simple_room_server/main.cpp index a3798b21..a3872684 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,17 @@ void loop() { ui_task.loop(); #endif rtc_clock.tick(); + + if (the_mesh.getNodePrefs()->powersaving_enabled && !board.isUsbDataConnected()) { + uint32_t sleep_secs = the_mesh.getPowerSaveSleepSeconds(30); +#if defined(NRF52_PLATFORM) + if (sleep_secs > 0) { + board.sleep(0); // nrf ignores seconds param, sleeps whenever possible + } +#else + 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_secure_chat/main.cpp b/examples/simple_secure_chat/main.cpp index 273cda0a..42f70168 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; @@ -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.cpp b/examples/simple_sensor/SensorMesh.cpp index 475a38ff..b8455c86 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -327,6 +327,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 } @@ -729,6 +732,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.radio_fem_rxgain = 1; // LoRa FEM RX gain on by default (FEM boards) _prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on') @@ -737,6 +741,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)); } 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/examples/simple_sensor/UITask.cpp b/examples/simple_sensor/UITask.cpp index 757ea1dc..68a80607 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/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..c83d668d --- /dev/null +++ b/mesh-america/generate-mesh-america-catalogs.ps1 @@ -0,0 +1,632 @@ +$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]@{ + 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 = @( + @{ 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' }, + @{ 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 = 'companionUsb'; Title = 'Companion USB'; SubTitle = 'Serial' }, + @{ 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 }, + @{ 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 }, + @{ 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 = 'Serial terminal chat' }, + @{ Suffix = 'sensor'; Role = 'sensor'; Title = 'Sensor'; SubTitle = 'Sensor telemetry node' } +) + +$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' + 'Generic_E22_sx1268' = 'Generic E22' + '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' + '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 Vision Master T190' + '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_handheld_nrf_e22_30dbm' = 'Ikoka Handheld nRF E22 30 dBm' + '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' + '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 1W' + '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' + 'Meshadventurer_sx1268' = 'Meshadventurer' + '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' + '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)' + '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' + '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' + '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' + 'Xiao_S3_WIO' = 'Seeed Studio Xiao S3 WIO' +} + +$DeviceSubTitleOverrides = @{ + 'Generic_E22_sx1262' = 'SX1262' + 'Generic_E22_sx1268' = 'SX1268' + 'Heltec_t114_without_display' = 'Without display' + 'heltec_v4_3' = 'v4.3' + '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' = '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' = '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.' +} + +$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)) { + return $DeviceNameOverrides[$Prefix] + } + + $name = $Prefix.Trim('_') + $name = $name -replace '_', ' ' + $name = $name -replace '\s+', ' ' + 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, + [string]$RoleSubTitle + ) + + $parts = @() + if ($DeviceSubTitleOverrides.ContainsKey($DeviceKey) -and -not $DeviceVariantInTitle.ContainsKey($DeviceKey)) { + $parts += $DeviceSubTitleOverrides[$DeviceKey] + } + if ($RoleSubTitle) { + $parts += $RoleSubTitle + } + + if (-not $parts.Count) { + return $null + } + + 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 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, + [string[]]$DeviceKeys, + [string]$Role + ) + + $parts = @($Description) + foreach ($deviceKey in @($DeviceKeys | Sort-Object -Unique)) { + if ($DeviceSelectionNotes.ContainsKey($deviceKey)) { + $parts += $DeviceSelectionNotes[$deviceKey] + } + } + if ($RoleSelectionNotes.ContainsKey($Role)) { + $parts += $RoleSelectionNotes[$Role] + } + + return ($parts -join "`n`n") +} + +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 '_' -or $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 + MakerKey = Get-DeviceMakerKey $roleInfo.DeviceKey + DeviceName = $roleInfo.DeviceName + Role = $roleInfo.Role + Title = Join-FirmwareTitle $roleInfo.DeviceKey $roleInfo.Role $roleInfo.Title + SubTitle = Join-SubTitle $roleInfo.DeviceKey $roleInfo.SubTitle + } + } + + $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 + + $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 | + 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 + } + + $versionNotes = Join-VersionNotes -Description $Definition.Description -DeviceKeys @($roleGroup.Group | ForEach-Object { $_.DeviceKey }) -Role $first.Role + $firmwareEntry.version = [ordered]@{ + $Definition.Tag = [ordered]@{ + notes = $versionNotes + files = $files + } + } + + [void]$firmware.Add($firmwareEntry) + } + + [void]$devices.Add([ordered]@{ + 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 = $makerMap + 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) -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 new file mode 100644 index 00000000..d1fcf443 --- /dev/null +++ b/mesh-america/keymind-cascade-logging-v1.16.0-provider.json @@ -0,0 +1,8753 @@ +{ + "description": "Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.", + "maker": { + "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": { + "companionWifi": { + "name": "Companion WiFi", + "description": "Companion radio build that exposes the MeshCore companion interface over Wi-Fi instead of BLE or USB." + }, + "sensor": { + "name": "Sensor", + "description": "Sensor and telemetry build for boards that read and publish sensor data on the mesh." + }, + "terminalChat": { + "name": "Terminal Chat", + "description": "Standalone serial terminal chat build for text messaging from a terminal console." + } + }, + "device": [ + { + "maker": "ebyte", + "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", + "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": "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": "elecrow", + "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": "elecrow", + "name": "Elecrow ThinkNode M2", + "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": "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": "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", + "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": "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": "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": "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", + "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": "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": "elecrow", + "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": "elecrow", + "name": "Elecrow ThinkNode M5", + "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": "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": "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", + "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": "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": "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": "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", + "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": "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": "elecrow", + "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": "gat-iot", + "name": "GAT-IoT GAT562 30s", + "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": "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": [ + { + "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": "generic", + "name": "Generic E22", + "type": "esp32", + "firmware": [ + { + "role": "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": "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": "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.", + "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": "repeater", + "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.", + "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": "generic", + "name": "Generic 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": "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", + "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": "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": "heltec", + "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": "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": "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", + "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.\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", + "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": "heltec", + "name": "Heltec 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": "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": "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": "heltec", + "name": "Heltec Mesh Node 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": "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": "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", + "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.\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", + "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": "heltec", + "name": "Heltec Mesh Node 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": "heltec", + "name": "Heltec MeshPocket", + "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": "heltec", + "name": "Heltec MeshSolar / MeshTower", + "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": "heltec", + "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": "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": "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_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": "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": "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": "repeater", + "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": "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": "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", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "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" + } + ] + } + } + }, + { + "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", + "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": "heltec", + "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", + "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_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": "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": "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", + "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": "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": "heltec", + "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", + "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_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": "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": "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": "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": "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", + "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.\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", + "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", + "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": "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": "heltec", + "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": "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", + "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.\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_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": "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", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade 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": "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": "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": "repeater", + "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": "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", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade 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": "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", + "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.\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", + "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": "sensor", + "title": "Sensor", + "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.\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", + "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", + "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": "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, 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": "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": "heltec", + "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": "heltec", + "name": "Heltec Vision Master 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": "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": "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": "heltec", + "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": "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": "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": "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": "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": "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": [ + { + "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": "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": "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": "heltec", + "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", + "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_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": "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": "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", + "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.\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", + "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", + "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": "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": "heltec", + "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", + "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_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": "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": "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": "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": "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", + "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.\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", + "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": "Ikoka", + "name": "Ikoka Handheld nRF E22 30 dBm", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "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.", + "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 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.", + "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 - 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.", + "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 - 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.", + "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": "Ikoka", + "name": "Ikoka Nano", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "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.", + "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 - 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", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "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 - 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", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "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 - 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.", + "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 - 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.", + "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": "Ikoka", + "name": "Ikoka Stick", + "type": "nrf52", + "firmware": [ + { + "role": "companionUsb", + "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.", + "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 - 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", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "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 - 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", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "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 - 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.", + "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 - 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.", + "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": "keepteen", + "name": "Keepteen LT1", + "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": "lilygo", + "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", + "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": "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": "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": "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": "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": "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", + "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": "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": "lilygo", + "name": "LilyGo T3 S3 (SX126x)", + "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": "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": "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", + "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": "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": "lilygo", + "name": "LilyGo T3 S3 (SX127x)", + "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": "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": "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", + "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": "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": "lilygo", + "name": "LilyGo T-Beam (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": "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": "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": "lilygo", + "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": "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": "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": "lilygo", + "name": "LilyGo T-Beam 1W", + "type": "esp32", + "firmware": [ + { + "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.\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_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", + "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.\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": "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 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.\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_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": "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", + "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_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)", + "type": "esp32", + "firmware": [ + { + "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": "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": "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": "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": "lilygo", + "name": "LilyGo T-Deck", + "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": "lilygo", + "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": "lilygo", + "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": "lilygo", + "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", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "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": "lilygo", + "name": "LilyGo T-ETH 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": "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": [ + { + "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": "m5stack", + "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": "meshadventurer", + "name": "Meshadventurer", + "type": "esp32", + "firmware": [ + { + "role": "companionUsb", + "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.", + "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": "companionUsb", + "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.", + "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 - 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.", + "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": "repeater", + "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.", + "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 - 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.", + "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 - 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": { + "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_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": "meshimi", + "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": "meshtiny", + "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": "minewsemi", + "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", + "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", + "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": "muziworks", + "name": "Muzi Works R1 Neo", + "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", + "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.\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", + "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", + "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", + "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": "nibble", + "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", + "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": "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": "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": "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", + "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": "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": "promicro", + "name": "ProMicro nrf52 (faketec)", + "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": "repeater", + "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", + "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.\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", + "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", + "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", + "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": "rak", + "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": "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": "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", + "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": "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": "rak", + "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", + "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.\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", + "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": "rak", + "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": "repeater", + "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": "repeater", + "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", + "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.\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", + "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", + "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", + "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": "rak", + "name": "RAK WisBlock 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", + "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": "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": "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": "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": "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": "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", + "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.\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", + "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", + "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": "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": "rak", + "name": "RAK WisMesh 1W Booster (3401 + 13302)", + "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", + "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.\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", + "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", + "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", + "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": "rak", + "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", + "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.\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", + "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": "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", + "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": "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": [ + { + "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": "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": "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", + "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": "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": "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": [ + { + "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": "seeed", + "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": "seeed", + "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": "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 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.", + "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": "companionUsb", + "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.", + "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 - 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.", + "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": "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 - Wio-E5 mini", + "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.\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", + "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": [ + { + "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", + "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": "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": "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": [ + { + "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": "seeed", + "name": "Seeed Studio 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", + "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": "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", + "type": "esp32", + "firmware": [ + { + "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.\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_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 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.\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_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": "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", + "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_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", + "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.\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", + "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", + "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": "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", + "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": "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": "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": "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", + "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.\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", + "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", + "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": "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": "tenstar", + "name": "Tenstar C3", + "type": "esp32", + "firmware": [ + { + "role": "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": "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": "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.", + "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": "repeater", + "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.", + "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": "tinyrelay", + "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", + "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.\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", + "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": "uniteng", + "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": "uniteng", + "name": "UnitEng 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", + "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": "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 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": "repeater", + "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": "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", + "version": { + "logging-v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade 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": "uniteng", + "name": "UnitEng/BQ Voyage Station G3", + "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", + "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": "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": "why2025", + "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)" + } + ] + } + } + } + ] + } + ] +} 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..b4784d2c --- /dev/null +++ b/mesh-america/keymind-cascade-v1.16.0-provider.json @@ -0,0 +1,11112 @@ +{ + "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": { + "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": { + "companionWifi": { + "name": "Companion WiFi", + "description": "Companion radio build that exposes the MeshCore companion interface over Wi-Fi instead of BLE or USB." + }, + "sensor": { + "name": "Sensor", + "description": "Sensor and telemetry build for boards that read and publish sensor data on the mesh." + }, + "terminalChat": { + "name": "Terminal Chat", + "description": "Standalone serial terminal chat build for text messaging from a terminal console." + } + }, + "device": [ + { + "maker": "ebyte", + "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", + "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": "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": "elecrow", + "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": "elecrow", + "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": "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": "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", + "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": "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": "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": "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", + "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": "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": "elecrow", + "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": "elecrow", + "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": "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": "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", + "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": "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": "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": "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", + "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": "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": "elecrow", + "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": "gat-iot", + "name": "GAT-IoT GAT562 30s", + "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": "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": [ + { + "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": "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", + "type": "esp32", + "firmware": [ + { + "role": "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": "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": "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.", + "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": "repeater", + "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.", + "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": "generic", + "name": "Generic 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": "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", + "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": "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": "heltec", + "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": "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": "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", + "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.\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", + "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": "heltec", + "name": "Heltec 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": "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": "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": "heltec", + "name": "Heltec Mesh Node 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": "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": "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", + "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.\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", + "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": "heltec", + "name": "Heltec Mesh Node 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": "heltec", + "name": "Heltec MeshPocket", + "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": "heltec", + "name": "Heltec MeshSolar / MeshTower", + "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": "heltec", + "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": "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": "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_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": "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": "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_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": "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": "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": "repeater", + "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": "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": "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", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.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" + } + ] + } + } + }, + { + "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", + "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": "heltec", + "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", + "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_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": "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": "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", + "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": "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": "heltec", + "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", + "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_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": "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": "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": "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": "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", + "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.\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", + "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", + "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": "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": "heltec", + "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": "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", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.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": "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", + "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.\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_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": "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", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.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": "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": "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": "repeater", + "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": "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", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.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": "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", + "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.\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", + "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": "sensor", + "title": "Sensor", + "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.\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", + "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", + "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": "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, 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": "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": "heltec", + "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": "heltec", + "name": "Heltec Vision Master 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": "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": "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": "heltec", + "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": "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": "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": "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": "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": "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": [ + { + "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": "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": "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": "heltec", + "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", + "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_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": "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": "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", + "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.\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", + "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", + "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": "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": "heltec", + "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", + "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_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": "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": "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": "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": "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", + "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.\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", + "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": "Ikoka", + "name": "Ikoka Handheld nRF E22 30 dBm", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "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.", + "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": "companionBle", + "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.", + "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 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.", + "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 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.", + "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" + } + ] + } + } + }, + { + "role": "repeater", + "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.", + "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 - 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" + } + ] + } + } + } + ] + }, + { + "maker": "Ikoka", + "name": "Ikoka Nano", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "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_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 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", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.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 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", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.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 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.", + "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 - 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.", + "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 - 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.", + "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": "Ikoka", + "name": "Ikoka Stick", + "type": "nrf52", + "firmware": [ + { + "role": "companionBle", + "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_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 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", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.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 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", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.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 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.", + "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 - 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.", + "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 - 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.", + "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": "keepteen", + "name": "Keepteen LT1", + "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": "lilygo", + "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", + "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": "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": "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": "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": "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": "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", + "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": "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": "lilygo", + "name": "LilyGo T3 S3 (SX126x)", + "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": "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": "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", + "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": "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": "lilygo", + "name": "LilyGo T3 S3 (SX127x)", + "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": "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": "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", + "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": "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": "lilygo", + "name": "LilyGo T-Beam (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": "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": "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": "lilygo", + "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": "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": "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": "lilygo", + "name": "LilyGo T-Beam 1W", + "type": "esp32", + "firmware": [ + { + "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.\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_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": "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.\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_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", + "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_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", + "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.\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": "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 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.\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_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": "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", + "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_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)", + "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", + "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": "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": "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": "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": "lilygo", + "name": "LilyGo T-Deck", + "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": "lilygo", + "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": "lilygo", + "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": "lilygo", + "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": "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", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.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": "lilygo", + "name": "LilyGo T-ETH 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": "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": [ + { + "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": "m5stack", + "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": "meshadventurer", + "name": "Meshadventurer", + "type": "esp32", + "firmware": [ + { + "role": "companionBle", + "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_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": "companionBle", + "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.", + "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 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.", + "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 - 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.", + "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": "repeater", + "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.", + "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 - 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.", + "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 - 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": { + "notes": "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_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": "meshimi", + "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": "meshtiny", + "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": "minewsemi", + "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", + "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", + "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": "muziworks", + "name": "Muzi Works R1 Neo", + "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", + "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.\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", + "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", + "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", + "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": "nibble", + "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", + "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": "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": "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": "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", + "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": "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": "promicro", + "name": "ProMicro nrf52 (faketec)", + "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": "repeater", + "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", + "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.\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", + "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", + "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", + "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": "rak", + "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": "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": "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", + "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": "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": "rak", + "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", + "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.\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", + "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": "rak", + "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": "repeater", + "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": "repeater", + "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", + "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.\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", + "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", + "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", + "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": "rak", + "name": "RAK WisBlock 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", + "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": "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": "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": "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": "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": "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", + "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.\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", + "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", + "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": "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": "rak", + "name": "RAK WisMesh 1W Booster (3401 + 13302)", + "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", + "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.\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", + "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", + "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", + "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": "rak", + "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", + "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.\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", + "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": "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", + "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": "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": [ + { + "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": "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": "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", + "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": "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": "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": [ + { + "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": "seeed", + "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": "seeed", + "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": "seeed", + "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": "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 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.", + "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": "companionUsb", + "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.", + "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 - 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.", + "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": "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 - Wio-E5 mini", + "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.\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", + "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": [ + { + "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", + "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": "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": "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": [ + { + "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": "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", + "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": "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", + "type": "esp32", + "firmware": [ + { + "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.\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_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": "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.\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_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", + "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_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 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.\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_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": "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", + "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_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", + "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.\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", + "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", + "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": "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", + "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": "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": "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": "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", + "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.\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", + "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", + "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": "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": "tenstar", + "name": "Tenstar C3", + "type": "esp32", + "firmware": [ + { + "role": "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": "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": "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.", + "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": "repeater", + "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.", + "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": "tinyrelay", + "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", + "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.\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", + "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": "uniteng", + "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": "uniteng", + "name": "UnitEng 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", + "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": "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 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": "repeater", + "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": "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", + "version": { + "v1.16.0-halo-keymind-dev-28ee5d2c": { + "notes": "Keymind Cascade MeshCore v1.16.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": "uniteng", + "name": "UnitEng/BQ Voyage Station G3", + "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", + "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": "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": "why2025", + "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)" + } + ] + } + } + } + ] + } + ] +} diff --git a/platformio.ini b/platformio.ini index e16f7b83..47cc0ab8 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 @@ -164,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/Dispatcher.cpp b/src/Dispatcher.cpp index 2a491a61..c0fe2f72 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -52,6 +52,17 @@ void Dispatcher::updateTxBudget() { } } +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 { return (int) ((pow(10, 0.85f - score) - 1.0) * air_time); } @@ -139,7 +150,9 @@ void Dispatcher::loop() { _radio->onSendFinished(); last_radio_active_ms = _ms->getMillis(); // TX success → radio is alive + restoreOutboundTxOverrides(); logTx(outbound, 2 + outbound->getPathByteLen() + outbound->payload_len); + onSendComplete(outbound); if (outbound->isRouteFlood()) { n_sent_flood++; } else { @@ -151,7 +164,9 @@ void Dispatcher::loop() { MESH_DEBUG_PRINTLN("%s Dispatcher::loop(): WARNING: outbound packed send timed out!", getLogDateTime()); _radio->onSendFinished(); + restoreOutboundTxOverrides(); logTxFail(outbound, 2 + outbound->getPathByteLen() + outbound->payload_len); + onSendFail(outbound); releasePacket(outbound); // return to pool outbound = NULL; @@ -182,6 +197,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()); @@ -301,7 +317,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); + } } } @@ -352,18 +371,47 @@ 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; uint32_t max_airtime = _radio->getEstAirtimeFor(len)*3/2; + 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 + && 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; + 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()); + restoreOutboundTxOverrides(); logTxFail(outbound, outbound->getRawLength()); + onSendFail(outbound); releasePacket(outbound); // return to pool outbound = NULL; @@ -393,6 +441,7 @@ Packet* Dispatcher::obtainNewPacket() { } else { pkt->payload_len = pkt->path_len = 0; pkt->_snr = 0; + pkt->tx_cr = 0; } return pkt; } @@ -401,13 +450,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 @@ -420,4 +477,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 67f706c3..557e9c8f 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -46,6 +46,14 @@ 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; } + virtual uint16_t getDefaultPreambleLength() const { return 0; } + virtual bool setPreambleLength(uint16_t len) { return false; } + /** * \returns true if the previous 'startSendRaw()' completed successfully. */ @@ -101,7 +109,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; @@ -137,6 +145,8 @@ class Dispatcher { unsigned long outbound_expiry, outbound_start, total_air_time, rx_air_time; unsigned long last_watchdog_recovery; unsigned long last_radio_active_ms; // updated on any TX or RX event; used by watchdog + uint16_t outbound_restore_preamble_len; + uint8_t outbound_restore_cr; unsigned long next_tx_time; unsigned long cad_busy_start; unsigned long radio_nonrx_start; @@ -149,6 +159,7 @@ class Dispatcher { unsigned long duty_cycle_window_ms; void processRecvPacket(Packet* pkt); + void restoreOutboundTxOverrides(); void updateTxBudget(); protected: @@ -161,6 +172,8 @@ 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(); cad_busy_start = 0; @@ -182,12 +195,15 @@ 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; 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 bool getCADEnabled() const { return false; } // hardware CAD disabled by default virtual int getAGCResetInterval() const { return 0; } // disabled by default @@ -195,6 +211,9 @@ protected: #ifdef WITH_MQTT_BRIDGE virtual uint32_t getRadioWatchdogMillis() const; // observer-only radio recovery #endif + 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(); @@ -202,7 +221,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; } @@ -222,8 +241,6 @@ public: bool millisHasNowPassed(unsigned long timestamp) const; unsigned long futureMillis(int millis_from_now) const; - bool tryParsePacket(Packet* pkt, const uint8_t* raw, int len); - private: void checkRecv(); void checkSend(); diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 7252974a..0ecb2412 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -3,12 +3,212 @@ namespace mesh { +static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT = 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; + +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; +} + +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; +} + +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; + _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; + 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; + _direct_retries[i].waiting_final_echo = false; + _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(); } void Mesh::loop() { Dispatcher::loop(); + + for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) { + 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, + _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; + } + + if (!_direct_retries[i].queued || !millisHasNowPassed(_direct_retries[i].retry_at)) { + continue; + } + + if (!isDirectRetryQueued(_direct_retries[i].packet)) { + if (_direct_retries[i].packet == getOutboundInFlight()) { + continue; // currently transmitting; keep slot until onSendComplete/onSendFail emits event + } + 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) { @@ -22,10 +222,86 @@ 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 { + (void)packet; + (void)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 { + return 200 + getDirectRetryPacketAirtimeDelay(packet); +} +uint8_t Mesh::getDirectRetryMaxAttempts(const Packet* packet) const { + 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) { + 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); +} +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 { + (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) { + 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 { return _rng->nextInt(1, 4)*120; } @@ -39,6 +315,12 @@ 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) { if (pkt->path_len < MAX_PATH_SIZE) { uint8_t i = 0; @@ -47,18 +329,24 @@ 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); + // 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 * (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->wasSeen(pkt)) { + _tables->markSeen(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); - 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; @@ -83,25 +371,37 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { } } - if (self_id.isHashMatch(pkt->path, pkt->getPathHashSize()) && 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->wasSeen(pkt)) { // don't retransmit! + _tables->markSeen(pkt); + removePathPrefix(pkt, 1); + routeDirectRecvAcks(pkt, 0); + } + return ACTION_RELEASE; } - return ACTION_RELEASE; - } - if (!_tables->hasSeen(pkt)) { - removeSelfFromPath(pkt); + if (!_tables->wasSeen(pkt)) { + _tables->markSeen(pkt); + removePathPrefix(pkt, 1); - uint32_t d = getDirectRetransmitDelay(pkt); - 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; @@ -115,7 +415,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); } @@ -132,7 +433,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(?) @@ -153,6 +455,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; @@ -191,7 +497,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); @@ -218,7 +525,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); @@ -249,7 +557,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; } @@ -276,7 +585,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) } @@ -294,7 +604,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); @@ -315,13 +626,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); } } @@ -334,8 +648,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; } @@ -351,8 +667,9 @@ 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! - removeSelfFromPath(&tmp); + if (!_tables->wasSeen(&tmp)) { // don't retransmit! + _tables->markSeen(&tmp); + removePathPrefix(&tmp, 1); routeDirectRecvAcks(&tmp, ((uint32_t)remaining + 1) * 300); // expect multipart ACKs 300ms apart (x2) } } @@ -369,6 +686,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--; @@ -379,11 +697,634 @@ 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_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; + 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; + _direct_retries[idx].waiting_final_echo = 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; + } + + 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. + _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); + 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, + _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) { + Packet* pending = _mgr->removeOutboundByIdx(j); + if (pending) { + releasePacket(pending); + } + break; + } + } + } + clearDirectRetrySlot(i); + } else { + if (_direct_retries[i].trigger_packet != NULL) { + _direct_retries[i].trigger_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].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; + } + + 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. + 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].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); + 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) { + // 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; + continue; + } + + Packet* retry = obtainNewPacket(); + if (retry == NULL) { + 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; + } + + *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); + 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); + _direct_retries[i].waiting_final_echo = false; + 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, + _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); + } + } + 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) { + 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; + } + + *retry = *packet; + retry->tx_cr = 0; + configureDirectRetryPacket(retry, packet, 1); + + // Start the echo wait only after the initial direct transmission actually completed. + 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; + _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; + 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, + _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); + } + } +} + +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. + 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, + _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); + } + } +} + +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: + // 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; + 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() == 0) { + 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 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; + if (offset + hash_size > route_bytes) { + return false; + } + + 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; + } +} + +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; + 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) { + 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; + } + + // 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); + _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; + 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; + _direct_retries[slot_idx].waiting_final_echo = false; + _direct_retries[slot_idx].queued = false; + _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; @@ -540,7 +1481,9 @@ Packet* Mesh::createGroupDatagram(uint8_t type, const GroupChannel& channel, con return packet; } -Packet* Mesh::createAck(const uint8_t* ack, uint8_t len) { +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()); @@ -548,13 +1491,19 @@ Packet* Mesh::createAck(const uint8_t* ack, uint8_t len) { } packet->header = (PAYLOAD_TYPE_ACK << PH_TYPE_SHIFT); // ROUTE_TYPE_* set later - memcpy(packet->payload, ack, len); - packet->payload_len = len; + memcpy(packet->payload, ack_hash, ack_len); + packet->payload_len = ack_len; return packet; } -Packet* Mesh::createMultiAck(const uint8_t* ack, uint8_t len, 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()); @@ -563,12 +1512,16 @@ Packet* Mesh::createMultiAck(const uint8_t* ack, uint8_t len, 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, len); - packet->payload_len = 1 + len; + memcpy(&packet->payload[1], ack_hash, ack_len); + packet->payload_len = 1 + ack_len; 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 @@ -631,7 +1584,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) { @@ -641,6 +1594,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); } @@ -660,7 +1614,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) { @@ -670,6 +1624,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); } @@ -684,7 +1639,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) { @@ -693,7 +1648,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); } @@ -703,7 +1659,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); } @@ -716,9 +1672,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 2302d6b5..8a4bdb6c 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -4,6 +4,18 @@ namespace mesh { +#ifndef MAX_DIRECT_RETRY_SLOTS + #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]; @@ -15,8 +27,10 @@ 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 markSent(const Packet* packet) = 0; + virtual void clear(const Packet* packet) = 0; // remove this packet hash from table }; /** @@ -24,17 +38,72 @@ 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_started_at; + unsigned long echo_wait_started_at; + unsigned long retry_at; + 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 payload_type; + uint8_t priority; + uint8_t progress_marker; + bool expect_path_growth; + bool waiting_final_echo; + bool queued; + 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 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; + 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; + 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); protected: DispatcherAction onRecvPacket(Packet* pkt) override; + void onSendComplete(Packet* packet) override; + void onSendFail(Packet* packet) override; virtual uint32_t getCADFailRetryDelay() const override; @@ -65,11 +134,109 @@ protected: */ virtual uint32_t getDirectRetransmitDelay(const Packet* packet); + /** + * \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; + + /** + * \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 a retry of the DIRECT packet. + */ + 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. + */ + 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); + + /** + * \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. */ 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, 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. + */ + 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) { } + + /** + * \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); + /** * \brief Perform search of local DB of peers/contacts. * \returns Number of peers with matching hash @@ -100,7 +267,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) { } @@ -185,10 +352,10 @@ 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, uint8_t len); - Packet* createAck(uint32_t ack_crc) { return createAck((uint8_t *) &ack_crc, 4); } - Packet* createMultiAck(const uint8_t* ack, uint8_t len, uint8_t remaining); - Packet* createMultiAck(uint32_t ack_crc, uint8_t remaining) { return createMultiAck((uint8_t *)&ack_crc, 4, remaining); } + 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); Packet* createRawData(const uint8_t* data, size_t len); diff --git a/src/MeshCore.h b/src/MeshCore.h index f0c8f387..6a3eecda 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -64,6 +64,7 @@ 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 // Pull-based OTA: fetch the firmware build for this variant from a baked-in manifest and flash it. // current_ver is the running firmware version string (used to skip if already up to date); when // dry_run is true the build is only reported, not flashed. Observer (ESP32+WiFi) builds only. @@ -77,11 +78,14 @@ public: // 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"; } 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/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..be97c28c 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 //... @@ -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/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..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/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index d3ef034e..972a97e9 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 @@ -139,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())) { @@ -930,11 +942,11 @@ 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) { - 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 c04bfda3..d9878547 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 @@ -166,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); 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.cpp b/src/helpers/ClientACL.cpp index 12823827..2ee73480 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 } @@ -99,7 +109,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; } @@ -115,6 +126,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..e12fe418 100644 --- a/src/helpers/ClientACL.h +++ b/src/helpers/ClientACL.h @@ -4,19 +4,23 @@ #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 +#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) @@ -31,6 +35,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 diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index b9ad2eea..c6c4ef93 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -6,6 +6,32 @@ #include #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) + #ifndef BRIDGE_MAX_BAUD #define BRIDGE_MAX_BAUD 115200 #endif @@ -20,6 +46,8 @@ #include "MQTTDefaults.h" #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; @@ -30,6 +58,30 @@ 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) { @@ -49,12 +101,538 @@ static const size_t LEGACY_MQTT_GAP_6SLOT = 306 + 6 * 186; // 1422 static const size_t LEGACY_MQTT_GAP_3SLOT = 306 + 3 * 186; // 864 static const size_t LEGACY_OBS_TAIL_MAX = 124; // rx_boosted(1) + flood(2) + snmp(25) + watchdog(1) + alert block(95) -// Bytes savePrefs() writes after owner_info (offsets 290-294): rx_boosted_gain, -// flood_max_unscoped, flood_max_advert, radio_fem_rxgain, cad_enabled. loadPrefsInt() -// treats any larger remainder as a legacy MQTT-gap file — keep this in sync with the -// trailing writes in savePrefs() whenever an upstream merge appends /com_prefs fields. -static const size_t COM_PREFS_TAIL_BYTES = 5; +static bool looksNumeric(const char* s) { + if (s == NULL) return false; + while (*s == ' ') s++; + if (*s == '-' || *s == '+') s++; + bool saw_digit = false; + bool saw_dot = false; + while (*s) { + if (*s >= '0' && *s <= '9') { + saw_digit = true; + } else if (*s == '.' && !saw_dot) { + saw_dot = true; + } else if (*s == ' ') { + while (*s == ' ') s++; + return saw_digit && *s == 0; + } else { + break; + } + s++; + } + return saw_digit && *s == 0; +} +static bool looksUnsignedInteger(const char* s) { + if (s == NULL) return false; + while (*s == ' ') s++; + bool saw_digit = false; + while (*s) { + if (*s >= '0' && *s <= '9') { + saw_digit = true; + } else if (*s == ' ') { + while (*s == ' ') s++; + return saw_digit && *s == 0; + } else { + return false; + } + 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)); +} + +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 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 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; + 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 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) { + 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; + } + applyFloodRetryPreset(prefs, prefs->retry_preset); + 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; + prefs->direct_retry_enabled = 1; + prefs->direct_retry_recent_enabled = DIRECT_RETRY_RECENT_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 > RECENT_REPEATER_PREFIX_MAX_BYTES * 2) { + return false; + } + for (size_t i = 0; i < hex_len; i++) { + if (!mesh::Utils::isHexChar(text[i])) { + 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) { bool is_fresh_install = false; @@ -144,7 +722,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 @@ -160,10 +739,14 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { // (upstream defaults: FEM RX gain on, CAD off) — overwritten below if present. _prefs->radio_fem_rxgain = 1; _prefs->cad_enabled = 0; - // A remainder larger than the new-format tail means an old fork file with the - // legacy MQTT gap; detect and recover it below. + // A remainder larger than the smallest legacy MQTT gap (864) means an old fork + // file with the zero-filled gap; detect and recover it below. Anything smaller + // (upstream/flex 5-byte tails, or the ~384-byte keymind retry tail) takes the + // normal path: guarded 290-294 reads, then the keymind retry tail whose absence + // in shorter files is detected via direct_retry_prefs_magic. size_t extra = file.available(); - if (extra > COM_PREFS_TAIL_BYTES) { + bool has_flood_retry_prefs = false; // set when the keymind flood-retry tail is read below + if (extra > LEGACY_MQTT_GAP_3SLOT) { _com_prefs_needs_upgrade = true; size_t gap = 0; if (extra > LEGACY_MQTT_GAP_6SLOT && extra <= LEGACY_MQTT_GAP_6SLOT + LEGACY_OBS_TAIL_MAX) { @@ -274,6 +857,73 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { if (file.available() >= (int)sizeof(_prefs->cad_enabled)) { // 294 file.read((uint8_t *)&_prefs->cad_enabled, sizeof(_prefs->cad_enabled)); } + // 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_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 + 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; + _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; + 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)); + } + 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)); + } + 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 } // sanitise bad pref values @@ -282,7 +932,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); @@ -290,8 +940,6 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _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 _prefs->loop_detect = constrain(_prefs->loop_detect, 0, 3); // LOOP_DETECT_OFF..LOOP_DETECT_STRICT - _prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean - _prefs->cad_enabled = constrain(_prefs->cad_enabled, 0, 1); // boolean // sanitise bad bridge pref values _prefs->bridge_enabled = constrain(_prefs->bridge_enabled, 0, 1); @@ -301,11 +949,56 @@ 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); _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); + 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; + } + _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_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); + _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->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) { + _prefs->battery_alert_low_percent = BATTERY_ALERT_LOW_PERCENT_DEFAULT; + _prefs->battery_alert_critical_percent = BATTERY_ALERT_CRITICAL_PERCENT_DEFAULT; + } file.close(); } @@ -359,7 +1052,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 @@ -367,13 +1061,42 @@ 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 // MQTT/observer settings are stored in /mqtt_prefs, not here. No zero-gap is - // written anymore — /com_prefs holds only the (non-observer) fields below. - // These trailing writes are COM_PREFS_TAIL_BYTES; keep the two in sync. - 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 - 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 + // written anymore — /com_prefs holds the upstream fields plus the keymind + // retry tail below (loadPrefsInt reads the same sequence; keep in sync). + 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 + 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_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 + 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)); + 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(); } @@ -701,11 +1424,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"); @@ -731,11 +1460,15 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re (int)heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL), (int)heap_caps_get_free_size(MALLOC_CAP_SPIRAM), (int)heap_caps_get_total_size(MALLOC_CAP_SPIRAM)); - } else if (memcmp(command, "start ota", 9) == 0) { - // Manual OTA: bring up the ElegantOTA SoftAP for a hand-uploaded binary. + } else if (memcmp(command, "start ota", 9) == 0 && (command[9] == 0 || command[9] == ' ')) { + // Manual OTA: bring up the ElegantOTA server for a hand-uploaded binary. 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); @@ -773,7 +1506,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 { @@ -787,10 +1520,15 @@ 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) { 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"); @@ -925,13 +1663,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 @@ -947,6 +1693,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)); +#if defined(ENV_INCLUDE_GPS) && 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"); @@ -1000,28 +1776,6 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep _prefs->cad_enabled = memcmp(&config[4], "on", 2) == 0; savePrefs(); strcpy(reply, "OK"); - } 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, "agc.reset.interval ", 19) == 0) { _prefs->agc_reset_interval = atoi(&config[19]) / 4; savePrefs(); @@ -1083,13 +1837,37 @@ 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"); + bool enabled = memcmp(&config[13], "on", 2) == 0; + _prefs->rx_boosted_gain = enabled; savePrefs(); - _callbacks->setRxBoostedGain(_prefs->rx_boosted_gain); -#endif + if (_callbacks->setRxBoostedGain(enabled)) { + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error: unsupported"); + } + } 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]; @@ -1098,7 +1876,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; @@ -1108,6 +1886,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(); @@ -1170,6 +1970,292 @@ 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 ", 13) == 0) { + if (strcmp(&config[13], "on") == 0) { + _prefs->direct_retry_enabled = 1; + savePrefs(); + strcpy(reply, "OK"); + } else if (strcmp(&config[13], "off") == 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.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"); + } 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 = looksUnsignedInteger(&config[19]) ? _atoi(&config[19]) : -1; + 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 = looksUnsignedInteger(&config[18]) ? _atoi(&config[18]) : -1; + 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 = looksUnsignedInteger(&config[18]) ? _atoi(&config[18]) : -1; + 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, "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; + 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.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]; + 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]; + 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; + 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, block_hops, reply); + } + } 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; + 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 = 12; // default to +3.0 dB when omitted or invalid + if (num < 1 || !parseHashPrefix(parts[0], prefix, prefix_len)) { + strcpy(reply, "Error, use: set recent.repeater [snr_db]"); + } else if (num > 1 && !looksNumeric(parts[1])) { + strcpy(reply, "Error, SNR must be numeric"); + } else { + if (num > 1) { + snr_x4 = parseSnrDbX4(parts[1]); + } + 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)) { + 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"); + } + } } else if (memcmp(config, "owner.info ", 11) == 0) { config += 11; char *dp = _prefs->owner_info; @@ -1288,8 +2374,21 @@ 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 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 { sprintf(reply, "unknown config: %s", config); } @@ -1299,6 +2398,7 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep const char* config = &command[4]; // Observer/MQTT/WiFi/timezone/alert/SNMP commands live in CommonCLI_Observer.cpp. if (handleObserverGetCmd(sender_timestamp, config, reply)) return; + int recent_page = 1; if (memcmp(config, "dutycycle", 9) == 0) { float dc = 100.0f / (_prefs->airtime_factor + 1.0f); int dc_int = (int)dc; @@ -1309,13 +2409,7 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep } 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, "radio.fem.rxgain", 16) == 0) { - if (!_board->canControlLoRaFemLna()) { - strcpy(reply, "Error: unsupported"); - } else { - sprintf(reply, "> %s", _board->isLoRaFemLnaEnabled() ? "on" : "off"); - } + 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) { @@ -1341,10 +2435,18 @@ 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"); + } 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)); @@ -1354,6 +2456,38 @@ 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 (strcmp(config, "flood.channel.data.hops") == 0) { + char hops[8]; + 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]; + 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]; + 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) { @@ -1362,6 +2496,59 @@ 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", 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); + 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, "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"); + } 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 (parseRecentRepeaterGet(config, recent_page)) { + _callbacks->formatRecentRepeatersReply(reply, recent_page); } else if (memcmp(config, "owner.info", 10) == 0) { *reply++ = '>'; *reply++ = ' '; @@ -1429,12 +2616,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); } @@ -1452,24 +2639,60 @@ 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()); #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); } } +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 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); + } +} + static char* skipSpaces(char* s) { while (*s == ' ') s++; return s; diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index ef4a797c..af746f6d 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -20,6 +20,85 @@ #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_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 + +#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 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 +#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]; @@ -29,7 +108,6 @@ struct NodePrefs { // persisted to file int8_t tx_power_dbm; uint8_t disable_fwd; uint8_t advert_interval; // minutes / 2 - uint8_t rx_boosted_gain; // power settings (file offset 79) uint8_t flood_advert_interval; // hours float rx_delay_base; float tx_delay_factor; @@ -46,7 +124,6 @@ struct NodePrefs { // persisted to file uint8_t flood_max_advert; uint8_t interference_threshold; uint8_t agc_reset_interval; // secs / 4 - uint8_t path_hash_mode; // which path mode to use when sending // Bridge settings uint8_t bridge_enabled; // boolean uint16_t bridge_delay; // milliseconds (default 500 ms) @@ -56,6 +133,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 @@ -63,18 +141,46 @@ struct NodePrefs { // persisted to file uint32_t discovery_mod_timestamp; float adc_multiplier; char owner_info[120]; - + // NOTE: member order below matches mcarper/keymindCascade (upstream/dev order). + // It is in-memory only — /com_prefs is read/written field-by-field in loadPrefsInt/ + // savePrefs, whose canonical file order (identical to the flex fleet's through + // offset 294, keymind retry tail at 295+) is what devices actually persist. + 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; - - // Restored from upstream (dropped by the 22eb9b87 revert). Persisted at the same - // /com_prefs offsets upstream uses (293, 294) so the file stays upstream-aligned. - uint8_t radio_fem_rxgain; // LoRa FEM RX-gain (LNA); default on. Hardware driving is - // wired per-board in the FEM-restore change; persisted here. - uint8_t cad_enabled; // hardware Channel Activity Detection before TX; default off + 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_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; + uint8_t flood_channel_data_enabled; + uint8_t flood_channel_block_max_hops; + uint8_t flood_channel_data_max_hops; // NOTE: observer settings (MQTT/WiFi/timezone/SNMP/alert) were moved out of // NodePrefs into MQTTPrefs (persisted to /mqtt_prefs) so this struct stays - // aligned with upstream. See struct MQTTPrefs below. + // aligned with upstream (plus the keymind retry/flood tail above). See + // struct MQTTPrefs below. }; #ifdef WITH_MQTT_BRIDGE @@ -295,10 +401,60 @@ public: virtual void formatRadioStatsReply(char *reply) = 0; virtual void formatRadioDiagReply(char *reply) { strcpy(reply, "Not supported"); } 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; 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 setFloodChannelBlock(int index, const uint8_t* secret, uint8_t key_len, + 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) { + (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 @@ -349,8 +505,8 @@ public: return false; // WITH_MQTT_BRIDGE builds override }; - virtual void setRxBoostedGain(bool enable) { - // no op by default + virtual bool setRxBoostedGain(bool enable) { + return false; // CommonCLI reports unsupported if not overridden by wrapper }; // Fault-alert channel hooks (see NodePrefs::alert_*). The default no-op @@ -380,7 +536,7 @@ class CommonCLI { SensorManager* _sensors; RegionMap* _region_map; ClientACL* _acl; - char tmp[PRV_KEY_SIZE*2 + 4]; + char tmp[COMMON_CLI_TMP_LEN]; #ifdef WITH_MQTT_BRIDGE MQTTPrefs _mqtt_prefs; LegacyObserverTail _legacy_tail; @@ -402,6 +558,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); // Observer/MQTT/WiFi/timezone/alert/SNMP CLI handling lives in the fork-owned // CommonCLI_Observer.cpp to keep these branches out of the upstream-tracked diff --git a/src/helpers/ESP32Board.cpp b/src/helpers/ESP32Board.cpp index 5a457638..5910209f 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 @@ -13,6 +14,12 @@ bool ESP32Board::startOTAUpdate(const char* id, char reply[]) { inhibit_sleep = true; // prevent sleep during OTA + if (ota_server != nullptr) { // already running (idempotent restart) + IPAddress cur_ip = (WiFi.status() == WL_CONNECTED) ? WiFi.localIP() : WiFi.softAPIP(); + sprintf(reply, "Started: http://%s/update", cur_ip.toString().c_str()); + return true; + } + // If the device is already on a WiFi network (e.g. an observer joined in STA // mode), serve ElegantOTA on the station IP so it's reachable from the LAN // without joining a separate AP. Otherwise raise the MeshCore-OTA SoftAP. @@ -32,18 +39,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; } @@ -52,6 +77,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 // --------------------------------------------------------------------------- @@ -419,4 +448,47 @@ bool ESP32Board::otaFromManifest(const char* current_ver, bool dry_run, char rep } #endif // WITH_MQTT_BRIDGE +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(); + + #ifdef P_LORA_NSS + // 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 + #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 eae9ab78..ba3fa57a 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -14,11 +14,15 @@ #include #include "soc/rtc.h" #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: @@ -62,8 +66,15 @@ public: return raw / 4; } + virtual void powerOff() override; + 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 { @@ -73,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); @@ -151,30 +170,86 @@ public: } bool startOTAUpdate(const char* id, char reply[]) override; + bool stopOTAUpdate(char reply[]) override; bool otaFromManifest(const char* current_ver, bool dry_run, char reply[]) override; // Heavy body (TLS + JSON / HTTPUpdate). Runs in a dedicated large-stack task // spawned by otaFromManifest() — public only so that task entry point can call // it; not meant to be invoked directly. bool otaFromManifestImpl(const char* current_ver, bool dry_run, char reply[]); + 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; } + + 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); @@ -185,6 +260,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/NRF52Board.cpp b/src/helpers/NRF52Board.cpp index 17265f04..802ff042 100644 --- a/src/helpers/NRF52Board.cpp +++ b/src/helpers/NRF52Board.cpp @@ -1,19 +1,38 @@ #if defined(NRF52_PLATFORM) #include "NRF52Board.h" +#include #include +#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; +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"); } @@ -251,6 +270,18 @@ bool NRF52Board::isExternalPowered() { } } +bool NRF52Board::isUsbDataConnected() { +#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 +} + 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 @@ -297,6 +328,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 @@ -317,13 +379,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 @@ -332,8 +408,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 @@ -355,12 +431,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 17065cf4..f578c390 100644 --- a/src/helpers/NRF52Board.h +++ b/src/helpers/NRF52Board.h @@ -50,10 +50,13 @@ 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 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; } @@ -76,4 +79,4 @@ public: NRF52BoardDCDC() {} virtual void begin() override; }; -#endif \ No newline at end of file +#endif 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 } diff --git a/src/helpers/RxReservePacketManager.h b/src/helpers/RxReservePacketManager.h index 96d66827..23c8f046 100644 --- a/src/helpers/RxReservePacketManager.h +++ b/src/helpers/RxReservePacketManager.h @@ -59,16 +59,16 @@ public: for (int i = 0; i < pool_size; i++) { _ages[i].pkt = NULL; _ages[i].scheduled_for = 0; } } - 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 { int free_count = getFreeCount(); if (free_count < _emergency_floor || (free_count < _rx_reserve && priority > MAX_PROTECTED_PRI)) { MESH_DEBUG_PRINTLN("RxReservePacketManager: pool below RX reserve, shedding outbound (pri %d)", (int)priority); free(packet); - return; + return false; } recordAge(packet, scheduled_for); - StaticPoolPacketManager::queueOutbound(packet, priority, scheduled_for); + return StaticPoolPacketManager::queueOutbound(packet, priority, scheduled_for); } mesh::Packet* getNextOutbound(uint32_t now) override { 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/SimpleMeshTables.h b/src/helpers/SimpleMeshTables.h index 0b79cfb4..ad47f5c7 100644 --- a/src/helpers/SimpleMeshTables.h +++ b/src/helpers/SimpleMeshTables.h @@ -1,29 +1,164 @@ #pragma once #include +#if ARDUINO + #include +#endif #ifdef ESP32 #include #endif #define MAX_PACKET_HASHES (128+32) +#ifndef MESH_ENABLE_RECENT_REPEATERS + #define MESH_ENABLE_RECENT_REPEATERS 0 +#endif +#ifndef MAX_RECENT_REPEATERS + // 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 + #else + #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 { +public: + 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; + uint32_t last_heard_millis; + }; + +private: uint8_t _hashes[MAX_PACKET_HASHES*MAX_HASH_SIZE]; int _next_idx; uint32_t _direct_dups, _flood_dups; + RecentRepeaterInfo _recent_repeaters[RECENT_REPEATER_STORAGE_SLOTS]; + + 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 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; + } + + 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. + 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++; + } + if (blended > 127) { + blended = 127; + } else if (blended < -128) { + blended = -128; + } + return (int8_t)blended; + } + + 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; + 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; + } + + 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) { + 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) { + return; + } + setRecentRepeater(prefix, prefix_len, packet->_snr); + } public: SimpleMeshTables() { memset(_hashes, 0, sizeof(_hashes)); _next_idx = 0; _direct_dups = _flood_dups = 0; + memset(_recent_repeaters, 0, sizeof(_recent_repeaters)); } #ifdef ESP32 void restoreFrom(File f) { f.read(_hashes, sizeof(_hashes)); f.read((uint8_t *) &_next_idx, sizeof(_next_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)); } void saveTo(File f) { f.write(_hashes, sizeof(_hashes)); @@ -31,34 +166,47 @@ 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 (packet->isRouteDirect()) { - _direct_dups++; // keep some stats - } else { - _flood_dups++; - } - return true; + if (hasSeenHash(hash)) { + if (packet->isRouteDirect()) { + _direct_dups++; // keep some stats + } 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); + if (!hasSeenHash(hash)) { + storeHash(hash); + recordRecentRepeater(packet); + } + } + + void markSent(const mesh::Packet* packet) override { + // Outbound packets must be marked as already-sent without teaching the recent-heard cache about ourselves. + uint8_t hash[MAX_HASH_SIZE]; + packet->calculatePacketHash(hash); + if (!hasSeenHash(hash)) { + storeHash(hash); + } + } + void clear(const mesh::Packet* packet) override { 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) { + if (memcmp(hash, sp, MAX_HASH_SIZE) == 0) { memset(sp, 0, MAX_HASH_SIZE); break; } @@ -68,5 +216,175 @@ 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 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; + } + + if (prefix_len > MAX_ROUTE_HASH_BYTES) { + prefix_len = MAX_ROUTE_HASH_BYTES; + } + + // 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 != prefix_len || memcmp(existing.prefix, prefix, prefix_len) != 0) { + 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; + } + + int slot_idx = -1; + for (int i = 0; i < MAX_RECENT_REPEATERS; i++) { + if (_recent_repeaters[i].prefix_len == 0) { + slot_idx = i; + break; + } + } + if (slot_idx < 0) { + // Table is full: evict the oldest heard entry. + slot_idx = 0; +#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++) { + 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]; + memset(slot.prefix, 0, sizeof(slot.prefix)); + 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) { + if (MAX_RECENT_REPEATERS == 0) { + return false; + } + 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 != prefix_len || memcmp(existing.prefix, prefix, prefix_len) != 0) { + continue; + } + 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; + } + 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) { + count++; + } + } + return count; + } + const RecentRepeaterInfo* getRecentRepeaterBySortedIdx(int idx_wanted) const { + if (MAX_RECENT_REPEATERS == 0) { + return NULL; + } + if (idx_wanted < 0) { + return NULL; + } + + 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 (best == NULL) { + return NULL; + } + last = best; + last_idx = best_idx; + } + return last; + } + + 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; + } + + // 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++) { + const RecentRepeaterInfo* info = &_recent_repeaters[i]; + if (info->prefix_len == 0) { + continue; + } + 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 + || (info->prefix_len == best->prefix_len && info->snr_x4 > best->snr_x4)) { + best = info; + } + } + } + return best; + } + void clearRecentRepeaters() { + memset(_recent_repeaters, 0, sizeof(_recent_repeaters)); + } + void resetStats() { _direct_dups = _flood_dups = 0; } }; 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 +}; 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 b9eb1c10..ab205b03 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) { @@ -32,7 +41,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; @@ -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); @@ -167,7 +176,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/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 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/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/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/radiolib/CustomLLCC68Wrapper.h b/src/helpers/radiolib/CustomLLCC68Wrapper.h index 8861f76d..87ed4519 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(); } @@ -33,8 +37,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..0a9a0a70 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) { } @@ -16,6 +20,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(); @@ -26,6 +34,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 @@ -36,8 +49,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/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/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 } diff --git a/src/helpers/radiolib/CustomSX1262Wrapper.h b/src/helpers/radiolib/CustomSX1262Wrapper.h index cc7bb223..11726035 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(); } @@ -40,8 +44,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..f847992f 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(); } @@ -37,8 +41,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/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.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp index 07259bfc..6930cf5d 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; @@ -68,8 +69,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() @@ -196,7 +197,10 @@ bool RadioLibWrapper::isChannelActive() { // try to read a non-existent packet and count a spurious recv error. state = STATE_IDLE; startRecv(); - if (result != RADIOLIB_CHANNEL_FREE) return true; + if (result != RADIOLIB_CHANNEL_FREE) { + _board->n_cad_busy++; + return true; + } } return false; diff --git a/src/helpers/radiolib/RadioLibWrappers.h b/src/helpers/radiolib/RadioLibWrappers.h index 20d096f3..3c732568 100644 --- a/src/helpers/radiolib/RadioLibWrappers.h +++ b/src/helpers/radiolib/RadioLibWrappers.h @@ -9,12 +9,12 @@ 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; unsigned long last_recv_millis; unsigned long last_radio_interrupt_millis; // updated on any ISR event, even CRC errors uint8_t _preamble_sf; - bool _cad_enabled; void idle() override; void startRecv() override; @@ -47,6 +47,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); @@ -77,7 +79,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; } }; diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 73842d9e..cfa215a4 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -8,6 +8,12 @@ #define TELEM_WIRE &Wire // Use default I2C bus for Environment Sensors #endif +bool EnvironmentSensorManager::i2c_probe(TwoWire& wire, uint8_t addr) { + wire.beginTransmission(addr); + uint8_t error = wire.endTransmission(); + return error == 0; +} + // ============================================================ // Sensor library includes and static driver instances // ============================================================ @@ -15,6 +21,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 @@ -40,6 +47,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 +71,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 +81,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 +567,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/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index 29147c89..b91d2dc3 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -41,6 +41,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 || defined(ENV_INCLUDE_BME680_BSEC) 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; 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 a5d647a7..be4912a5 100644 --- a/src/helpers/ui/OLEDDisplay.cpp +++ b/src/helpers/ui/OLEDDisplay.cpp @@ -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 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/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(); +} 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_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/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_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/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_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/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/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/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 diff --git a/variants/heltec_ct62/platformio.ini b/variants/heltec_ct62/platformio.ini index 0179d965..2dd55881 100644 --- a/variants/heltec_ct62/platformio.ini +++ b/variants/heltec_ct62/platformio.ini @@ -130,6 +130,9 @@ 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 + [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_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/heltec_t096/LoRaFEMControl.h b/variants/heltec_t096/LoRaFEMControl.h index 0ce60fff..a3b5c4ed 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 = 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 5cff3c37..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 { @@ -126,6 +122,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; } @@ -141,4 +141,4 @@ bool T096Board::canControlLoRaFemLna() const { bool T096Board::isLoRaFemLnaEnabled() const { return loRaFEMControl.isLNAEnabled(); -} \ No newline at end of file +} diff --git a/variants/heltec_t096/platformio.ini b/variants/heltec_t096/platformio.ini index e820bf58..e8762f89 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] +[env:Heltec_t096_companion_radio_ble_femon] extends = Heltec_t096 board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld board_upload.maximum_size = 712704 @@ -143,6 +143,15 @@ lib_deps = ${Heltec_t096.lib_deps} densaugeo/base64 @ ~1.4.0 +[env:Heltec_t096_companion_radio_ble] +extends = env:Heltec_t096_companion_radio_ble_femon + +[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_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/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_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; 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_tower_v2/HeltecTowerV2Board.cpp b/variants/heltec_tower_v2/HeltecTowerV2Board.cpp new file mode 100644 index 00000000..04213bda --- /dev/null +++ b/variants/heltec_tower_v2/HeltecTowerV2Board.cpp @@ -0,0 +1,103 @@ +#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); + loRaFEMControl.setSleepModeEnable(); + + 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(); + + pinMode(P_LORA_TX_LED, OUTPUT); + digitalWrite(P_LORA_TX_LED, !LED_STATE_ON); + + 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); + loRaFEMControl.init(); +} + +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(); +} + +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); + loRaFEMControl.setSleepModeEnable(); + 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..6912acde --- /dev/null +++ b/variants/heltec_tower_v2/HeltecTowerV2Board.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include +#include "LoRaFEMControl.h" + +class HeltecTowerV2Board : public NRF52BoardDCDC { +protected: +#ifdef NRF52_POWER_MANAGEMENT + void initiateShutdown(uint8_t reason) override; +#endif + +public: + LoRaFEMControl loRaFEMControl; + + HeltecTowerV2Board() : NRF52Board("TOWER_V2_OTA") {} + void begin(); + void onBeforeTransmit() override; + void onAfterTransmit() override; + 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 new file mode 100644 index 00000000..f029b7d4 --- /dev/null +++ b/variants/heltec_tower_v2/platformio.ini @@ -0,0 +1,104 @@ +[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=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} + + + + + +<../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..cfb88c4a --- /dev/null +++ b/variants/heltec_tower_v2/variant.cpp @@ -0,0 +1,42 @@ +#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_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); + 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); + 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 new file mode 100644 index 00000000..352184b8 --- /dev/null +++ b/variants/heltec_tower_v2/variant.h @@ -0,0 +1,108 @@ +#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 + 30) +#define PIN_WIRE_SCL (0 + 5) +#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 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) +#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 diff --git a/variants/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini index 69293d70..f25d0c4b 100644 --- a/variants/heltec_tracker/platformio.ini +++ b/variants/heltec_tracker/platformio.ini @@ -99,6 +99,9 @@ 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 + [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 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_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_tracker_v2/platformio.ini b/variants/heltec_tracker_v2/platformio.ini index 688b1c7d..217ec8ad 100644 --- a/variants/heltec_tracker_v2/platformio.ini +++ b/variants/heltec_tracker_v2/platformio.ini @@ -177,6 +177,9 @@ 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 + [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..2723fdc2 100644 --- a/variants/heltec_v2/platformio.ini +++ b/variants/heltec_v2/platformio.ini @@ -172,6 +172,9 @@ 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 + [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..728f57c3 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; @@ -53,6 +51,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_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 4bd3819a..04b5aa09 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -274,6 +274,9 @@ 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 + [env:Heltec_v3_companion_radio_wifi] extends = Heltec_lora32_v3 build_flags = @@ -418,6 +421,9 @@ 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 + [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 ac804cc2..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() { @@ -85,9 +64,14 @@ void HeltecV4Board::begin() { } 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; diff --git a/variants/heltec_v4/HeltecV4Board.h b/variants/heltec_v4/HeltecV4Board.h index d59f730d..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) { @@ -36,8 +37,4 @@ public: } float getAdcMultiplier() const override { return adc_mult; } 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 4ab9b9c9..277d6cd2 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=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 f3622bc5..d7b90bb5 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 @@ -459,6 +460,18 @@ 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 + +[env:heltec_v4_companion_radio_ble_ps] +extends = env:heltec_v4_companion_radio_ble_ps_femon + +[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 = @@ -466,7 +479,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"' @@ -623,6 +635,13 @@ 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 +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..f059b212 100644 --- a/variants/heltec_wireless_paper/platformio.ini +++ b/variants/heltec_wireless_paper/platformio.ini @@ -64,6 +64,9 @@ 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 + [env:Heltec_Wireless_Paper_companion_radio_usb] extends = Heltec_Wireless_Paper_base build_flags = 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_t3s3/platformio.ini b/variants/lilygo_t3s3/platformio.ini index 4a259ee7..c5d3db26 100644 --- a/variants/lilygo_t3s3/platformio.ini +++ b/variants/lilygo_t3s3/platformio.ini @@ -260,6 +260,9 @@ 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 + [env:LilyGo_T3S3_sx1262_kiss_modem] extends = LilyGo_T3S3_sx1262 build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} 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_tbeam_1w/platformio.ini b/variants/lilygo_tbeam_1w/platformio.ini index 827cac38..d3245fe0 100644 --- a/variants/lilygo_tbeam_1w/platformio.ini +++ b/variants/lilygo_tbeam_1w/platformio.ini @@ -146,6 +146,10 @@ 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 + ; === 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 2e0d8334..bf237604 100644 --- a/variants/lilygo_tbeam_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_SX1262/platformio.ini @@ -65,6 +65,12 @@ 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 +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 41f56115..0492ec6e 100644 --- a/variants/lilygo_tbeam_SX1276/platformio.ini +++ b/variants/lilygo_tbeam_SX1276/platformio.ini @@ -61,6 +61,9 @@ 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 + [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 7c588e7c..d210aad7 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -223,6 +223,9 @@ 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 + [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_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/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); 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(); diff --git a/variants/lilygo_tlora_v2_1/platformio.ini b/variants/lilygo_tlora_v2_1/platformio.ini index 37a909f8..77d81754 100644 --- a/variants/lilygo_tlora_v2_1/platformio.ini +++ b/variants/lilygo_tlora_v2_1/platformio.ini @@ -104,7 +104,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 @@ -120,6 +120,9 @@ 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 + [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/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/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"' 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/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/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/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/> diff --git a/variants/nibble_zero_connect/platformio.ini b/variants/nibble_zero_connect/platformio.ini new file mode 100644 index 00000000..83495cd8 --- /dev/null +++ b/variants/nibble_zero_connect/platformio.ini @@ -0,0 +1,158 @@ +[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 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 + +[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..d53b6a1c --- /dev/null +++ b/variants/nibble_zero_connect/target.cpp @@ -0,0 +1,35 @@ +#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); +#endif + +#ifndef LORA_CR + #define LORA_CR 5 +#endif + +bool radio_init() { + fallback_clock.begin(); + 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/nibble_zero_connect/target.h b/variants/nibble_zero_connect/target.h new file mode 100644 index 00000000..87c55f0f --- /dev/null +++ b/variants/nibble_zero_connect/target.h @@ -0,0 +1,28 @@ +#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(); +mesh::LocalIdentity radio_new_identity(); + + 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/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/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/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/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/sensecap_solar/platformio.ini b/variants/sensecap_solar/platformio.ini index 8f3d89a0..e478d2b9 100644 --- a/variants/sensecap_solar/platformio.ini +++ b/variants/sensecap_solar/platformio.ini @@ -11,7 +11,7 @@ build_flags = ${nrf52_base.build_flags} -UENV_INCLUDE_GPS -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/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/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/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..d38a50ad 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 @@ -16,4 +16,3 @@ extern SensorManager sensors; bool radio_init(); mesh::LocalIdentity radio_new_identity(); - 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_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_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_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/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/thinknode_m5/platformio.ini b/variants/thinknode_m5/platformio.ini index 8572d1eb..cab1c1a3 100644 --- a/variants/thinknode_m5/platformio.ini +++ b/variants/thinknode_m5/platformio.ini @@ -165,6 +165,9 @@ 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 + [env:ThinkNode_M5_companion_radio_usb] extends = ThinkNode_M5 build_flags = @@ -213,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} 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-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"' 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"' 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/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 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..0eddb0e5 100644 --- a/variants/xiao_c3/platformio.ini +++ b/variants/xiao_c3/platformio.ini @@ -90,6 +90,10 @@ 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 +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/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(); } }; 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..c70e16f2 100644 --- a/variants/xiao_s3/platformio.ini +++ b/variants/xiao_s3/platformio.ini @@ -114,6 +114,9 @@ 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 + [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 8e4314cf..9df3d213 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -6,6 +6,7 @@ board_build.mcu = esp32s3 build_flags = ${esp32_base.build_flags} -I variants/xiao_s3_wio -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 @@ -246,6 +247,9 @@ 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 + [env:Xiao_S3_WIO_companion_radio_serial] extends = Xiao_S3_WIO build_flags =