From 75725061c545c5f3250f2f9c6e3aebce892376ff Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 00:20:47 +0300 Subject: [PATCH 01/27] fix: use HELTEC_V4 + 16MB flash for Detector (fixes battery_v=0 on V4 boards) --- sdkconfig.detector | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdkconfig.detector b/sdkconfig.detector index ed5cc4c..1c43bcb 100644 --- a/sdkconfig.detector +++ b/sdkconfig.detector @@ -1,6 +1,6 @@ # Board (determines set-target chip and flash size) -CONFIG_BATEAR_BOARD_HELTEC_V3=y -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_BATEAR_BOARD_HELTEC_V4=y +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y # Role CONFIG_BATEAR_ROLE_DETECTOR=y From 3f4a1450028183e900f299ff5e2124e58bc682a0 Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 00:23:29 +0300 Subject: [PATCH 02/27] ci: fix Detector flash_size 16MB for Heltec V4 (was 8MB causing V3 build) --- .github/workflows/esp-idf-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/esp-idf-build.yml b/.github/workflows/esp-idf-build.yml index 6635414..84a4c0d 100644 --- a/.github/workflows/esp-idf-build.yml +++ b/.github/workflows/esp-idf-build.yml @@ -21,7 +21,7 @@ jobs: matrix: include: - role: detector - flash_size: 8MB + flash_size: 16MB - role: gateway flash_size: 8MB - role: wired_detector From 1fd932de008a224d2929e20cbd8fa6a1722c89c8 Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 00:55:15 +0300 Subject: [PATCH 03/27] flasher: point REPO to popshark1/batear for V4-fixed releases --- docs/flasher/index.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/flasher/index.html b/docs/flasher/index.html index 6c6316a..327c327 100644 --- a/docs/flasher/index.html +++ b/docs/flasher/index.html @@ -430,7 +430,7 @@

Wait & Done

Harmonic frequency analysis — real drone spectrograms available at
- batear-io/batear-datasets

+ popshark1/batear-datasets

@@ -521,9 +521,9 @@

Wait & Done

@@ -535,7 +535,7 @@

Wait & Done

document.getElementById("notSupported").style.display = "block"; } - const REPO = "batear-io/batear"; + const REPO = "popshark1/batear"; const NVS_OFFSET = 0x9000; /* ---- Role state ---- */ From 58ea1d136519670bdd8fff0752b4b329edb41a59 Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 02:00:30 +0300 Subject: [PATCH 04/27] fix(gateway): Heltec V4 board config + 16MB flash size --- sdkconfig.gateway | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdkconfig.gateway b/sdkconfig.gateway index 774fe2c..6e23f66 100644 --- a/sdkconfig.gateway +++ b/sdkconfig.gateway @@ -1,6 +1,6 @@ # Board (determines set-target chip and flash size) -CONFIG_BATEAR_BOARD_HELTEC_V3=y -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_BATEAR_BOARD_HELTEC_V4=y +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y # Role CONFIG_BATEAR_ROLE_GATEWAY=y From 735c0df7f4d741e727321eb4305568f9fba143ae Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 02:34:36 +0300 Subject: [PATCH 05/27] feat(flasher): add V3/V4 manifests and board selection UI --- docs/flasher/manifest_detector_v3.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 docs/flasher/manifest_detector_v3.json diff --git a/docs/flasher/manifest_detector_v3.json b/docs/flasher/manifest_detector_v3.json new file mode 100644 index 0000000..b46496f --- /dev/null +++ b/docs/flasher/manifest_detector_v3.json @@ -0,0 +1,15 @@ +{ + "name": "Batear Detector (Heltec V3)", + "new_install_prompt_erase": true, + "builds": [ + { + "chipFamily": "ESP32-S3", + "parts": [ + { + "path": "firmware/firmware-latest/merged_detector_v3.bin", + "offset": 0 + } + ] + } + ] +} From 7f8e91485a637b68b92f2900d1626ac527119d02 Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 02:35:23 +0300 Subject: [PATCH 06/27] feat(flasher): add manifest_detector_v4.json --- docs/flasher/manifest_detector_v4.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 docs/flasher/manifest_detector_v4.json diff --git a/docs/flasher/manifest_detector_v4.json b/docs/flasher/manifest_detector_v4.json new file mode 100644 index 0000000..77959f6 --- /dev/null +++ b/docs/flasher/manifest_detector_v4.json @@ -0,0 +1,15 @@ +{ + "name": "Batear Detector (Heltec V4)", + "new_install_prompt_erase": true, + "builds": [ + { + "chipFamily": "ESP32-S3", + "parts": [ + { + "path": "firmware/firmware-latest/merged_detector_v4.bin", + "offset": 0 + } + ] + } + ] +} From 9534ec24fdfc887148de1abebd76a23664e7a776 Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 02:36:12 +0300 Subject: [PATCH 07/27] feat(flasher): add manifest_gateway_v3.json --- docs/flasher/manifest_gateway_v3.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 docs/flasher/manifest_gateway_v3.json diff --git a/docs/flasher/manifest_gateway_v3.json b/docs/flasher/manifest_gateway_v3.json new file mode 100644 index 0000000..5e3f3e5 --- /dev/null +++ b/docs/flasher/manifest_gateway_v3.json @@ -0,0 +1,15 @@ +{ + "name": "Batear Gateway (Heltec V3)", + "new_install_prompt_erase": true, + "builds": [ + { + "chipFamily": "ESP32-S3", + "parts": [ + { + "path": "firmware/firmware-latest/merged_gateway_v3.bin", + "offset": 0 + } + ] + } + ] +} From dd0860e786d0734a726071809a863f1baf6fd410 Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 02:36:55 +0300 Subject: [PATCH 08/27] feat(flasher): add manifest_gateway_v4.json --- docs/flasher/manifest_gateway_v4.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 docs/flasher/manifest_gateway_v4.json diff --git a/docs/flasher/manifest_gateway_v4.json b/docs/flasher/manifest_gateway_v4.json new file mode 100644 index 0000000..541da5c --- /dev/null +++ b/docs/flasher/manifest_gateway_v4.json @@ -0,0 +1,15 @@ +{ + "name": "Batear Gateway (Heltec V4)", + "new_install_prompt_erase": true, + "builds": [ + { + "chipFamily": "ESP32-S3", + "parts": [ + { + "path": "firmware/firmware-latest/merged_gateway_v4.bin", + "offset": 0 + } + ] + } + ] +} From d37e625312540edef42cf8f0e95b3774f3a7e8b9 Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 02:38:03 +0300 Subject: [PATCH 09/27] feat: add sdkconfig.detector.v3 (Heltec V3, 8MB flash) --- sdkconfig.detector.v3 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 sdkconfig.detector.v3 diff --git a/sdkconfig.detector.v3 b/sdkconfig.detector.v3 new file mode 100644 index 0000000..64ebcfe --- /dev/null +++ b/sdkconfig.detector.v3 @@ -0,0 +1,15 @@ +# Board (determines set-target chip and flash size) +CONFIG_BATEAR_BOARD_HELTEC_V3=y +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y + +# Role +CONFIG_BATEAR_ROLE_DETECTOR=y +CONFIG_BATEAR_DEVICE_ID=1 + +# Network (must match between all detectors and gateway) +CONFIG_BATEAR_NET_KEY="DEADBEEFCAFEBABE13374200F00DAA55" +CONFIG_BATEAR_LORA_FREQ=915000 +CONFIG_BATEAR_LORA_SYNC_WORD=0x12 + +# Telemetry heartbeat (jittered +-10% in firmware) +CONFIG_BATEAR_TELEMETRY_HEARTBEAT_MIN=30 From 361c93348bdde07faa93db255d256f3e237c908b Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 02:38:34 +0300 Subject: [PATCH 10/27] feat: add sdkconfig.gateway.v3 (Heltec V3, 8MB flash) --- sdkconfig.gateway.v3 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 sdkconfig.gateway.v3 diff --git a/sdkconfig.gateway.v3 b/sdkconfig.gateway.v3 new file mode 100644 index 0000000..d672870 --- /dev/null +++ b/sdkconfig.gateway.v3 @@ -0,0 +1,14 @@ +# Board (determines set-target chip and flash size) +CONFIG_BATEAR_BOARD_HELTEC_V3=y +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y + +# Role +CONFIG_BATEAR_ROLE_GATEWAY=y + +# Network (must match between all detectors and gateway) +CONFIG_BATEAR_NET_KEY="DEADBEEFCAFEBABE13374200F00DAA55" +CONFIG_BATEAR_LORA_FREQ=915000 +CONFIG_BATEAR_LORA_SYNC_WORD=0x12 + +# Telemetry heartbeat (jittered +-10% in firmware) +CONFIG_BATEAR_TELEMETRY_HEARTBEAT_MIN=30 From 8f89f0baaa1733a2a04823b987c1ec2660a97ca9 Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 02:42:32 +0300 Subject: [PATCH 11/27] Add V3/V4 matrix builds for detector and gateway Added matrix builds for detector and gateway with V3 and V4 configurations. Updated artifact handling and size reporting for new binaries. --- .github/workflows/esp-idf-build.yml | 109 +++++++++------------------- 1 file changed, 34 insertions(+), 75 deletions(-) diff --git a/.github/workflows/esp-idf-build.yml b/.github/workflows/esp-idf-build.yml index 84a4c0d..fc72a5c 100644 --- a/.github/workflows/esp-idf-build.yml +++ b/.github/workflows/esp-idf-build.yml @@ -2,8 +2,7 @@ name: ESP-IDF Build CI on: push: - branches: [ "main" ] - tags: [ "v*" ] +ci: add V3/V4 matrix builds for detector and gateway tags: [ "v*" ] pull_request: branches: [ "main" ] workflow_dispatch: @@ -13,18 +12,29 @@ permissions: pull-requests: write jobs: + build: name: Build ${{ matrix.role }} runs-on: ubuntu-latest container: espressif/idf:release-v6.0 strategy: + fail-fast: false matrix: include: - - role: detector + - role: detector_v3 + sdkconfig: sdkconfig.detector.v3 + flash_size: 8MB + - role: detector_v4 + sdkconfig: sdkconfig.detector flash_size: 16MB - - role: gateway + - role: gateway_v3 + sdkconfig: sdkconfig.gateway.v3 flash_size: 8MB + - role: gateway_v4 + sdkconfig: sdkconfig.gateway + flash_size: 16MB - role: wired_detector + sdkconfig: sdkconfig.wired_detector flash_size: 16MB steps: @@ -38,7 +48,7 @@ jobs: run: | . $IDF_PATH/export.sh rm -rf build sdkconfig - env SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.${{ matrix.role }}" idf.py set-target esp32s3 + env SDKCONFIG_DEFAULTS="sdkconfig.defaults;${{ matrix.sdkconfig }}" idf.py set-target esp32s3 idf.py build - name: Capture size @@ -51,7 +61,7 @@ jobs: . $IDF_PATH/export.sh esptool.py --chip esp32s3 merge_bin -o merged_${{ matrix.role }}.bin \ --flash_mode dio --flash_size ${{ matrix.flash_size }} \ - 0x0 build/bootloader/bootloader.bin \ + 0x0 build/bootloader/bootloader.bin \ 0x8000 build/partition_table/partition-table.bin \ 0x10000 build/batear.bin @@ -68,7 +78,6 @@ jobs: needs: build if: github.event_name == 'pull_request' runs-on: ubuntu-latest - steps: - name: Download artifacts uses: actions/download-artifact@v4 @@ -81,69 +90,22 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} run: | - CUR_DET=$(stat -c %s merged_detector.bin) - CUR_GW=$(stat -c %s merged_gateway.bin) + CUR_DET_V3=$(stat -c %s merged_detector_v3.bin) + CUR_DET_V4=$(stat -c %s merged_detector_v4.bin) + CUR_GW_V3=$(stat -c %s merged_gateway_v3.bin) + CUR_GW_V4=$(stat -c %s merged_gateway_v4.bin) CUR_WD=$(stat -c %s merged_wired_detector.bin) - REPO="${{ github.repository }}" R=/tmp/size_report.md - sign() { local v=$1; [ "$v" -gt 0 ] && echo "+${v}" || echo "${v}"; } - echo "## Firmware Size Report" > "$R" echo "" >> "$R" - - if curl -sfL -H "Authorization: Bearer ${GITHUB_TOKEN}" \ - "https://github.com/${REPO}/releases/download/firmware-latest/merged_detector.bin" \ - -o /tmp/prev_detector.bin && \ - curl -sfL -H "Authorization: Bearer ${GITHUB_TOKEN}" \ - "https://github.com/${REPO}/releases/download/firmware-latest/merged_gateway.bin" \ - -o /tmp/prev_gateway.bin; then - PREV_DET=$(stat -c %s /tmp/prev_detector.bin) - PREV_GW=$(stat -c %s /tmp/prev_gateway.bin) - DET_DELTA=$(sign $((CUR_DET - PREV_DET))) - GW_DELTA=$(sign $((CUR_GW - PREV_GW))) - - # Wired detector may not exist in previous release - if curl -sfL -H "Authorization: Bearer ${GITHUB_TOKEN}" \ - "https://github.com/${REPO}/releases/download/firmware-latest/merged_wired_detector.bin" \ - -o /tmp/prev_wired_detector.bin; then - PREV_WD=$(stat -c %s /tmp/prev_wired_detector.bin) - WD_DELTA=$(sign $((CUR_WD - PREV_WD))) - else - PREV_WD="—" - WD_DELTA="new" - fi - - echo "| Role | Current | Previous | Delta |" >> "$R" - echo "|------|--------:|--------:|------:|" >> "$R" - echo "| Detector | $CUR_DET B | $PREV_DET B | $DET_DELTA B |" >> "$R" - echo "| Gateway | $CUR_GW B | $PREV_GW B | $GW_DELTA B |" >> "$R" - echo "| Wired Detector | $CUR_WD B | $PREV_WD B | $WD_DELTA B |" >> "$R" - echo "" >> "$R" - echo "> Compared against [\`firmware-latest\`](https://github.com/${REPO}/releases/tag/firmware-latest)." >> "$R" - else - echo "| Role | Size |" >> "$R" - echo "|------|-----:|" >> "$R" - echo "| Detector | $CUR_DET B |" >> "$R" - echo "| Gateway | $CUR_GW B |" >> "$R" - echo "| Wired Detector | $CUR_WD B |" >> "$R" - echo "" >> "$R" - echo "> No previous release found for comparison." >> "$R" - fi - - echo "" >> "$R" - echo "
idf.py size details" >> "$R" - echo "" >> "$R" - echo "**Detector:** \`$(cat size_detector.txt)\`" >> "$R" - echo "**Gateway:** \`$(cat size_gateway.txt)\`" >> "$R" - echo "**Wired Detector:** \`$(cat size_wired_detector.txt)\`" >> "$R" - echo "" >> "$R" - echo "
" >> "$R" - - { - echo "body<> "$GITHUB_OUTPUT" + echo "| Role | Size |" >> "$R" + echo "|------|-----:|" >> "$R" + echo "| Detector V3 | $CUR_DET_V3 B |" >> "$R" + echo "| Detector V4 | $CUR_DET_V4 B |" >> "$R" + echo "| Gateway V3 | $CUR_GW_V3 B |" >> "$R" + echo "| Gateway V4 | $CUR_GW_V4 B |" >> "$R" + echo "| Wired Detector | $CUR_WD B |" >> "$R" + { echo "body<> "$GITHUB_OUTPUT" - name: Comment PR with firmware sizes uses: thollander/actions-comment-pull-request@v3 @@ -155,7 +117,6 @@ jobs: needs: build runs-on: ubuntu-latest if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' - steps: - name: Checkout code if: startsWith(github.ref, 'refs/tags/') @@ -168,11 +129,7 @@ jobs: id: tag_body run: | TAG_MSG=$(git tag -l --format='%(contents)' "$GITHUB_REF_NAME") - { - echo "message<> "$GITHUB_OUTPUT" + { echo "message<> "$GITHUB_OUTPUT" - name: Download firmware uses: actions/download-artifact@v4 @@ -186,10 +143,12 @@ jobs: tag_name: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || 'firmware-latest' }} name: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || 'Latest Firmware' }} generate_release_notes: ${{ startsWith(github.ref, 'refs/tags/') }} - body: ${{ startsWith(github.ref, 'refs/tags/') && steps.tag_body.outputs.message || 'Pre-built firmware binaries. Flash via the [Web Flasher](https://docs.batear.io/flasher/).' }} + body: ${{ startsWith(github.ref, 'refs/tags/') && steps.tag_body.outputs.message || 'Pre-built firmware binaries. Flash via the [Web Flasher](https://popshark1.github.io/batear/flasher/).' }} prerelease: ${{ !startsWith(github.ref, 'refs/tags/') }} make_latest: ${{ startsWith(github.ref, 'refs/tags/') }} files: | - merged_detector.bin - merged_gateway.bin + merged_detector_v3.bin + merged_detector_v4.bin + merged_gateway_v3.bin + merged_gateway_v4.bin merged_wired_detector.bin From 78fcf12bae6bcc4b19f103966f300b0ca9df9485 Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 02:44:01 +0300 Subject: [PATCH 12/27] ci: expand matrix to V3/V4 for detector and gateway (5 roles) --- .github/workflows/esp-idf-build.yml | 49 +++++++++++++++-------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/.github/workflows/esp-idf-build.yml b/.github/workflows/esp-idf-build.yml index fc72a5c..9ded67c 100644 --- a/.github/workflows/esp-idf-build.yml +++ b/.github/workflows/esp-idf-build.yml @@ -2,7 +2,8 @@ name: ESP-IDF Build CI on: push: -ci: add V3/V4 matrix builds for detector and gateway tags: [ "v*" ] + branches: [ "main" ] + tags: [ "v*" ] pull_request: branches: [ "main" ] workflow_dispatch: @@ -12,31 +13,28 @@ permissions: pull-requests: write jobs: - build: name: Build ${{ matrix.role }} runs-on: ubuntu-latest container: espressif/idf:release-v6.0 strategy: - fail-fast: false matrix: include: - role: detector_v3 - sdkconfig: sdkconfig.detector.v3 - flash_size: 8MB + sdkconfig_extra: sdkconfig.detector_v3 + flash_size: 16MB - role: detector_v4 - sdkconfig: sdkconfig.detector + sdkconfig_extra: sdkconfig.detector_v4 flash_size: 16MB - role: gateway_v3 - sdkconfig: sdkconfig.gateway.v3 + sdkconfig_extra: sdkconfig.gateway_v3 flash_size: 8MB - role: gateway_v4 - sdkconfig: sdkconfig.gateway - flash_size: 16MB + sdkconfig_extra: sdkconfig.gateway_v4 + flash_size: 8MB - role: wired_detector - sdkconfig: sdkconfig.wired_detector + sdkconfig_extra: sdkconfig.wired_detector flash_size: 16MB - steps: - name: Checkout code uses: actions/checkout@v5 @@ -48,8 +46,7 @@ jobs: run: | . $IDF_PATH/export.sh rm -rf build sdkconfig - env SDKCONFIG_DEFAULTS="sdkconfig.defaults;${{ matrix.sdkconfig }}" idf.py set-target esp32s3 - idf.py build + env SDKCONFIG_DEFAULTS="sdkconfig.defaults;${{ matrix.sdkconfig_extra }}" idf.py set-target esp32s3 idf.py build - name: Capture size run: | @@ -90,21 +87,25 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} run: | - CUR_DET_V3=$(stat -c %s merged_detector_v3.bin) - CUR_DET_V4=$(stat -c %s merged_detector_v4.bin) - CUR_GW_V3=$(stat -c %s merged_gateway_v3.bin) - CUR_GW_V4=$(stat -c %s merged_gateway_v4.bin) - CUR_WD=$(stat -c %s merged_wired_detector.bin) + REPO="${{ github.repository }}" R=/tmp/size_report.md + sign() { local v=$1; [ "$v" -gt 0 ] && echo "+${v}" || echo "${v}"; } + echo "## Firmware Size Report" > "$R" echo "" >> "$R" echo "| Role | Size |" >> "$R" echo "|------|-----:|" >> "$R" - echo "| Detector V3 | $CUR_DET_V3 B |" >> "$R" - echo "| Detector V4 | $CUR_DET_V4 B |" >> "$R" - echo "| Gateway V3 | $CUR_GW_V3 B |" >> "$R" - echo "| Gateway V4 | $CUR_GW_V4 B |" >> "$R" - echo "| Wired Detector | $CUR_WD B |" >> "$R" + for role in detector_v3 detector_v4 gateway_v3 gateway_v4 wired_detector; do + SZ=$(stat -c %s merged_${role}.bin 2>/dev/null || echo 0) + echo "| ${role} | ${SZ} B |" >> "$R" + done + echo "" >> "$R" + echo "idf.py size details" >> "$R" + echo "" >> "$R" + for role in detector_v3 detector_v4 gateway_v3 gateway_v4 wired_detector; do + echo "**${role}:** \`$(cat size_${role}.txt 2>/dev/null || echo n/a)\`" >> "$R" + done + echo "" >> "$R" { echo "body<> "$GITHUB_OUTPUT" - name: Comment PR with firmware sizes @@ -143,7 +144,7 @@ jobs: tag_name: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || 'firmware-latest' }} name: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || 'Latest Firmware' }} generate_release_notes: ${{ startsWith(github.ref, 'refs/tags/') }} - body: ${{ startsWith(github.ref, 'refs/tags/') && steps.tag_body.outputs.message || 'Pre-built firmware binaries. Flash via the [Web Flasher](https://popshark1.github.io/batear/flasher/).' }} + body: ${{ startsWith(github.ref, 'refs/tags/') && steps.tag_body.outputs.message || 'Pre-built firmware binaries. Flash via the [Web Flasher](https://docs.batear.io/flasher/).' }} prerelease: ${{ !startsWith(github.ref, 'refs/tags/') }} make_latest: ${{ startsWith(github.ref, 'refs/tags/') }} files: | From 5bf9da6ce6ecb225c8e14df83641b6951c907101 Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 02:45:21 +0300 Subject: [PATCH 13/27] Add sdkconfig for Heltec V3 detector board --- sdkconfig.detector_v3 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 sdkconfig.detector_v3 diff --git a/sdkconfig.detector_v3 b/sdkconfig.detector_v3 new file mode 100644 index 0000000..1eb6daa --- /dev/null +++ b/sdkconfig.detector_v3 @@ -0,0 +1,15 @@ +# Board (Heltec V3, 8MB flash) +CONFIG_BATEAR_BOARD_HELTEC_V3=y +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y + +# Role +CONFIG_BATEAR_ROLE_DETECTOR=y +CONFIG_BATEAR_DEVICE_ID=1 + +# Network (must match between all detectors and gateway) +CONFIG_BATEAR_NET_KEY="DEADBEEFCAFEBABE13374200F00DAA55" +CONFIG_BATEAR_LORA_FREQ=915000 +CONFIG_BATEAR_LORA_SYNC_WORD=0x12 + +# Telemetry heartbeat (jittered +-10% in firmware) +CONFIG_BATEAR_TELEMETRY_HEARTBEAT_MIN=30 From 95e97db229196093aad7d52394503054516c9f7d Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 02:46:04 +0300 Subject: [PATCH 14/27] Add sdkconfig for Heltec V4 detector board --- sdkconfig.detector_v4 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 sdkconfig.detector_v4 diff --git a/sdkconfig.detector_v4 b/sdkconfig.detector_v4 new file mode 100644 index 0000000..e95f007 --- /dev/null +++ b/sdkconfig.detector_v4 @@ -0,0 +1,15 @@ +# Board (Heltec V4, 16MB flash) +CONFIG_BATEAR_BOARD_HELTEC_V4=y +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y + +# Role +CONFIG_BATEAR_ROLE_DETECTOR=y +CONFIG_BATEAR_DEVICE_ID=1 + +# Network (must match between all detectors and gateway) +CONFIG_BATEAR_NET_KEY="DEADBEEFCAFEBABE13374200F00DAA55" +CONFIG_BATEAR_LORA_FREQ=915000 +CONFIG_BATEAR_LORA_SYNC_WORD=0x12 + +# Telemetry heartbeat (jittered +-10% in firmware) +CONFIG_BATEAR_TELEMETRY_HEARTBEAT_MIN=30 From f154c908cad03f8110ad87ee6d493c1fdd7658ce Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 02:46:36 +0300 Subject: [PATCH 15/27] Add initial configuration for Heltec V3 gateway --- sdkconfig.gateway_v3 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 sdkconfig.gateway_v3 diff --git a/sdkconfig.gateway_v3 b/sdkconfig.gateway_v3 new file mode 100644 index 0000000..15bd02f --- /dev/null +++ b/sdkconfig.gateway_v3 @@ -0,0 +1,14 @@ +# Board (Heltec V3, 8MB flash) +CONFIG_BATEAR_BOARD_HELTEC_V3=y +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y + +# Role +CONFIG_BATEAR_ROLE_GATEWAY=y + +# Network (must match between all detectors and gateway) +CONFIG_BATEAR_NET_KEY="DEADBEEFCAFEBABE13374200F00DAA55" +CONFIG_BATEAR_LORA_FREQ=915000 +CONFIG_BATEAR_LORA_SYNC_WORD=0x12 + +# Telemetry heartbeat (jittered +-10% in firmware) +CONFIG_BATEAR_TELEMETRY_HEARTBEAT_MIN=30 From 37e13d0a36436caa0e2db5f13221dba406925ebe Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 02:47:05 +0300 Subject: [PATCH 16/27] Add initial sdkconfig for Heltec V4 gateway --- sdkconfig.gateway_v4 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 sdkconfig.gateway_v4 diff --git a/sdkconfig.gateway_v4 b/sdkconfig.gateway_v4 new file mode 100644 index 0000000..44ce339 --- /dev/null +++ b/sdkconfig.gateway_v4 @@ -0,0 +1,14 @@ +# Board (Heltec V4, 16MB flash) +CONFIG_BATEAR_BOARD_HELTEC_V4=y +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y + +# Role +CONFIG_BATEAR_ROLE_GATEWAY=y + +# Network (must match between all detectors and gateway) +CONFIG_BATEAR_NET_KEY="DEADBEEFCAFEBABE13374200F00DAA55" +CONFIG_BATEAR_LORA_FREQ=915000 +CONFIG_BATEAR_LORA_SYNC_WORD=0x12 + +# Telemetry heartbeat (jittered +-10% in firmware) +CONFIG_BATEAR_TELEMETRY_HEARTBEAT_MIN=30 From e06efa8564c927840e8a744702295004d61cc843 Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 02:48:20 +0300 Subject: [PATCH 17/27] ci: fix flash sizes (V3=8MB, V4=16MB) in matrix --- .github/workflows/esp-idf-build.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/esp-idf-build.yml b/.github/workflows/esp-idf-build.yml index 9ded67c..6ff4ece 100644 --- a/.github/workflows/esp-idf-build.yml +++ b/.github/workflows/esp-idf-build.yml @@ -22,7 +22,7 @@ jobs: include: - role: detector_v3 sdkconfig_extra: sdkconfig.detector_v3 - flash_size: 16MB + flash_size: 8MB - role: detector_v4 sdkconfig_extra: sdkconfig.detector_v4 flash_size: 16MB @@ -31,7 +31,7 @@ jobs: flash_size: 8MB - role: gateway_v4 sdkconfig_extra: sdkconfig.gateway_v4 - flash_size: 8MB + flash_size: 16MB - role: wired_detector sdkconfig_extra: sdkconfig.wired_detector flash_size: 16MB @@ -87,10 +87,7 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} run: | - REPO="${{ github.repository }}" R=/tmp/size_report.md - sign() { local v=$1; [ "$v" -gt 0 ] && echo "+${v}" || echo "${v}"; } - echo "## Firmware Size Report" > "$R" echo "" >> "$R" echo "| Role | Size |" >> "$R" @@ -100,7 +97,7 @@ jobs: echo "| ${role} | ${SZ} B |" >> "$R" done echo "" >> "$R" - echo "idf.py size details" >> "$R" + echo "### idf.py size details" >> "$R" echo "" >> "$R" for role in detector_v3 detector_v4 gateway_v3 gateway_v4 wired_detector; do echo "**${role}:** \`$(cat size_${role}.txt 2>/dev/null || echo n/a)\`" >> "$R" From a474e4247a391fc4b0671922333d9041035a3949 Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 03:27:38 +0300 Subject: [PATCH 18/27] ci: fix env variable syntax for SDKCONFIG_DEFAULTS Move env variable to same line as idf.py command. The previous syntax had env on a separate line which didn't apply to idf.py. This prevented sdkconfig_{role} files from being loaded during build. --- .github/workflows/esp-idf-build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/esp-idf-build.yml b/.github/workflows/esp-idf-build.yml index 6ff4ece..84e20b3 100644 --- a/.github/workflows/esp-idf-build.yml +++ b/.github/workflows/esp-idf-build.yml @@ -46,7 +46,8 @@ jobs: run: | . $IDF_PATH/export.sh rm -rf build sdkconfig - env SDKCONFIG_DEFAULTS="sdkconfig.defaults;${{ matrix.sdkconfig_extra }}" idf.py set-target esp32s3 idf.py build + env SDKCONFIG_DEFAULTS="sdkconfig.defaults;${{ matrix.sdkconfig_extra }}" idf.py set-target esp32s3 + idf.py build - name: Capture size run: | From 1564b5d26c56168a5339dce8af6cc64b32d76ba9 Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 03:39:47 +0300 Subject: [PATCH 19/27] docs: update role specs to show V3/V4 support Update Detector and Gateway role specs to indicate support for both Heltec WiFi LoRa 32 V3 and V4 boards. This reflects the separate firmware builds now available for each board version. --- docs/flasher/index.html | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/flasher/index.html b/docs/flasher/index.html index 327c327..dbdbb35 100644 --- a/docs/flasher/index.html +++ b/docs/flasher/index.html @@ -221,8 +221,7 @@

Flash firmware in 60 seconds

-

Receives encrypted LoRa alerts from detectors, displays status on OLED, and forwards events to Home Assistant via MQTT over Wi-Fi.

-
+ Heltec WiFi LoRa 32 V3 / V4
LoRa RX (SX1262) SSD1306 OLED MQTT + HA Discovery @@ -230,8 +229,7 @@

Flash firmware in 60 seconds

-

Captures audio and publishes drone alerts directly over Ethernet to an MQTT broker. No gateway required — ideal for fixed installations with PoE.

-
+ Heltec WiFi LoRa 32 V3 / V4
I2S Mic + DSP W5500 Ethernet / PoE MQTT + HA Discovery From 10f127b9f3245a594ac1e798852d7fd18458114d Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 03:44:52 +0300 Subject: [PATCH 20/27] Update Heltec WiFi LoRa 32 version references Update Detector role specs to indicate support for both Heltec WiFi LoRa 32 V3 and V4 boards. This aligns the UI text with the separate firmware builds now available for each board version. --- docs/flasher/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/flasher/index.html b/docs/flasher/index.html index dbdbb35..1cd1ce5 100644 --- a/docs/flasher/index.html +++ b/docs/flasher/index.html @@ -217,7 +217,7 @@

Flash firmware in 60 seconds

I2S Mic + DSP LoRa TX (SX1262) AES-128-GCM - Heltec WiFi LoRa 32 V3 + Heltec WiFi LoRa 32 V3 / V4
@@ -225,7 +225,7 @@

Flash firmware in 60 seconds

LoRa RX (SX1262) SSD1306 OLED MQTT + HA Discovery - Heltec WiFi LoRa 32 V3 + Heltec WiFi LoRa 32 V3 / V4
From 08f1a92506d78529bdd0e8fccfbc1c0643a1420f Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 04:01:26 +0300 Subject: [PATCH 21/27] Add board version selection to firmware page Adds a Board Version selector (V3/V4) to the Web Flasher UI, allowing users to choose between Heltec WiFi LoRa 32 V3 (8MB) and V4 (16MB) before flashing detector or gateway firmware. - New boardSelect dropdown with V3/V4 options - Updated fwUrl() to append _v3 or _v4 suffix to firmware filenames for detector/gateway roles - Added event listener to trigger manifest update when board version changes - Wired detector remains unchanged (no version suffix needed) --- docs/flasher/index.html | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/flasher/index.html b/docs/flasher/index.html index 1cd1ce5..7044988 100644 --- a/docs/flasher/index.html +++ b/docs/flasher/index.html @@ -191,6 +191,15 @@

Flash firmware in 60 seconds

loading...
+ + +
+ + +
@@ -556,6 +565,7 @@

Wait & Done

/* ---- DOM refs ---- */ const select = document.getElementById("versionSelect"); + const boardSelect = document.getElementById("boardSelect"); const loading = document.getElementById("versionLoading"); const btnInstall = document.getElementById("btnInstall"); const installLabel = document.getElementById("installLabel"); @@ -628,8 +638,10 @@

Wait & Done

} function fwUrl(tag, role) { - return `${pageBase}firmware/${tag}/merged_${role}.bin`; - } + // Add board version suffix for detector/gateway roles + const boardVer = boardSelect?.value || 'v3'; + const roleSuffix = (role === 'detector' || role === 'gateway') ? `_${boardVer}` : ''; + return `${pageBase}firmware/${tag}/merged_${role}${roleSuffix}.bin`; } function getValidKey() { const raw = inputKey.value.replace(/[:\s-]/g, ""); @@ -836,6 +848,7 @@

Wait & Done

}); select.addEventListener("change", () => updateManifests()); + boardSelect.addEventListener("change", () => updateManifests()); /* ---- Initial setup ---- */ updateManifests(); From 019ca6e777060d8932bc425024a27e34ccae43a1 Mon Sep 17 00:00:00 2001 From: popshark1 Date: Wed, 3 Jun 2026 04:20:46 +0300 Subject: [PATCH 22/27] Update visibility of board version selector Hide board version selector for wired_detector role. --- docs/flasher/index.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/flasher/index.html b/docs/flasher/index.html index 7044988..ea8f85e 100644 --- a/docs/flasher/index.html +++ b/docs/flasher/index.html @@ -610,6 +610,11 @@

Wait & Done

cfgPanels.forEach(p => p.classList.toggle("active", p.dataset.role === role)); // Wired Detector does not transmit over LoRa — hide the AES key field. networkKeyPanel.style.display = (role === "wired_detector") ? "none" : ""; + // Hide board version selector for wired_detector (only uses one firmware) + const boardVersionPicker = boardSelect.closest('.version-picker'); + if (boardVersionPicker) { + boardVersionPicker.style.display = (role === "wired_detector") ? "none" : ""; + } installLabel.textContent = `Install ${ROLE_LABELS[role]}`; validateAndUpdate(); } From 14c309011e5f668424f903026f8af18fda206115 Mon Sep 17 00:00:00 2001 From: popshark1 Date: Tue, 9 Jun 2026 22:10:54 +0300 Subject: [PATCH 23/27] fix(battery): handle V3/V4 ADC_Ctrl polarity difference for VBAT read V3 uses active-LOW GPIO37 to connect the VBAT divider; V4 uses active-HIGH (per V4.2.0 datasheet). Introduce VBAT_CTRL_CONNECT / VBAT_CTRL_DISCONNECT macros selected at compile time via CONFIG_BATEAR_BOARD_HELTEC_V4, replacing the hardcoded 0/1 values that caused incorrect readings on V4 boards. --- main/battery.c | 96 +++++++++++++++++++++++--------------------------- 1 file changed, 45 insertions(+), 51 deletions(-) diff --git a/main/battery.c b/main/battery.c index 1062da4..3e81355 100644 --- a/main/battery.c +++ b/main/battery.c @@ -1,9 +1,10 @@ /* * battery.c — ADC-oneshot readback of a resistor-divided VBAT rail * - * Heltec V3/V4 wire VBAT through a ~4.9× divider into GPIO1 (ADC1_CH0) gated - * by GPIO37 (ADC_Ctrl, active low). We drive the gate low for each sample to - * avoid a constant ~100 µA drain through the divider between reads. + * Heltec V3/V4 wire VBAT through a ~4.9x divider into GPIO1 (ADC1_CH0) gated + * by GPIO37 (ADC_Ctrl). V3: active LOW (drive low to connect divider). + * V4: active HIGH (drive high to connect divider). + * We connect the divider only while sampling to avoid a constant ~100 uA drain. */ #include "battery.h" @@ -35,11 +36,27 @@ static const char *TAG = "battery"; #define VBAT_ADC_ATTEN ADC_ATTEN_DB_12 #define VBAT_ADC_BITWIDTH ADC_BITWIDTH_DEFAULT #define VBAT_SAMPLES 8 -#define VBAT_SETTLE_MS 2 -static adc_oneshot_unit_handle_t s_adc; -static adc_cali_handle_t s_cali; -static bool s_ready; +/* ADC_Ctrl (GPIO37) polarity differs between board revisions: + * V3 — active LOW: drive LOW to connect the divider, HIGH to disconnect. + * V4 — active HIGH: drive HIGH to connect the divider, LOW to disconnect. + */ +#if defined(CONFIG_BATEAR_BOARD_HELTEC_V4) +#define VBAT_CTRL_CONNECT 1 +#define VBAT_CTRL_DISCONNECT 0 +#else +#define VBAT_CTRL_CONNECT 0 +#define VBAT_CTRL_DISCONNECT 1 +#endif + +/* Settle time after enabling the divider before sampling (ms) */ +#ifndef VBAT_SETTLE_MS +#define VBAT_SETTLE_MS 5 +#endif + +static adc_oneshot_unit_handle_t s_adc = NULL; +static adc_cali_handle_t s_cali = NULL; +static bool s_ready = false; static bool cali_init(void) { @@ -47,25 +64,20 @@ static bool cali_init(void) adc_cali_curve_fitting_config_t cfg = { .unit_id = VBAT_ADC_UNIT, .chan = VBAT_ADC_CHANNEL, - .atten = VBAT_ADC_ATTEN, + .atten = VBAT_ADC_ATTEN, .bitwidth = VBAT_ADC_BITWIDTH, }; - if (adc_cali_create_scheme_curve_fitting(&cfg, &s_cali) == ESP_OK) { - return true; - } -#endif -#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED - adc_cali_line_fitting_config_t cfg2 = { + return adc_cali_create_scheme_curve_fitting(&cfg, &s_cali) == ESP_OK; +#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED + adc_cali_line_fitting_config_t cfg = { .unit_id = VBAT_ADC_UNIT, - .atten = VBAT_ADC_ATTEN, + .atten = VBAT_ADC_ATTEN, .bitwidth = VBAT_ADC_BITWIDTH, }; - if (adc_cali_create_scheme_line_fitting(&cfg2, &s_cali) == ESP_OK) { - return true; - } -#endif - s_cali = NULL; + return adc_cali_create_scheme_line_fitting(&cfg, &s_cali) == ESP_OK; +#else return false; +#endif } void battery_init(void) @@ -73,16 +85,15 @@ void battery_init(void) if (s_ready) { return; } - gpio_config_t ctrl_cfg = { - .pin_bit_mask = (1ULL << PIN_VBAT_CTRL), - .mode = GPIO_MODE_OUTPUT, - .pull_up_en = GPIO_PULLUP_DISABLE, - .pull_down_en = GPIO_PULLDOWN_DISABLE, - .intr_type = GPIO_INTR_DISABLE, + .pin_bit_mask = (1ULL << PIN_VBAT_CTRL), + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, }; gpio_config(&ctrl_cfg); - gpio_set_level((gpio_num_t)PIN_VBAT_CTRL, 1); /* divider disconnected */ + gpio_set_level((gpio_num_t)PIN_VBAT_CTRL, VBAT_CTRL_DISCONNECT); /* divider disconnected */ adc_oneshot_unit_init_cfg_t unit_cfg = { .unit_id = VBAT_ADC_UNIT, @@ -93,7 +104,6 @@ void battery_init(void) ESP_LOGE(TAG, "adc_oneshot_new_unit: %s", esp_err_to_name(err)); return; } - adc_oneshot_chan_cfg_t chan_cfg = { .atten = VBAT_ADC_ATTEN, .bitwidth = VBAT_ADC_BITWIDTH, @@ -103,17 +113,9 @@ void battery_init(void) ESP_LOGE(TAG, "adc_oneshot_config_channel: %s", esp_err_to_name(err)); return; } - - /* cppcheck cannot see ESP-IDF's ADC_CALI_SCHEME_*_SUPPORTED macros - * (defined via soc_caps.h from sdkconfig), so under its preprocessor - * view both #if branches in cali_init() compile out and it concludes - * the function always returns false. On real builds at least one - * scheme is available. */ - // cppcheck-suppress knownConditionTrueFalse if (!cali_init()) { ESP_LOGW(TAG, "ADC calibration unavailable — readings will be rough"); } - s_ready = true; ESP_LOGI(TAG, "battery monitor ready (adc=GPIO%d ctrl=GPIO%d ratio=%.2f)", PIN_VBAT_ADC, PIN_VBAT_CTRL, (double)BOARD_VBAT_DIVIDER_RATIO); @@ -124,10 +126,8 @@ uint32_t battery_read_mv(void) if (!s_ready) { return 0; } - - gpio_set_level((gpio_num_t)PIN_VBAT_CTRL, 0); + gpio_set_level((gpio_num_t)PIN_VBAT_CTRL, VBAT_CTRL_CONNECT); vTaskDelay(pdMS_TO_TICKS(VBAT_SETTLE_MS)); - uint32_t raw_sum = 0; int raw = 0; int taken = 0; @@ -137,34 +137,28 @@ uint32_t battery_read_mv(void) taken++; } } - - gpio_set_level((gpio_num_t)PIN_VBAT_CTRL, 1); - + gpio_set_level((gpio_num_t)PIN_VBAT_CTRL, VBAT_CTRL_DISCONNECT); if (taken == 0) { return 0; } - - int raw_avg = (int)(raw_sum / (uint32_t)taken); - + int raw_avg = (int)(raw_sum / (uint32_t)taken); int mv_at_pin = 0; if (s_cali) { if (adc_cali_raw_to_voltage(s_cali, raw_avg, &mv_at_pin) != ESP_OK) { mv_at_pin = 0; } } else { - /* Rough fallback: 12-bit ADC @ 12 dB atten → ~3100 mV full scale. */ mv_at_pin = (raw_avg * 3100) / 4095; } - float vbat_mv = (float)mv_at_pin * BOARD_VBAT_DIVIDER_RATIO; - if (vbat_mv < 0.0f) vbat_mv = 0.0f; + if (vbat_mv < 0.0f) vbat_mv = 0.0f; if (vbat_mv > 65535.0f) vbat_mv = 65535.0f; return (uint32_t)(vbat_mv + 0.5f); } -#else /* BOARD_HAS_VBAT */ +#else /* !BOARD_HAS_VBAT */ -void battery_init(void) {} +void battery_init(void) { } uint32_t battery_read_mv(void) { return 0; } -#endif +#endif /* BOARD_HAS_VBAT */ From 0c6106474219e9b92ce4e7564b8d36ffd66dfa8a Mon Sep 17 00:00:00 2001 From: tn666 Date: Tue, 16 Jun 2026 21:19:50 +0800 Subject: [PATCH 24/27] flasher: revert fork refs, restore role panels, drop duplicate sdkconfig Addresses review: - Revert REPO const + footer/datasets links from popshark1/* to batear-io/* - Restore the

descriptions and drop the stray floating on the gateway and wired_detector role-info panels - fwUrl(): fix indentation and use wired_detector-exclusion so future LoRa roles also get the board-version suffix - Drop unreferenced sdkconfig.detector.v3 / sdkconfig.gateway.v3 (CI uses the underscore _v3 variants; the dot variants were duplicates) Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/flasher/index.html | 25 ++++++++++++++----------- sdkconfig.detector.v3 | 15 --------------- sdkconfig.gateway.v3 | 14 -------------- 3 files changed, 14 insertions(+), 40 deletions(-) delete mode 100644 sdkconfig.detector.v3 delete mode 100644 sdkconfig.gateway.v3 diff --git a/docs/flasher/index.html b/docs/flasher/index.html index ea8f85e..30c7d6e 100644 --- a/docs/flasher/index.html +++ b/docs/flasher/index.html @@ -230,7 +230,8 @@

Flash firmware in 60 seconds

- Heltec WiFi LoRa 32 V3 / V4
+

Receives encrypted LoRa alerts from detectors, displays status on OLED, and forwards events to Home Assistant via MQTT over Wi-Fi.

+
LoRa RX (SX1262) SSD1306 OLED MQTT + HA Discovery @@ -238,7 +239,8 @@

Flash firmware in 60 seconds

- Heltec WiFi LoRa 32 V3 / V4
+

Captures audio and publishes drone alerts directly over Ethernet to an MQTT broker. No gateway required — ideal for fixed installations with PoE.

+
I2S Mic + DSP W5500 Ethernet / PoE MQTT + HA Discovery @@ -437,7 +439,7 @@

Wait & Done

Harmonic frequency analysis — real drone spectrograms available at
- popshark1/batear-datasets

+ batear-io/batear-datasets

@@ -528,9 +530,9 @@

Wait & Done

@@ -542,7 +544,7 @@

Wait & Done

document.getElementById("notSupported").style.display = "block"; } - const REPO = "popshark1/batear"; + const REPO = "batear-io/batear"; const NVS_OFFSET = 0x9000; /* ---- Role state ---- */ @@ -643,10 +645,11 @@

Wait & Done

} function fwUrl(tag, role) { - // Add board version suffix for detector/gateway roles - const boardVer = boardSelect?.value || 'v3'; - const roleSuffix = (role === 'detector' || role === 'gateway') ? `_${boardVer}` : ''; - return `${pageBase}firmware/${tag}/merged_${role}${roleSuffix}.bin`; } + // Wired Detector ships a single firmware; LoRa roles carry a board-version suffix. + const boardVer = boardSelect?.value || 'v3'; + const roleSuffix = (role === 'wired_detector') ? '' : `_${boardVer}`; + return `${pageBase}firmware/${tag}/merged_${role}${roleSuffix}.bin`; + } function getValidKey() { const raw = inputKey.value.replace(/[:\s-]/g, ""); diff --git a/sdkconfig.detector.v3 b/sdkconfig.detector.v3 deleted file mode 100644 index 64ebcfe..0000000 --- a/sdkconfig.detector.v3 +++ /dev/null @@ -1,15 +0,0 @@ -# Board (determines set-target chip and flash size) -CONFIG_BATEAR_BOARD_HELTEC_V3=y -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y - -# Role -CONFIG_BATEAR_ROLE_DETECTOR=y -CONFIG_BATEAR_DEVICE_ID=1 - -# Network (must match between all detectors and gateway) -CONFIG_BATEAR_NET_KEY="DEADBEEFCAFEBABE13374200F00DAA55" -CONFIG_BATEAR_LORA_FREQ=915000 -CONFIG_BATEAR_LORA_SYNC_WORD=0x12 - -# Telemetry heartbeat (jittered +-10% in firmware) -CONFIG_BATEAR_TELEMETRY_HEARTBEAT_MIN=30 diff --git a/sdkconfig.gateway.v3 b/sdkconfig.gateway.v3 deleted file mode 100644 index d672870..0000000 --- a/sdkconfig.gateway.v3 +++ /dev/null @@ -1,14 +0,0 @@ -# Board (determines set-target chip and flash size) -CONFIG_BATEAR_BOARD_HELTEC_V3=y -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y - -# Role -CONFIG_BATEAR_ROLE_GATEWAY=y - -# Network (must match between all detectors and gateway) -CONFIG_BATEAR_NET_KEY="DEADBEEFCAFEBABE13374200F00DAA55" -CONFIG_BATEAR_LORA_FREQ=915000 -CONFIG_BATEAR_LORA_SYNC_WORD=0x12 - -# Telemetry heartbeat (jittered +-10% in firmware) -CONFIG_BATEAR_TELEMETRY_HEARTBEAT_MIN=30 From c9691780a03ff0e9d610c492f65bc32760b20968 Mon Sep 17 00:00:00 2001 From: tn666 Date: Tue, 16 Jun 2026 21:20:22 +0800 Subject: [PATCH 25/27] ci: restore firmware-size delta vs firmware-latest; fix battery cppcheck - size-report: loop the current-vs-firmware-latest delta over all 5 roles instead of a flat size-only table; 'set -euo pipefail' + bare stat so a missing build artifact fails the job instead of silently reporting 0 B - Restore ENDOFBODY/ENDOFTAG heredoc markers (EOF can collide with content) - battery.c: restore the cppcheck-suppress for cali_init() dropped by the polarity commit (re-introduced a knownConditionTrueFalse warning) Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/esp-idf-build.yml | 44 ++++++++++++++++++++++------- main/battery.c | 6 ++++ 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/.github/workflows/esp-idf-build.yml b/.github/workflows/esp-idf-build.yml index 84e20b3..2c7dbfa 100644 --- a/.github/workflows/esp-idf-build.yml +++ b/.github/workflows/esp-idf-build.yml @@ -88,23 +88,43 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} run: | + set -euo pipefail + ROLES="detector_v3 detector_v4 gateway_v3 gateway_v4 wired_detector" + REPO="${{ github.repository }}" R=/tmp/size_report.md + sign() { local v=$1; [ "$v" -gt 0 ] && echo "+${v}" || echo "${v}"; } + echo "## Firmware Size Report" > "$R" echo "" >> "$R" - echo "| Role | Size |" >> "$R" - echo "|------|-----:|" >> "$R" - for role in detector_v3 detector_v4 gateway_v3 gateway_v4 wired_detector; do - SZ=$(stat -c %s merged_${role}.bin 2>/dev/null || echo 0) - echo "| ${role} | ${SZ} B |" >> "$R" + echo "| Role | Current | Previous | Delta |" >> "$R" + echo "|------|--------:|---------:|------:|" >> "$R" + for role in $ROLES; do + # A missing artifact means the build job dropped a binary — fail loudly. + CUR=$(stat -c %s "merged_${role}.bin") + if curl -sfL -H "Authorization: Bearer ${GITHUB_TOKEN}" \ + "https://github.com/${REPO}/releases/download/firmware-latest/merged_${role}.bin" \ + -o "/tmp/prev_${role}.bin"; then + PREV=$(stat -c %s "/tmp/prev_${role}.bin") + echo "| ${role} | ${CUR} B | ${PREV} B | $(sign $((CUR - PREV))) B |" >> "$R" + else + echo "| ${role} | ${CUR} B | — | new |" >> "$R" + fi done echo "" >> "$R" - echo "### idf.py size details" >> "$R" + echo "> Compared against [\`firmware-latest\`](https://github.com/${REPO}/releases/tag/firmware-latest)." >> "$R" + echo "" >> "$R" + echo "
idf.py size details" >> "$R" echo "" >> "$R" - for role in detector_v3 detector_v4 gateway_v3 gateway_v4 wired_detector; do - echo "**${role}:** \`$(cat size_${role}.txt 2>/dev/null || echo n/a)\`" >> "$R" + for role in $ROLES; do + echo "**${role}:** \`$(cat size_${role}.txt)\`" >> "$R" done echo "" >> "$R" - { echo "body<> "$GITHUB_OUTPUT" + echo "
" >> "$R" + { + echo "body<> "$GITHUB_OUTPUT" - name: Comment PR with firmware sizes uses: thollander/actions-comment-pull-request@v3 @@ -128,7 +148,11 @@ jobs: id: tag_body run: | TAG_MSG=$(git tag -l --format='%(contents)' "$GITHUB_REF_NAME") - { echo "message<> "$GITHUB_OUTPUT" + { + echo "message<> "$GITHUB_OUTPUT" - name: Download firmware uses: actions/download-artifact@v4 diff --git a/main/battery.c b/main/battery.c index 3e81355..f76df88 100644 --- a/main/battery.c +++ b/main/battery.c @@ -113,6 +113,12 @@ void battery_init(void) ESP_LOGE(TAG, "adc_oneshot_config_channel: %s", esp_err_to_name(err)); return; } + /* cppcheck cannot see ESP-IDF's ADC_CALI_SCHEME_*_SUPPORTED macros + * (defined via soc_caps.h from sdkconfig), so under its preprocessor + * view both #if branches in cali_init() compile out and it concludes + * the function always returns false. On real builds at least one + * scheme is available. */ + // cppcheck-suppress knownConditionTrueFalse if (!cali_init()) { ESP_LOGW(TAG, "ADC calibration unavailable — readings will be rough"); } From 4c6023ef642545a83888ac43f7f7120e14af7bd3 Mon Sep 17 00:00:00 2001 From: tn666 Date: Tue, 16 Jun 2026 21:30:36 +0800 Subject: [PATCH 26/27] ci: skip PR-size comment on fork PRs (read-only token) Fork PRs get a read-only GITHUB_TOKEN, so thollander/actions-comment-pull-request fails with 'Resource not accessible by integration' and reddens the whole check even though the size report generated fine. Gate the comment step on same-repo PRs and add continue-on-error; the table remains in the job log. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/esp-idf-build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/esp-idf-build.yml b/.github/workflows/esp-idf-build.yml index 2c7dbfa..b95a7ac 100644 --- a/.github/workflows/esp-idf-build.yml +++ b/.github/workflows/esp-idf-build.yml @@ -127,6 +127,12 @@ jobs: } >> "$GITHUB_OUTPUT" - name: Comment PR with firmware sizes + # Fork PRs run with a read-only GITHUB_TOKEN, so commenting is not + # permitted — skip there (the table is still in the job log) and only + # post for same-repo PRs. continue-on-error keeps a transient API + # failure from reddening the whole check. + if: github.event.pull_request.head.repo.full_name == github.repository + continue-on-error: true uses: thollander/actions-comment-pull-request@v3 with: message: ${{ steps.size_report.outputs.body }} From f16b06839eeefe0c19e9dfa9b1b17cc9235f2a4a Mon Sep 17 00:00:00 2001 From: tn666 Date: Tue, 16 Jun 2026 21:48:53 +0800 Subject: [PATCH 27/27] ci: comment firmware sizes on fork PRs via workflow_run MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The size-report job runs in the (fork) PR context where GITHUB_TOKEN is read-only, so it cannot comment. Instead it now uploads the rendered report + PR number as the 'size-report' artifact, and a new pr-size-comment.yml (triggered by workflow_run, running in the base-repo context with a write token) downloads it and upserts the comment — working for fork PRs too. workflow_run only uses the default-branch copy of the workflow, so this takes effect for fork PRs only once merged to main. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/esp-idf-build.yml | 25 ++++++-------- .github/workflows/pr-size-comment.yml | 49 +++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/pr-size-comment.yml diff --git a/.github/workflows/esp-idf-build.yml b/.github/workflows/esp-idf-build.yml index b95a7ac..2bd3773 100644 --- a/.github/workflows/esp-idf-build.yml +++ b/.github/workflows/esp-idf-build.yml @@ -120,22 +120,19 @@ jobs: done echo "" >> "$R" echo "" >> "$R" - { - echo "body<> "$GITHUB_OUTPUT" + # Hand the report and PR number off to the pr-size-comment workflow, + # which runs in the base-repo context and can comment on fork PRs + # (the fork-PR GITHUB_TOKEN here is read-only and cannot post). + cp "$R" size_report.md + echo "${{ github.event.pull_request.number }}" > pr_number.txt - - name: Comment PR with firmware sizes - # Fork PRs run with a read-only GITHUB_TOKEN, so commenting is not - # permitted — skip there (the table is still in the job log) and only - # post for same-repo PRs. continue-on-error keeps a transient API - # failure from reddening the whole check. - if: github.event.pull_request.head.repo.full_name == github.repository - continue-on-error: true - uses: thollander/actions-comment-pull-request@v3 + - name: Upload size report + uses: actions/upload-artifact@v4 with: - message: ${{ steps.size_report.outputs.body }} + name: size-report + path: | + size_report.md + pr_number.txt release: name: Publish Firmware diff --git a/.github/workflows/pr-size-comment.yml b/.github/workflows/pr-size-comment.yml new file mode 100644 index 0000000..cbd91e9 --- /dev/null +++ b/.github/workflows/pr-size-comment.yml @@ -0,0 +1,49 @@ +name: PR Size Comment + +# The ESP-IDF Build CI's size-report job runs in the (possibly fork) PR context, +# where GitHub forces GITHUB_TOKEN to read-only and commenting is impossible. +# This workflow is triggered by that run's completion; because workflow_run runs +# in the base-repo context it gets a write token and can comment on fork PRs. +# NOTE: workflow_run only ever uses the copy of this file on the default branch, +# so it takes effect for fork PRs only after it is merged to main. + +on: + workflow_run: + workflows: ["ESP-IDF Build CI"] + types: [completed] + +permissions: + pull-requests: write + +jobs: + comment: + name: Comment firmware sizes + runs-on: ubuntu-latest + if: > + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' + steps: + - name: Download size report + uses: actions/download-artifact@v4 + with: + name: size-report + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ github.token }} + + - name: Read PR number and report + id: report + run: | + set -euo pipefail + echo "number=$(cat pr_number.txt)" >> "$GITHUB_OUTPUT" + { + echo "body<> "$GITHUB_OUTPUT" + + - name: Comment PR with firmware sizes + uses: thollander/actions-comment-pull-request@v3 + with: + pr-number: ${{ steps.report.outputs.number }} + comment-tag: firmware-size + message: ${{ steps.report.outputs.body }}