mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-09 19:37:34 +00:00
Compare commits
130 Commits
XEdDSA
...
t-echo-plu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0601537fe1 | ||
|
|
d71305306c | ||
|
|
2ed82874b0 | ||
|
|
36a9adbe3a | ||
|
|
1e914140ca | ||
|
|
99e76483f5 | ||
|
|
e0f6ea2ed0 | ||
|
|
b5e952b008 | ||
|
|
11b5f1a4fe | ||
|
|
f9c9350f45 | ||
|
|
a5b2d4a9d5 | ||
|
|
8b0eb98f04 | ||
|
|
7fb95841e4 | ||
|
|
eaab8f04b5 | ||
|
|
9058ccecf9 | ||
|
|
1b83501ee2 | ||
|
|
ac571d5dd2 | ||
|
|
ef30fd850d | ||
|
|
b9a0015149 | ||
|
|
9673cfb0b2 | ||
|
|
757f7b68d6 | ||
|
|
5510dae8d3 | ||
|
|
52fd362720 | ||
|
|
33e1f58f6e | ||
|
|
9dc7ef612e | ||
|
|
b2c82bdc41 | ||
|
|
54a928f47f | ||
|
|
33f18659c8 | ||
|
|
3a7093a973 | ||
|
|
a4f6f4515a | ||
|
|
d609d05698 | ||
|
|
83c6161ac6 | ||
|
|
d93d68d31e | ||
|
|
1021d967da | ||
|
|
217abc4c10 | ||
|
|
e6af68bd14 | ||
|
|
530f0135ee | ||
|
|
208a873c4c | ||
|
|
f57eb6f27d | ||
|
|
155cdf9f9d | ||
|
|
661f49ad7a | ||
|
|
31e55d0b66 | ||
|
|
85aba3a4f7 | ||
|
|
5262233b2d | ||
|
|
40f1f91c0d | ||
|
|
269dee7a2d | ||
|
|
8e0547e76d | ||
|
|
8a48321555 | ||
|
|
aa8bb6c6f1 | ||
|
|
1952982896 | ||
|
|
de2b9632bb | ||
|
|
c2b7dc2641 | ||
|
|
b74238194b | ||
|
|
5d5819b876 | ||
|
|
f127702bef | ||
|
|
cce8cbfe34 | ||
|
|
a4a6c3509a | ||
|
|
68250dc937 | ||
|
|
c8628b3422 | ||
|
|
2ac74d6677 | ||
|
|
9d487ddc0d | ||
|
|
bcfe069997 | ||
|
|
4fc96bdf83 | ||
|
|
4ef943f204 | ||
|
|
a8fa5f25cb | ||
|
|
3b2a1547de | ||
|
|
6f725a1996 | ||
|
|
467c042bf7 | ||
|
|
cc4c41167c | ||
|
|
fff2bbf4a0 | ||
|
|
fba92229a6 | ||
|
|
ff0a4ea320 | ||
|
|
83b603827c | ||
|
|
2032ff1c32 | ||
|
|
5910cc2e26 | ||
|
|
ee80ec7b68 | ||
|
|
aa72e397f2 | ||
|
|
ec0dfb7337 | ||
|
|
c55bea8460 | ||
|
|
aa605fc4a2 | ||
|
|
d75680a2dd | ||
|
|
817f3b9ec8 | ||
|
|
decd58cd5c | ||
|
|
e691bd9732 | ||
|
|
6bad81f8dd | ||
|
|
69b9977fc1 | ||
|
|
0726bb4b56 | ||
|
|
6b11991be0 | ||
|
|
8e63dcf59a | ||
|
|
042543eb25 | ||
|
|
ae8d3fbb3d | ||
|
|
928739e0fb | ||
|
|
8be7915fc7 | ||
|
|
c052963395 | ||
|
|
65c418d4e1 | ||
|
|
c3a69a2742 | ||
|
|
66ff1536f3 | ||
|
|
5671e9d96f | ||
|
|
bd4bcb94f0 | ||
|
|
4b2f241478 | ||
|
|
eb087849c0 | ||
|
|
94aedff6ae | ||
|
|
2ae391197f | ||
|
|
2a17c3b5d4 | ||
|
|
8060134224 | ||
|
|
eeaafda62a | ||
|
|
6e9fd189b4 | ||
|
|
2f4eb25b2f | ||
|
|
aa85fbbcc4 | ||
|
|
3f40916223 | ||
|
|
1b4925bd07 | ||
|
|
0828c445fb | ||
|
|
61e41a8beb | ||
|
|
90584359e4 | ||
|
|
8a43741589 | ||
|
|
525c048354 | ||
|
|
41cbd77db3 | ||
|
|
f3e38a425f | ||
|
|
a11152e545 | ||
|
|
859ae4d3d2 | ||
|
|
03600b1252 | ||
|
|
a3d3e1c912 | ||
|
|
0e653056e7 | ||
|
|
eba6e4ed75 | ||
|
|
80e8745714 | ||
|
|
1abf8ddb30 | ||
|
|
5a595a3ae7 | ||
|
|
bcd4a1176a | ||
|
|
a59723030a | ||
|
|
a6cdf2c50b |
@@ -51,7 +51,7 @@ for f in .clusterfuzzlite/*_fuzzer.cpp; do
|
|||||||
fuzzer=$(basename "$f" .cpp)
|
fuzzer=$(basename "$f" .cpp)
|
||||||
cp -f "$f" src/fuzzer.cpp
|
cp -f "$f" src/fuzzer.cpp
|
||||||
pio run -vvv --environment "$PIO_ENV"
|
pio run -vvv --environment "$PIO_ENV"
|
||||||
program="$PLATFORMIO_WORKSPACE_DIR/build/$PIO_ENV/program"
|
program="$PLATFORMIO_WORKSPACE_DIR/build/$PIO_ENV/meshtasticd"
|
||||||
cp "$program" "$OUT/$fuzzer"
|
cp "$program" "$OUT/$fuzzer"
|
||||||
|
|
||||||
# Copy shared libraries used by the fuzzer.
|
# Copy shared libraries used by the fuzzer.
|
||||||
|
|||||||
1
.github/actionlint.yaml
vendored
1
.github/actionlint.yaml
vendored
@@ -2,4 +2,5 @@
|
|||||||
self-hosted-runner:
|
self-hosted-runner:
|
||||||
# Labels of self-hosted runner in array of strings.
|
# Labels of self-hosted runner in array of strings.
|
||||||
labels:
|
labels:
|
||||||
|
- arctastic
|
||||||
- test-runner
|
- test-runner
|
||||||
|
|||||||
6
.github/actions/build-variant/action.yml
vendored
6
.github/actions/build-variant/action.yml
vendored
@@ -76,7 +76,7 @@ runs:
|
|||||||
done
|
done
|
||||||
|
|
||||||
- name: PlatformIO ${{ inputs.arch }} download cache
|
- name: PlatformIO ${{ inputs.arch }} download cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio/.cache
|
path: ~/.platformio/.cache
|
||||||
key: pio-cache-${{ inputs.arch }}-${{ hashFiles('.github/actions/**', '**.ini') }}
|
key: pio-cache-${{ inputs.arch }}-${{ hashFiles('.github/actions/**', '**.ini') }}
|
||||||
@@ -100,9 +100,9 @@ runs:
|
|||||||
id: version
|
id: version
|
||||||
|
|
||||||
- name: Store binaries as an artifact
|
- name: Store binaries as an artifact
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: firmware-${{ inputs.arch }}-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
|
name: firmware-${{ inputs.arch }}-${{ inputs.board }}-${{ steps.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
path: |
|
path: |
|
||||||
${{ inputs.artifact-paths }}
|
${{ inputs.artifact-paths }}
|
||||||
|
|||||||
2
.github/workflows/build_debian_src.yml
vendored
2
.github/workflows/build_debian_src.yml
vendored
@@ -64,7 +64,7 @@ jobs:
|
|||||||
PKG_VERSION: ${{ steps.version.outputs.deb }}
|
PKG_VERSION: ${{ steps.version.outputs.deb }}
|
||||||
|
|
||||||
- name: Store binaries as an artifact
|
- name: Store binaries as an artifact
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
|
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|||||||
25
.github/workflows/build_firmware.yml
vendored
25
.github/workflows/build_firmware.yml
vendored
@@ -18,7 +18,8 @@ permissions: read-all
|
|||||||
jobs:
|
jobs:
|
||||||
pio-build:
|
pio-build:
|
||||||
name: build-${{ inputs.platform }}
|
name: build-${{ inputs.platform }}
|
||||||
runs-on: ubuntu-24.04
|
# Use 'arctastic' self-hosted runner pool when building in the main repo
|
||||||
|
runs-on: ${{ github.repository_owner == 'meshtastic' && 'arctastic' || 'ubuntu-latest' }}
|
||||||
outputs:
|
outputs:
|
||||||
artifact-id: ${{ steps.upload.outputs.artifact-id }}
|
artifact-id: ${{ steps.upload.outputs.artifact-id }}
|
||||||
steps:
|
steps:
|
||||||
@@ -55,15 +56,31 @@ jobs:
|
|||||||
ota_firmware_source: ${{ steps.ota_dir.outputs.src || '' }}
|
ota_firmware_source: ${{ steps.ota_dir.outputs.src || '' }}
|
||||||
ota_firmware_target: ${{ steps.ota_dir.outputs.tgt || '' }}
|
ota_firmware_target: ${{ steps.ota_dir.outputs.tgt || '' }}
|
||||||
|
|
||||||
|
- name: Job summary
|
||||||
|
env:
|
||||||
|
PIO_ENV: ${{ inputs.pio_env }}
|
||||||
|
run: |
|
||||||
|
echo "## $PIO_ENV" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "<details><summary><strong>Manifest</strong></summary>" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo '' >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo '```json' >> $GITHUB_STEP_SUMMARY
|
||||||
|
cat release/firmware-*.mt.json >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo '' >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "</details>" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
- name: Store binaries as an artifact
|
- name: Store binaries as an artifact
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
id: upload
|
id: upload
|
||||||
with:
|
with:
|
||||||
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}.zip
|
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
path: |
|
path: |
|
||||||
|
release/*.mt.json
|
||||||
release/*.bin
|
release/*.bin
|
||||||
release/*.elf
|
release/*.elf
|
||||||
release/*.uf2
|
release/*.uf2
|
||||||
release/*.hex
|
release/*.hex
|
||||||
release/*-ota.zip
|
release/*.zip
|
||||||
|
release/device-*.sh
|
||||||
|
release/device-*.bat
|
||||||
|
|||||||
14
.github/workflows/build_one_target.yml
vendored
14
.github/workflows/build_one_target.yml
vendored
@@ -98,7 +98,7 @@ jobs:
|
|||||||
ref: ${{github.event.pull_request.head.ref}}
|
ref: ${{github.event.pull_request.head.ref}}
|
||||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||||
|
|
||||||
- uses: actions/download-artifact@v6
|
- uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
path: ./
|
path: ./
|
||||||
pattern: firmware-*-*
|
pattern: firmware-*-*
|
||||||
@@ -111,7 +111,7 @@ jobs:
|
|||||||
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
|
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
|
||||||
|
|
||||||
- name: Repackage in single firmware zip
|
- name: Repackage in single firmware zip
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: firmware-${{inputs.target}}-${{ needs.version.outputs.long }}
|
name: firmware-${{inputs.target}}-${{ needs.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
@@ -119,7 +119,7 @@ jobs:
|
|||||||
./firmware-*.bin
|
./firmware-*.bin
|
||||||
./firmware-*.uf2
|
./firmware-*.uf2
|
||||||
./firmware-*.hex
|
./firmware-*.hex
|
||||||
./firmware-*-ota.zip
|
./firmware-*.zip
|
||||||
./device-*.sh
|
./device-*.sh
|
||||||
./device-*.bat
|
./device-*.bat
|
||||||
./littlefs-*.bin
|
./littlefs-*.bin
|
||||||
@@ -127,7 +127,7 @@ jobs:
|
|||||||
./Meshtastic_nRF52_factory_erase*.uf2
|
./Meshtastic_nRF52_factory_erase*.uf2
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
- uses: actions/download-artifact@v6
|
- uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: firmware-*-${{ needs.version.outputs.long }}
|
pattern: firmware-*-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -139,14 +139,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Device scripts permissions
|
- name: Device scripts permissions
|
||||||
run: |
|
run: |
|
||||||
chmod +x ./output/device-install.sh
|
chmod +x ./output/device-install.sh || true
|
||||||
chmod +x ./output/device-update.sh
|
chmod +x ./output/device-update.sh || true
|
||||||
|
|
||||||
- name: Zip firmware
|
- name: Zip firmware
|
||||||
run: zip -j -9 -r ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output
|
run: zip -j -9 -r ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
- name: Repackage in single elfs zip
|
- name: Repackage in single elfs zip
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip
|
name: debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|||||||
83
.github/workflows/main_matrix.yml
vendored
83
.github/workflows/main_matrix.yml
vendored
@@ -77,16 +77,21 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
check: ${{ fromJson(needs.setup.outputs.check) }}
|
check: ${{ fromJson(needs.setup.outputs.check) }}
|
||||||
|
# Use 'arctastic' self-hosted runner pool when checking in the main repo
|
||||||
runs-on: ubuntu-latest
|
runs-on: ${{ github.repository_owner == 'meshtastic' && 'arctastic' || 'ubuntu-latest' }}
|
||||||
if: ${{ github.event_name != 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}
|
if: ${{ github.event_name != 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
- name: Build base
|
with:
|
||||||
id: base
|
submodules: recursive
|
||||||
uses: ./.github/actions/setup-base
|
ref: ${{github.event.pull_request.head.ref}}
|
||||||
|
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||||
- name: Check ${{ matrix.check.board }}
|
- name: Check ${{ matrix.check.board }}
|
||||||
run: bin/check-all.sh ${{ matrix.check.board }}
|
uses: meshtastic/gh-action-firmware@main
|
||||||
|
with:
|
||||||
|
pio_platform: ${{ matrix.check.platform }}
|
||||||
|
pio_env: ${{ matrix.check.board }}
|
||||||
|
pio_target: check
|
||||||
|
|
||||||
build:
|
build:
|
||||||
needs: [setup, version]
|
needs: [setup, version]
|
||||||
@@ -168,7 +173,7 @@ jobs:
|
|||||||
ref: ${{github.event.pull_request.head.ref}}
|
ref: ${{github.event.pull_request.head.ref}}
|
||||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||||
|
|
||||||
- uses: actions/download-artifact@v6
|
- uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
path: ./
|
path: ./
|
||||||
pattern: firmware-${{matrix.arch}}-*
|
pattern: firmware-${{matrix.arch}}-*
|
||||||
@@ -177,19 +182,17 @@ jobs:
|
|||||||
- name: Display structure of downloaded files
|
- name: Display structure of downloaded files
|
||||||
run: ls -R
|
run: ls -R
|
||||||
|
|
||||||
- name: Move files up
|
|
||||||
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
|
|
||||||
|
|
||||||
- name: Repackage in single firmware zip
|
- name: Repackage in single firmware zip
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
path: |
|
path: |
|
||||||
|
./firmware-*.mt.json
|
||||||
./firmware-*.bin
|
./firmware-*.bin
|
||||||
./firmware-*.uf2
|
./firmware-*.uf2
|
||||||
./firmware-*.hex
|
./firmware-*.hex
|
||||||
./firmware-*-ota.zip
|
./firmware-*.zip
|
||||||
./device-*.sh
|
./device-*.sh
|
||||||
./device-*.bat
|
./device-*.bat
|
||||||
./littlefs-*.bin
|
./littlefs-*.bin
|
||||||
@@ -197,7 +200,7 @@ jobs:
|
|||||||
./Meshtastic_nRF52_factory_erase*.uf2
|
./Meshtastic_nRF52_factory_erase*.uf2
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
- uses: actions/download-artifact@v6
|
- uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -209,16 +212,16 @@ jobs:
|
|||||||
|
|
||||||
- name: Device scripts permissions
|
- name: Device scripts permissions
|
||||||
run: |
|
run: |
|
||||||
chmod +x ./output/device-install.sh
|
chmod +x ./output/device-install.sh || true
|
||||||
chmod +x ./output/device-update.sh
|
chmod +x ./output/device-update.sh || true
|
||||||
|
|
||||||
- name: Zip firmware
|
- name: Zip firmware
|
||||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
- name: Repackage in single elfs zip
|
- name: Repackage in single elfs zip
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
path: ./*.elf
|
path: ./*.elf
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
@@ -236,6 +239,7 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
needs:
|
needs:
|
||||||
|
- setup
|
||||||
- version
|
- version
|
||||||
- gather-artifacts
|
- gather-artifacts
|
||||||
- build-debian-src
|
- build-debian-src
|
||||||
@@ -244,11 +248,6 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup Python
|
|
||||||
uses: actions/setup-python@v6
|
|
||||||
with:
|
|
||||||
python-version: 3.x
|
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
id: create_release
|
id: create_release
|
||||||
@@ -261,14 +260,14 @@ jobs:
|
|||||||
Autogenerated by github action, developer should edit as required before publishing...
|
Autogenerated by github action, developer should edit as required before publishing...
|
||||||
|
|
||||||
- name: Download source deb
|
- name: Download source deb
|
||||||
uses: actions/download-artifact@v6
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src
|
pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
path: ./output/debian-src
|
path: ./output/debian-src
|
||||||
|
|
||||||
- name: Download `native-tft` pio deps
|
- name: Download `native-tft` pio deps
|
||||||
uses: actions/download-artifact@v6
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
|
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -284,10 +283,25 @@ jobs:
|
|||||||
- name: Display structure of downloaded files
|
- name: Display structure of downloaded files
|
||||||
run: ls -lR
|
run: ls -lR
|
||||||
|
|
||||||
- name: Add Linux sources to GtiHub Release
|
- name: Generate Release manifest
|
||||||
|
run: |
|
||||||
|
jq -n --arg ver "${{ needs.version.outputs.long }}" --argjson targets ${{ toJson(needs.setup.outputs.all) }} '{
|
||||||
|
"version": $ver,
|
||||||
|
"targets": $targets
|
||||||
|
}' > firmware-${{ needs.version.outputs.long }}.json
|
||||||
|
|
||||||
|
- name: Save Release manifest artifact
|
||||||
|
uses: actions/upload-artifact@v6
|
||||||
|
with:
|
||||||
|
name: manifest-${{ needs.version.outputs.long }}
|
||||||
|
overwrite: true
|
||||||
|
path: firmware-${{ needs.version.outputs.long }}.json
|
||||||
|
|
||||||
|
- name: Add sources to GitHub Release
|
||||||
# Only run when targeting master branch with workflow_dispatch
|
# Only run when targeting master branch with workflow_dispatch
|
||||||
if: ${{ github.ref_name == 'master' }}
|
if: ${{ github.ref_name == 'master' }}
|
||||||
run: |
|
run: |
|
||||||
|
gh release upload v${{ needs.version.outputs.long }} ./firmware-${{ needs.version.outputs.long }}.json
|
||||||
gh release upload v${{ needs.version.outputs.long }} ./output/meshtasticd-${{ needs.version.outputs.deb }}-src.zip
|
gh release upload v${{ needs.version.outputs.long }} ./output/meshtasticd-${{ needs.version.outputs.deb }}-src.zip
|
||||||
gh release upload v${{ needs.version.outputs.long }} ./output/platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip
|
gh release upload v${{ needs.version.outputs.long }} ./output/platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip
|
||||||
env:
|
env:
|
||||||
@@ -318,7 +332,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
|
|
||||||
- uses: actions/download-artifact@v6
|
- uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -329,15 +343,15 @@ jobs:
|
|||||||
|
|
||||||
- name: Device scripts permissions
|
- name: Device scripts permissions
|
||||||
run: |
|
run: |
|
||||||
chmod +x ./output/device-install.sh
|
chmod +x ./output/device-install.sh || true
|
||||||
chmod +x ./output/device-update.sh
|
chmod +x ./output/device-update.sh || true
|
||||||
|
|
||||||
- name: Zip firmware
|
- name: Zip firmware
|
||||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
- uses: actions/download-artifact@v6
|
- uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
path: ./elfs
|
path: ./elfs
|
||||||
|
|
||||||
@@ -373,12 +387,19 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
|
|
||||||
- uses: actions/download-artifact@v6
|
- name: Get firmware artifacts
|
||||||
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
|
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
path: ./publish
|
path: ./publish
|
||||||
|
|
||||||
|
- name: Get manifest artifact
|
||||||
|
uses: actions/download-artifact@v7
|
||||||
|
with:
|
||||||
|
pattern: manifest-${{ needs.version.outputs.long }}
|
||||||
|
path: ./publish
|
||||||
|
|
||||||
- name: Publish firmware to meshtastic.github.io
|
- name: Publish firmware to meshtastic.github.io
|
||||||
uses: peaceiris/actions-gh-pages@v4
|
uses: peaceiris/actions-gh-pages@v4
|
||||||
env:
|
env:
|
||||||
|
|||||||
37
.github/workflows/merge_queue.yml
vendored
37
.github/workflows/merge_queue.yml
vendored
@@ -147,7 +147,7 @@ jobs:
|
|||||||
ref: ${{github.event.pull_request.head.ref}}
|
ref: ${{github.event.pull_request.head.ref}}
|
||||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||||
|
|
||||||
- uses: actions/download-artifact@v6
|
- uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
path: ./
|
path: ./
|
||||||
pattern: firmware-${{matrix.arch}}-*
|
pattern: firmware-${{matrix.arch}}-*
|
||||||
@@ -160,7 +160,7 @@ jobs:
|
|||||||
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
|
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
|
||||||
|
|
||||||
- name: Repackage in single firmware zip
|
- name: Repackage in single firmware zip
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
@@ -168,7 +168,7 @@ jobs:
|
|||||||
./firmware-*.bin
|
./firmware-*.bin
|
||||||
./firmware-*.uf2
|
./firmware-*.uf2
|
||||||
./firmware-*.hex
|
./firmware-*.hex
|
||||||
./firmware-*-ota.zip
|
./firmware-*.zip
|
||||||
./device-*.sh
|
./device-*.sh
|
||||||
./device-*.bat
|
./device-*.bat
|
||||||
./littlefs-*.bin
|
./littlefs-*.bin
|
||||||
@@ -176,7 +176,7 @@ jobs:
|
|||||||
./Meshtastic_nRF52_factory_erase*.uf2
|
./Meshtastic_nRF52_factory_erase*.uf2
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
- uses: actions/download-artifact@v6
|
- uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -188,16 +188,16 @@ jobs:
|
|||||||
|
|
||||||
- name: Device scripts permissions
|
- name: Device scripts permissions
|
||||||
run: |
|
run: |
|
||||||
chmod +x ./output/device-install.sh
|
chmod +x ./output/device-install.sh || true
|
||||||
chmod +x ./output/device-update.sh
|
chmod +x ./output/device-update.sh || true
|
||||||
|
|
||||||
- name: Zip firmware
|
- name: Zip firmware
|
||||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
- name: Repackage in single elfs zip
|
- name: Repackage in single elfs zip
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
path: ./*.elf
|
path: ./*.elf
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
@@ -223,11 +223,6 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup Python
|
|
||||||
uses: actions/setup-python@v6
|
|
||||||
with:
|
|
||||||
python-version: 3.x
|
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
id: create_release
|
id: create_release
|
||||||
@@ -240,14 +235,14 @@ jobs:
|
|||||||
Autogenerated by github action, developer should edit as required before publishing...
|
Autogenerated by github action, developer should edit as required before publishing...
|
||||||
|
|
||||||
- name: Download source deb
|
- name: Download source deb
|
||||||
uses: actions/download-artifact@v6
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src
|
pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
path: ./output/debian-src
|
path: ./output/debian-src
|
||||||
|
|
||||||
- name: Download `native-tft` pio deps
|
- name: Download `native-tft` pio deps
|
||||||
uses: actions/download-artifact@v6
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
|
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -297,7 +292,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
|
|
||||||
- uses: actions/download-artifact@v6
|
- uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -308,15 +303,15 @@ jobs:
|
|||||||
|
|
||||||
- name: Device scripts permissions
|
- name: Device scripts permissions
|
||||||
run: |
|
run: |
|
||||||
chmod +x ./output/device-install.sh
|
chmod +x ./output/device-install.sh || true
|
||||||
chmod +x ./output/device-update.sh
|
chmod +x ./output/device-update.sh || true
|
||||||
|
|
||||||
- name: Zip firmware
|
- name: Zip firmware
|
||||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||||
|
|
||||||
- uses: actions/download-artifact@v6
|
- uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
path: ./elfs
|
path: ./elfs
|
||||||
|
|
||||||
@@ -352,7 +347,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
|
|
||||||
- uses: actions/download-artifact@v6
|
- uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
|
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|||||||
2
.github/workflows/package_obs.yml
vendored
2
.github/workflows/package_obs.yml
vendored
@@ -58,7 +58,7 @@ jobs:
|
|||||||
id: version
|
id: version
|
||||||
|
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v6
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
|
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|||||||
2
.github/workflows/package_pio_deps.yml
vendored
2
.github/workflows/package_pio_deps.yml
vendored
@@ -56,7 +56,7 @@ jobs:
|
|||||||
PLATFORMIO_CORE_DIR: pio/core
|
PLATFORMIO_CORE_DIR: pio/core
|
||||||
|
|
||||||
- name: Store binaries as an artifact
|
- name: Store binaries as an artifact
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: platformio-deps-${{ inputs.pio_env }}-${{ steps.version.outputs.long }}
|
name: platformio-deps-${{ inputs.pio_env }}-${{ steps.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|||||||
2
.github/workflows/package_ppa.yml
vendored
2
.github/workflows/package_ppa.yml
vendored
@@ -60,7 +60,7 @@ jobs:
|
|||||||
id: version
|
id: version
|
||||||
|
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v6
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
|
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|||||||
2
.github/workflows/pr_enforce_labels.yml
vendored
2
.github/workflows/pr_enforce_labels.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const labels = context.payload.pull_request.labels.map(label => label.name);
|
const labels = context.payload.pull_request.labels.map(label => label.name);
|
||||||
const requiredLabels = ['bugfix', 'enhancement', 'hardware-support', 'dependencies', 'submodules', 'github_actions', 'trunk'];
|
const requiredLabels = ['bugfix', 'enhancement', 'hardware-support', 'dependencies', 'submodules', 'github_actions', 'trunk', 'cleanup'];
|
||||||
const hasRequiredLabel = labels.some(label => requiredLabels.includes(label));
|
const hasRequiredLabel = labels.some(label => requiredLabels.includes(label));
|
||||||
if (!hasRequiredLabel) {
|
if (!hasRequiredLabel) {
|
||||||
core.setFailed(`PR must have at least one of the following labels before it can be merged: ${requiredLabels.join(', ')}.`);
|
core.setFailed(`PR must have at least one of the following labels before it can be merged: ${requiredLabels.join(', ')}.`);
|
||||||
|
|||||||
4
.github/workflows/pr_tests.yml
vendored
4
.github/workflows/pr_tests.yml
vendored
@@ -50,9 +50,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Download test artifacts
|
- name: Download test artifacts
|
||||||
if: needs.native-tests.result != 'skipped'
|
if: needs.native-tests.result != 'skipped'
|
||||||
uses: actions/download-artifact@v6
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
|
name: platformio-test-report-${{ steps.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Parse test results and create detailed summary
|
- name: Parse test results and create detailed summary
|
||||||
|
|||||||
2
.github/workflows/release_channels.yml
vendored
2
.github/workflows/release_channels.yml
vendored
@@ -102,7 +102,7 @@ jobs:
|
|||||||
PIP_DISABLE_PIP_VERSION_CHECK: 1
|
PIP_DISABLE_PIP_VERSION_CHECK: 1
|
||||||
|
|
||||||
- name: Create Bumps pull request
|
- name: Create Bumps pull request
|
||||||
uses: peter-evans/create-pull-request@v7
|
uses: peter-evans/create-pull-request@v8
|
||||||
with:
|
with:
|
||||||
base: ${{ github.event.repository.default_branch }}
|
base: ${{ github.event.repository.default_branch }}
|
||||||
branch: create-pull-request/bump-version
|
branch: create-pull-request/bump-version
|
||||||
|
|||||||
2
.github/workflows/sec_sast_semgrep_cron.yml
vendored
2
.github/workflows/sec_sast_semgrep_cron.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
|||||||
|
|
||||||
# step 3
|
# step 3
|
||||||
- name: save report as pipeline artifact
|
- name: save report as pipeline artifact
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: report.sarif
|
name: report.sarif
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|||||||
2
.github/workflows/stale_bot.yml
vendored
2
.github/workflows/stale_bot.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Stale PR+Issues
|
- name: Stale PR+Issues
|
||||||
uses: actions/stale@v10.1.0
|
uses: actions/stale@v10.1.1
|
||||||
with:
|
with:
|
||||||
days-before-stale: 45
|
days-before-stale: 45
|
||||||
stale-issue-message: This issue has not had any comment or update in the last month. If it is still relevant, please post update comments. If no comments are made, this issue will be closed automagically in 7 days.
|
stale-issue-message: This issue has not had any comment or update in the last month. If it is still relevant, please post update comments. If no comments are made, this issue will be closed automagically in 7 days.
|
||||||
|
|||||||
28
.github/workflows/test_native.yml
vendored
28
.github/workflows/test_native.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Integration test
|
- name: Integration test
|
||||||
run: |
|
run: |
|
||||||
.pio/build/coverage/program -s &
|
.pio/build/coverage/meshtasticd -s &
|
||||||
PID=$!
|
PID=$!
|
||||||
timeout 20 bash -c "until ls -al /proc/$PID/fd | grep socket; do sleep 1; done"
|
timeout 20 bash -c "until ls -al /proc/$PID/fd | grep socket; do sleep 1; done"
|
||||||
echo "Simulator started, launching python test..."
|
echo "Simulator started, launching python test..."
|
||||||
@@ -59,10 +59,10 @@ jobs:
|
|||||||
id: version
|
id: version
|
||||||
|
|
||||||
- name: Save coverage information
|
- name: Save coverage information
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
if: always() # run this step even if previous step failed
|
if: always() # run this step even if previous step failed
|
||||||
with:
|
with:
|
||||||
name: lcov-coverage-info-native-simulator-test-${{ steps.version.outputs.long }}.zip
|
name: lcov-coverage-info-native-simulator-test-${{ steps.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
path: ./coverage_*.info
|
path: ./coverage_*.info
|
||||||
|
|
||||||
@@ -94,9 +94,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Save test results
|
- name: Save test results
|
||||||
if: always() # run this step even if previous step failed
|
if: always() # run this step even if previous step failed
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
|
name: platformio-test-report-${{ steps.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
path: ./testreport.xml
|
path: ./testreport.xml
|
||||||
|
|
||||||
@@ -108,10 +108,10 @@ jobs:
|
|||||||
sed -i -e "s#${PWD}#.#" coverage_tests.info # Make paths relative.
|
sed -i -e "s#${PWD}#.#" coverage_tests.info # Make paths relative.
|
||||||
|
|
||||||
- name: Save coverage information
|
- name: Save coverage information
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
if: always() # run this step even if previous step failed
|
if: always() # run this step even if previous step failed
|
||||||
with:
|
with:
|
||||||
name: lcov-coverage-info-native-platformio-tests-${{ steps.version.outputs.long }}.zip
|
name: lcov-coverage-info-native-platformio-tests-${{ steps.version.outputs.long }}
|
||||||
overwrite: true
|
overwrite: true
|
||||||
path: ./coverage_*.info
|
path: ./coverage_*.info
|
||||||
|
|
||||||
@@ -137,22 +137,22 @@ jobs:
|
|||||||
id: version
|
id: version
|
||||||
|
|
||||||
- name: Download test artifacts
|
- name: Download test artifacts
|
||||||
uses: actions/download-artifact@v6
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
|
name: platformio-test-report-${{ steps.version.outputs.long }}
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Test Report
|
- name: Test Report
|
||||||
uses: dorny/test-reporter@v2.2.0
|
uses: dorny/test-reporter@v2.4.0
|
||||||
with:
|
with:
|
||||||
name: PlatformIO Tests
|
name: PlatformIO Tests
|
||||||
path: testreport.xml
|
path: testreport.xml
|
||||||
reporter: java-junit
|
reporter: java-junit
|
||||||
|
|
||||||
- name: Download coverage artifacts
|
- name: Download coverage artifacts
|
||||||
uses: actions/download-artifact@v6
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }}.zip
|
pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }}
|
||||||
path: code-coverage-report
|
path: code-coverage-report
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ jobs:
|
|||||||
genhtml --quiet --legend --prefix "${PWD}" code-coverage-report/coverage_src.info --output-directory code-coverage-report
|
genhtml --quiet --legend --prefix "${PWD}" code-coverage-report/coverage_src.info --output-directory code-coverage-report
|
||||||
|
|
||||||
- name: Save Code Coverage Report
|
- name: Save Code Coverage Report
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: code-coverage-report-${{ steps.version.outputs.long }}.zip
|
name: code-coverage-report-${{ steps.version.outputs.long }}
|
||||||
path: code-coverage-report
|
path: code-coverage-report
|
||||||
|
|||||||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
# - uses: actions/setup-python@v5
|
# - uses: actions/setup-python@v6
|
||||||
# with:
|
# with:
|
||||||
# python-version: '3.10'
|
# python-version: '3.10'
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/update_protobufs.yml
vendored
2
.github/workflows/update_protobufs.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
|||||||
./bin/regen-protos.sh
|
./bin/regen-protos.sh
|
||||||
|
|
||||||
- name: Create pull request
|
- name: Create pull request
|
||||||
uses: peter-evans/create-pull-request@v7
|
uses: peter-evans/create-pull-request@v8
|
||||||
with:
|
with:
|
||||||
branch: create-pull-request/update-protobufs
|
branch: create-pull-request/update-protobufs
|
||||||
labels: submodules
|
labels: submodules
|
||||||
|
|||||||
@@ -8,25 +8,25 @@ plugins:
|
|||||||
uri: https://github.com/trunk-io/plugins
|
uri: https://github.com/trunk-io/plugins
|
||||||
lint:
|
lint:
|
||||||
enabled:
|
enabled:
|
||||||
- checkov@3.2.495
|
- checkov@3.2.497
|
||||||
- renovate@42.24.1
|
- renovate@42.69.2
|
||||||
- prettier@3.6.2
|
- prettier@3.7.4
|
||||||
- trufflehog@3.91.1
|
- trufflehog@3.92.4
|
||||||
- yamllint@1.37.1
|
- yamllint@1.37.1
|
||||||
- bandit@1.9.2
|
- bandit@1.9.2
|
||||||
- trivy@0.67.2
|
- trivy@0.68.2
|
||||||
- taplo@0.10.0
|
- taplo@0.10.0
|
||||||
- ruff@0.14.6
|
- ruff@0.14.10
|
||||||
- isort@7.0.0
|
- isort@7.0.0
|
||||||
- markdownlint@0.46.0
|
- markdownlint@0.47.0
|
||||||
- oxipng@9.1.5
|
- oxipng@10.0.0
|
||||||
- svgo@4.0.0
|
- svgo@4.0.0
|
||||||
- actionlint@1.7.9
|
- actionlint@1.7.10
|
||||||
- flake8@7.3.0
|
- flake8@7.3.0
|
||||||
- hadolint@2.14.0
|
- hadolint@2.14.0
|
||||||
- shfmt@3.6.0
|
- shfmt@3.6.0
|
||||||
- shellcheck@0.11.0
|
- shellcheck@0.11.0
|
||||||
- black@25.11.0
|
- black@25.12.0
|
||||||
- git-diff-check
|
- git-diff-check
|
||||||
- gitleaks@8.30.0
|
- gitleaks@8.30.0
|
||||||
- clang-format@16.0.3
|
- clang-format@16.0.3
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ RUN bash ./bin/build-native.sh "$PIO_ENV" && \
|
|||||||
|
|
||||||
# ##### PRODUCTION BUILD #############
|
# ##### PRODUCTION BUILD #############
|
||||||
|
|
||||||
FROM alpine:3.22
|
FROM alpine:3.23
|
||||||
LABEL org.opencontainers.image.title="Meshtastic" \
|
LABEL org.opencontainers.image.title="Meshtastic" \
|
||||||
org.opencontainers.image.description="Alpine Meshtastic daemon" \
|
org.opencontainers.image.description="Alpine Meshtastic daemon" \
|
||||||
org.opencontainers.image.url="https://meshtastic.org" \
|
org.opencontainers.image.url="https://meshtastic.org" \
|
||||||
|
|||||||
165
bin/analyze_map.py
Normal file
165
bin/analyze_map.py
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Summarise linker map output to highlight heavy object files and libraries.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python bin/analyze_map.py --map .pio/build/rak4631/output.map --top 20
|
||||||
|
|
||||||
|
The script parses GNU ld map files and aggregates section sizes per object file
|
||||||
|
and per archive/library, then prints sortable tables that make it easy to spot
|
||||||
|
modules worth trimming or hiding behind feature flags.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import collections
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from typing import DefaultDict, Dict, Tuple
|
||||||
|
|
||||||
|
|
||||||
|
SECTION_LINE_RE = re.compile(r"^\s+(?P<section>\S+)\s+0x[0-9A-Fa-f]+\s+0x(?P<size>[0-9A-Fa-f]+)\s+(?P<object>.+)$")
|
||||||
|
ARCHIVE_MEMBER_RE = re.compile(r"^(?P<archive>.+)\((?P<object>[^)]+)\)$")
|
||||||
|
|
||||||
|
|
||||||
|
def human_size(num_bytes: int) -> str:
|
||||||
|
"""Return a friendly size string with one decimal place."""
|
||||||
|
if num_bytes < 1024:
|
||||||
|
return f"{num_bytes:,} B"
|
||||||
|
num = float(num_bytes)
|
||||||
|
for unit in ("KB", "MB", "GB"):
|
||||||
|
num /= 1024.0
|
||||||
|
if num < 1024.0:
|
||||||
|
return f"{num:.1f} {unit}"
|
||||||
|
return f"{num:.1f} TB"
|
||||||
|
|
||||||
|
|
||||||
|
def shorten_path(path: str, root: str) -> str:
|
||||||
|
"""Prefer repository-relative paths for readability."""
|
||||||
|
path = path.strip()
|
||||||
|
if not path:
|
||||||
|
return path
|
||||||
|
|
||||||
|
# Normalise Windows archives (backslashes) to POSIX style for consistency.
|
||||||
|
path = path.replace("\\", "/")
|
||||||
|
|
||||||
|
# Attempt to strip the root when an absolute path lives inside the repo.
|
||||||
|
if os.path.isabs(path):
|
||||||
|
try:
|
||||||
|
rel = os.path.relpath(path, root)
|
||||||
|
if not rel.startswith(".."):
|
||||||
|
return rel
|
||||||
|
except ValueError:
|
||||||
|
# relpath can fail on mixed drives on Windows; fall back to basename.
|
||||||
|
pass
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def describe_object(raw_object: str, root: str) -> Tuple[str, str]:
|
||||||
|
"""Return a human friendly object label and the library it belongs to."""
|
||||||
|
raw_object = raw_object.strip()
|
||||||
|
lib_label = "[app]"
|
||||||
|
match = ARCHIVE_MEMBER_RE.match(raw_object)
|
||||||
|
if match:
|
||||||
|
archive = shorten_path(match.group("archive"), root)
|
||||||
|
obj = match.group("object")
|
||||||
|
lib_label = os.path.basename(archive) or archive
|
||||||
|
label = f"{archive}:{obj}"
|
||||||
|
else:
|
||||||
|
label = shorten_path(raw_object, root)
|
||||||
|
# If the object lives under libs, hint at the containing directory.
|
||||||
|
parent = os.path.basename(os.path.dirname(label))
|
||||||
|
if parent:
|
||||||
|
lib_label = parent
|
||||||
|
return label, lib_label
|
||||||
|
|
||||||
|
|
||||||
|
def parse_map(map_path: str, repo_root: str) -> Tuple[Dict[str, int], Dict[str, int], Dict[str, Dict[str, int]]]:
|
||||||
|
per_object: DefaultDict[str, int] = collections.defaultdict(int)
|
||||||
|
per_library: DefaultDict[str, int] = collections.defaultdict(int)
|
||||||
|
per_object_sections: DefaultDict[str, DefaultDict[str, int]] = collections.defaultdict(lambda: collections.defaultdict(int))
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(map_path, "r", encoding="utf-8", errors="ignore") as handle:
|
||||||
|
for line in handle:
|
||||||
|
match = SECTION_LINE_RE.match(line)
|
||||||
|
if not match:
|
||||||
|
continue
|
||||||
|
|
||||||
|
section = match.group("section")
|
||||||
|
if section.startswith("*") or section in {"LOAD", "ORIGIN"}:
|
||||||
|
continue
|
||||||
|
|
||||||
|
size = int(match.group("size"), 16)
|
||||||
|
if size == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
obj_token = match.group("object").strip()
|
||||||
|
if not obj_token or obj_token.startswith("*") or "load address" in obj_token:
|
||||||
|
continue
|
||||||
|
|
||||||
|
label, lib_label = describe_object(obj_token, repo_root)
|
||||||
|
per_object[label] += size
|
||||||
|
per_library[lib_label] += size
|
||||||
|
per_object_sections[label][section] += size
|
||||||
|
except FileNotFoundError:
|
||||||
|
raise SystemExit(f"error: map file '{map_path}' not found. Run a build first.")
|
||||||
|
|
||||||
|
return per_object, per_library, per_object_sections
|
||||||
|
|
||||||
|
|
||||||
|
def format_section_breakdown(section_sizes: Dict[str, int], total: int, limit: int = 3) -> str:
|
||||||
|
items = sorted(section_sizes.items(), key=lambda kv: kv[1], reverse=True)
|
||||||
|
parts = []
|
||||||
|
for section, size in items[:limit]:
|
||||||
|
pct = (size / total) * 100 if total else 0
|
||||||
|
parts.append(f"{section} {pct:.1f}%")
|
||||||
|
if len(items) > limit:
|
||||||
|
remainder = total - sum(size for _, size in items[:limit])
|
||||||
|
pct = (remainder / total) * 100 if total else 0
|
||||||
|
parts.append(f"other {pct:.1f}%")
|
||||||
|
return ", ".join(parts)
|
||||||
|
|
||||||
|
|
||||||
|
def print_report(map_path: str, top_n: int, per_object: Dict[str, int], per_library: Dict[str, int], per_object_sections: Dict[str, Dict[str, int]]):
|
||||||
|
total_bytes = sum(per_object.values())
|
||||||
|
if total_bytes == 0:
|
||||||
|
print("No section data found in map file.")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"Map file: {map_path}")
|
||||||
|
print(f"Accounted size: {human_size(total_bytes)} across {len(per_object)} object files\n")
|
||||||
|
|
||||||
|
sorted_objects = sorted(per_object.items(), key=lambda kv: kv[1], reverse=True)
|
||||||
|
print(f"Top {min(top_n, len(sorted_objects))} object files by linked size:")
|
||||||
|
for idx, (obj, size) in enumerate(sorted_objects[:top_n], 1):
|
||||||
|
pct = (size / total_bytes) * 100
|
||||||
|
breakdown = format_section_breakdown(per_object_sections[obj], size)
|
||||||
|
print(f"{idx:2}. {human_size(size):>9} ({size:,} B, {pct:5.2f}% of linked size)")
|
||||||
|
print(f" {obj}")
|
||||||
|
if breakdown:
|
||||||
|
print(f" sections: {breakdown}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
sorted_libs = sorted(per_library.items(), key=lambda kv: kv[1], reverse=True)
|
||||||
|
print(f"Top {min(top_n, len(sorted_libs))} libraries or source roots:")
|
||||||
|
for idx, (lib, size) in enumerate(sorted_libs[:top_n], 1):
|
||||||
|
pct = (size / total_bytes) * 100
|
||||||
|
print(f"{idx:2}. {human_size(size):>9} ({size:,} B, {pct:5.2f}% of linked size) {lib}")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser(description="Highlight heavy object files from a GNU ld map file.")
|
||||||
|
parser.add_argument("--map", default=".pio/build/rak4631/output.map", help="Path to the map file (default: %(default)s)")
|
||||||
|
parser.add_argument("--top", type=int, default=20, help="Number of entries to display per table (default: %(default)s)")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
map_path = os.path.abspath(args.map)
|
||||||
|
repo_root = os.path.abspath(os.getcwd())
|
||||||
|
|
||||||
|
per_object, per_library, per_object_sections = parse_map(map_path, repo_root)
|
||||||
|
print_report(os.path.relpath(map_path, repo_root), args.top, per_object, per_library, per_object_sections)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -5,7 +5,8 @@ set -e
|
|||||||
VERSION=`bin/buildinfo.py long`
|
VERSION=`bin/buildinfo.py long`
|
||||||
SHORT_VERSION=`bin/buildinfo.py short`
|
SHORT_VERSION=`bin/buildinfo.py short`
|
||||||
|
|
||||||
OUTDIR=release/
|
BUILDDIR=.pio/build/$1
|
||||||
|
OUTDIR=release
|
||||||
|
|
||||||
rm -f $OUTDIR/firmware*
|
rm -f $OUTDIR/firmware*
|
||||||
rm -r $OUTDIR/* || true
|
rm -r $OUTDIR/* || true
|
||||||
@@ -14,33 +15,27 @@ rm -r $OUTDIR/* || true
|
|||||||
platformio pkg install -e $1
|
platformio pkg install -e $1
|
||||||
|
|
||||||
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
|
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
|
||||||
rm -f .pio/build/$1/firmware.*
|
rm -f $BUILDDIR/firmware*
|
||||||
|
|
||||||
# The shell vars the build tool expects to find
|
# The shell vars the build tool expects to find
|
||||||
export APP_VERSION=$VERSION
|
export APP_VERSION=$VERSION
|
||||||
|
|
||||||
basename=firmware-$1-$VERSION
|
basename=firmware-$1-$VERSION
|
||||||
|
|
||||||
pio run --environment $1 # -v
|
pio run --environment $1 -t mtjson # -v
|
||||||
SRCELF=.pio/build/$1/firmware.elf
|
|
||||||
cp $SRCELF $OUTDIR/$basename.elf
|
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
|
||||||
|
|
||||||
echo "Copying ESP32 bin file"
|
echo "Copying ESP32 bin file"
|
||||||
SRCBIN=.pio/build/$1/firmware.factory.bin
|
cp $BUILDDIR/$basename.factory.bin $OUTDIR/$basename.factory.bin
|
||||||
cp $SRCBIN $OUTDIR/$basename.bin
|
|
||||||
|
|
||||||
echo "Copying ESP32 update bin file"
|
echo "Copying ESP32 update bin file"
|
||||||
SRCBIN=.pio/build/$1/firmware.bin
|
cp $BUILDDIR/$basename.bin $OUTDIR/$basename.bin
|
||||||
cp $SRCBIN $OUTDIR/$basename-update.bin
|
|
||||||
|
|
||||||
echo "Building Filesystem for ESP32 targets"
|
echo "Copying Filesystem for ESP32 targets"
|
||||||
# If you want to build the webui, uncomment the following lines
|
cp $BUILDDIR/littlefs-$1-$VERSION.bin $OUTDIR/littlefs-$1-$VERSION.bin
|
||||||
# pio run --environment $1 -t buildfs
|
cp bin/device-install.* $OUTDIR/
|
||||||
# cp .pio/build/$1/littlefs.bin $OUTDIR/littlefswebui-$1-$VERSION.bin
|
cp bin/device-update.* $OUTDIR/
|
||||||
# # Remove webserver files from the filesystem and rebuild
|
|
||||||
# ls -l data/static # Diagnostic list of files
|
echo "Copying manifest"
|
||||||
# rm -rf data/static
|
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json
|
||||||
pio run --environment $1 -t buildfs
|
|
||||||
cp .pio/build/$1/littlefs.bin $OUTDIR/littlefs-$1-$VERSION.bin
|
|
||||||
cp bin/device-install.* $OUTDIR
|
|
||||||
cp bin/device-update.* $OUTDIR
|
|
||||||
|
|||||||
@@ -17,15 +17,19 @@ VERSION=$(bin/buildinfo.py long)
|
|||||||
SHORT_VERSION=$(bin/buildinfo.py short)
|
SHORT_VERSION=$(bin/buildinfo.py short)
|
||||||
PIO_ENV=${1:-native}
|
PIO_ENV=${1:-native}
|
||||||
|
|
||||||
OUTDIR=release/
|
BUILDDIR=.pio/build/$PIO_ENV
|
||||||
|
OUTDIR=release
|
||||||
|
|
||||||
rm -f $OUTDIR/firmware*
|
rm -f $OUTDIR/meshtasticd*
|
||||||
|
|
||||||
mkdir -p $OUTDIR/
|
mkdir -p $OUTDIR/
|
||||||
rm -r $OUTDIR/* || true
|
rm -r $OUTDIR/* || true
|
||||||
|
|
||||||
|
basename=meshtasticd-$1-$VERSION
|
||||||
|
|
||||||
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
|
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
|
||||||
pio pkg install --environment "$PIO_ENV" || platformioFailed
|
pio pkg install --environment "$PIO_ENV" || platformioFailed
|
||||||
pio run --environment "$PIO_ENV" || platformioFailed
|
pio run --environment "$PIO_ENV" || platformioFailed
|
||||||
cp ".pio/build/$PIO_ENV/program" "$OUTDIR/meshtasticd_linux_$(uname -m)"
|
|
||||||
cp bin/native-install.* $OUTDIR
|
cp "$BUILDDIR/meshtasticd" "$OUTDIR/meshtasticd_linux_$(uname -m)"
|
||||||
|
cp bin/native-install.* $OUTDIR/
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ set -e
|
|||||||
VERSION=$(bin/buildinfo.py long)
|
VERSION=$(bin/buildinfo.py long)
|
||||||
SHORT_VERSION=$(bin/buildinfo.py short)
|
SHORT_VERSION=$(bin/buildinfo.py short)
|
||||||
|
|
||||||
OUTDIR=release/
|
BUILDDIR=.pio/build/$1
|
||||||
|
OUTDIR=release
|
||||||
|
|
||||||
rm -f $OUTDIR/firmware*
|
rm -f $OUTDIR/firmware*
|
||||||
rm -r $OUTDIR/* || true
|
rm -r $OUTDIR/* || true
|
||||||
@@ -14,40 +15,38 @@ rm -r $OUTDIR/* || true
|
|||||||
platformio pkg install -e $1
|
platformio pkg install -e $1
|
||||||
|
|
||||||
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
|
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
|
||||||
rm -f .pio/build/$1/firmware.*
|
rm -f $BUILDDIR/firmware*
|
||||||
|
|
||||||
# The shell vars the build tool expects to find
|
# The shell vars the build tool expects to find
|
||||||
export APP_VERSION=$VERSION
|
export APP_VERSION=$VERSION
|
||||||
|
|
||||||
basename=firmware-$1-$VERSION
|
basename=firmware-$1-$VERSION
|
||||||
|
ota_basename=${basename}-ota
|
||||||
|
|
||||||
pio run --environment $1 # -v
|
pio run --environment $1 -t mtjson # -v
|
||||||
SRCELF=.pio/build/$1/firmware.elf
|
|
||||||
cp $SRCELF $OUTDIR/$basename.elf
|
|
||||||
|
|
||||||
echo "Generating NRF52 dfu file"
|
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
|
||||||
DFUPKG=.pio/build/$1/firmware.zip
|
|
||||||
cp $DFUPKG $OUTDIR/$basename-ota.zip
|
|
||||||
|
|
||||||
echo "Generating NRF52 uf2 file"
|
echo "Copying NRF52 dfu (OTA) file"
|
||||||
SRCHEX=.pio/build/$1/firmware.hex
|
cp $BUILDDIR/$basename.zip $OUTDIR/$ota_basename.zip
|
||||||
|
|
||||||
# if WM1110 target, merge hex with softdevice 7.3.0
|
echo "Copying NRF52 UF2 file"
|
||||||
|
cp $BUILDDIR/$basename.uf2 $OUTDIR/$basename.uf2
|
||||||
|
cp bin/*.uf2 $OUTDIR/
|
||||||
|
|
||||||
|
SRCHEX=$BUILDDIR/$basename.hex
|
||||||
|
|
||||||
|
# if WM1110 target, copy the merged.hex
|
||||||
if (echo $1 | grep -q "wio-sdk-wm1110"); then
|
if (echo $1 | grep -q "wio-sdk-wm1110"); then
|
||||||
echo "Merging with softdevice"
|
echo "Copying .merged.hex file"
|
||||||
bin/mergehex -m bin/s140_nrf52_7.3.0_softdevice.hex $SRCHEX -o .pio/build/$1/$basename.hex
|
SRCHEX=$BUILDDIR/$basename.merged.hex
|
||||||
SRCHEX=.pio/build/$1/$basename.hex
|
cp $SRCHEX $OUTDIR/
|
||||||
bin/uf2conv.py $SRCHEX -c -o $OUTDIR/$basename.uf2 -f 0xADA52840
|
|
||||||
cp $SRCHEX $OUTDIR
|
|
||||||
cp bin/*.uf2 $OUTDIR
|
|
||||||
else
|
|
||||||
bin/uf2conv.py $SRCHEX -c -o $OUTDIR/$basename.uf2 -f 0xADA52840
|
|
||||||
cp bin/device-install.* $OUTDIR
|
|
||||||
cp bin/device-update.* $OUTDIR
|
|
||||||
cp bin/*.uf2 $OUTDIR
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if (echo $1 | grep -q "rak4631"); then
|
if (echo $1 | grep -q "rak4631"); then
|
||||||
echo "Copying hex file"
|
echo "Copying .hex file"
|
||||||
cp .pio/build/$1/firmware.hex $OUTDIR/$basename.hex
|
cp $SRCHEX $OUTDIR/
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "Copying manifest"
|
||||||
|
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ set -e
|
|||||||
VERSION=`bin/buildinfo.py long`
|
VERSION=`bin/buildinfo.py long`
|
||||||
SHORT_VERSION=`bin/buildinfo.py short`
|
SHORT_VERSION=`bin/buildinfo.py short`
|
||||||
|
|
||||||
OUTDIR=release/
|
BUILDDIR=.pio/build/$1
|
||||||
|
OUTDIR=release
|
||||||
|
|
||||||
rm -f $OUTDIR/firmware*
|
rm -f $OUTDIR/firmware*
|
||||||
rm -r $OUTDIR/* || true
|
rm -r $OUTDIR/* || true
|
||||||
@@ -14,20 +15,19 @@ rm -r $OUTDIR/* || true
|
|||||||
platformio pkg install -e $1
|
platformio pkg install -e $1
|
||||||
|
|
||||||
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
|
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
|
||||||
rm -f .pio/build/$1/firmware.*
|
rm -f $BUILDDIR/firmware*
|
||||||
|
|
||||||
# The shell vars the build tool expects to find
|
# The shell vars the build tool expects to find
|
||||||
export APP_VERSION=$VERSION
|
export APP_VERSION=$VERSION
|
||||||
|
|
||||||
basename=firmware-$1-$VERSION
|
basename=firmware-$1-$VERSION
|
||||||
|
|
||||||
pio run --environment $1 # -v
|
pio run --environment $1 -t mtjson # -v
|
||||||
SRCELF=.pio/build/$1/firmware.elf
|
|
||||||
cp $SRCELF $OUTDIR/$basename.elf
|
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
|
||||||
|
|
||||||
echo "Copying uf2 file"
|
echo "Copying uf2 file"
|
||||||
SRCBIN=.pio/build/$1/firmware.uf2
|
cp $BUILDDIR/$basename.uf2 $OUTDIR/$basename.uf2
|
||||||
cp $SRCBIN $OUTDIR/$basename.uf2
|
|
||||||
|
|
||||||
cp bin/device-install.* $OUTDIR
|
echo "Copying manifest"
|
||||||
cp bin/device-update.* $OUTDIR
|
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ set -e
|
|||||||
VERSION=$(bin/buildinfo.py long)
|
VERSION=$(bin/buildinfo.py long)
|
||||||
SHORT_VERSION=$(bin/buildinfo.py short)
|
SHORT_VERSION=$(bin/buildinfo.py short)
|
||||||
|
|
||||||
OUTDIR=release/
|
BUILDDIR=.pio/build/$1
|
||||||
|
OUTDIR=release
|
||||||
|
|
||||||
rm -f $OUTDIR/firmware*
|
rm -f $OUTDIR/firmware*
|
||||||
rm -r $OUTDIR/* || true
|
rm -r $OUTDIR/* || true
|
||||||
@@ -14,16 +15,19 @@ rm -r $OUTDIR/* || true
|
|||||||
platformio pkg install -e $1
|
platformio pkg install -e $1
|
||||||
|
|
||||||
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
|
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
|
||||||
rm -f .pio/build/$1/firmware.*
|
rm -f $BUILDDIR/firmware*
|
||||||
|
|
||||||
# The shell vars the build tool expects to find
|
# The shell vars the build tool expects to find
|
||||||
export APP_VERSION=$VERSION
|
export APP_VERSION=$VERSION
|
||||||
|
|
||||||
basename=firmware-$1-$VERSION
|
basename=firmware-$1-$VERSION
|
||||||
|
|
||||||
pio run --environment $1 # -v
|
pio run --environment $1 -t mtjson # -v
|
||||||
SRCELF=.pio/build/$1/firmware.elf
|
|
||||||
cp $SRCELF $OUTDIR/$basename.elf
|
|
||||||
|
|
||||||
SRCBIN=.pio/build/$1/firmware.bin
|
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
|
||||||
cp $SRCBIN $OUTDIR/$basename.bin
|
|
||||||
|
echo "Copying STM32 bin file"
|
||||||
|
cp $BUILDDIR/$basename.bin $OUTDIR/$basename.bin
|
||||||
|
|
||||||
|
echo "Copying manifest"
|
||||||
|
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json
|
||||||
|
|||||||
@@ -184,6 +184,8 @@ Input:
|
|||||||
Logging:
|
Logging:
|
||||||
LogLevel: info # debug, info, warn, error
|
LogLevel: info # debug, info, warn, error
|
||||||
# TraceFile: /var/log/meshtasticd.json
|
# TraceFile: /var/log/meshtasticd.json
|
||||||
|
# JSONFile: /packets.json # File location for JSON output of decoded packets
|
||||||
|
# JSONFilter: position # filter for packets to save to JSON file
|
||||||
# AsciiLogs: true # default if not specified is !isatty() on stdout
|
# AsciiLogs: true # default if not specified is !isatty() on stdout
|
||||||
|
|
||||||
Webserver:
|
Webserver:
|
||||||
|
|||||||
@@ -5,22 +5,14 @@ TITLE Meshtastic device-install
|
|||||||
SET "SCRIPT_NAME=%~nx0"
|
SET "SCRIPT_NAME=%~nx0"
|
||||||
SET "DEBUG=0"
|
SET "DEBUG=0"
|
||||||
SET "PYTHON="
|
SET "PYTHON="
|
||||||
SET "TFT_BUILD=0"
|
|
||||||
SET "BIGDB8=0"
|
|
||||||
SET "MUIDB8=0"
|
|
||||||
SET "BIGDB16=0"
|
|
||||||
SET "ESPTOOL_BAUD=115200"
|
SET "ESPTOOL_BAUD=115200"
|
||||||
SET "ESPTOOL_CMD="
|
SET "ESPTOOL_CMD="
|
||||||
SET "LOGCOUNTER=0"
|
SET "LOGCOUNTER=0"
|
||||||
SET "BPS_RESET=0"
|
SET "BPS_RESET=0"
|
||||||
|
@REM Default offsets.
|
||||||
@REM FIXME: Determine mcu from PlatformIO variant, this is unmaintainable.
|
@REM https://github.com/meshtastic/web-flasher/blob/main/stores/firmwareStore.ts#L202
|
||||||
SET "S3=s3 v3 t-deck wireless-paper wireless-tracker station-g2 unphone t-eth-elite tlora-pager mesh-tab dreamcatcher ESP32-S3-Pico seeed-sensecap-indicator heltec_capsule_sensor_v3 vision-master icarus tracksenger elecrow-adv heltec-v4"
|
SET "OTA_OFFSET=0x260000"
|
||||||
SET "C3=esp32c3"
|
SET "SPIFFS_OFFSET=0x300000"
|
||||||
@REM FIXME: Determine flash size from PlatformIO variant, this is unmaintainable.
|
|
||||||
SET "BIGDB_8MB=crowpanel-esp32s3 heltec_capsule_sensor_v3 heltec-v3 heltec-vision-master-e213 heltec-vision-master-e290 heltec-vision-master-t190 heltec-wireless-paper heltec-wireless-tracker heltec-wsl-v3 icarus seeed-xiao-s3 tbeam-s3-core tracksenger"
|
|
||||||
SET "MUIDB_8MB=picomputer-s3 unphone seeed-sensecap-indicator"
|
|
||||||
SET "BIGDB_16MB=t-deck mesh-tab t-energy-s3 dreamcatcher ESP32-S3-Pico m5stack-cores3 station-g2 t-eth-elite tlora-pager t-watch-s3 elecrow-adv heltec-v4"
|
|
||||||
|
|
||||||
GOTO getopts
|
GOTO getopts
|
||||||
:help
|
:help
|
||||||
@@ -29,7 +21,7 @@ ECHO.
|
|||||||
ECHO Usage: %SCRIPT_NAME% -f filename [-p PORT] [-P python] [--1200bps-reset]
|
ECHO Usage: %SCRIPT_NAME% -f filename [-p PORT] [-P python] [--1200bps-reset]
|
||||||
ECHO.
|
ECHO.
|
||||||
ECHO Options:
|
ECHO Options:
|
||||||
ECHO -f filename The firmware .bin file to flash. Custom to your device type and region. (required)
|
ECHO -f filename The firmware .factory.bin file to flash. Custom to your device type and region. (required)
|
||||||
ECHO The file must be located in this current directory.
|
ECHO The file must be located in this current directory.
|
||||||
ECHO -p PORT Set the environment variable for ESPTOOL_PORT.
|
ECHO -p PORT Set the environment variable for ESPTOOL_PORT.
|
||||||
ECHO If not set, ESPTOOL iterates all ports (Dangerous).
|
ECHO If not set, ESPTOOL iterates all ports (Dangerous).
|
||||||
@@ -40,12 +32,12 @@ ECHO --1200bps-reset Attempt to place the device in correct mode. (1200bps
|
|||||||
ECHO Some hardware requires this twice.
|
ECHO Some hardware requires this twice.
|
||||||
ECHO.
|
ECHO.
|
||||||
ECHO Example: %SCRIPT_NAME% -p COM17 --1200bps-reset
|
ECHO Example: %SCRIPT_NAME% -p COM17 --1200bps-reset
|
||||||
ECHO Example: %SCRIPT_NAME% -f firmware-t-deck-tft-2.6.0.0b106d4.bin -p COM11
|
ECHO Example: %SCRIPT_NAME% -f firmware-t-deck-tft-2.6.0.0b106d4.factory.bin -p COM11
|
||||||
ECHO Example: %SCRIPT_NAME% -f firmware-unphone-2.6.0.0b106d4.bin -p COM11
|
ECHO Example: %SCRIPT_NAME% -f firmware-unphone-2.6.0.0b106d4.factory.bin -p COM11
|
||||||
GOTO eof
|
GOTO eof
|
||||||
|
|
||||||
:version
|
:version
|
||||||
ECHO %SCRIPT_NAME% [Version 2.6.2]
|
ECHO %SCRIPT_NAME% [Version 2.7.0]
|
||||||
ECHO Meshtastic
|
ECHO Meshtastic
|
||||||
GOTO eof
|
GOTO eof
|
||||||
|
|
||||||
@@ -78,8 +70,8 @@ IF "__!FILENAME!__"=="____" (
|
|||||||
CALL :LOG_MESSAGE ERROR "Filename containing spaces are not supported."
|
CALL :LOG_MESSAGE ERROR "Filename containing spaces are not supported."
|
||||||
GOTO help
|
GOTO help
|
||||||
)
|
)
|
||||||
IF "__!FILENAME:firmware-=!__"=="__!FILENAME!__" (
|
IF NOT "__!FILENAME:.factory.bin=!__"=="__!FILENAME!__" (
|
||||||
CALL :LOG_MESSAGE ERROR "Filename must be a firmware-* file."
|
CALL :LOG_MESSAGE ERROR "Filename must be a firmware-*.factory.bin file."
|
||||||
GOTO help
|
GOTO help
|
||||||
)
|
)
|
||||||
@REM Remove ".\" or "./" file prefix if present.
|
@REM Remove ".\" or "./" file prefix if present.
|
||||||
@@ -93,12 +85,26 @@ IF NOT EXIST !FILENAME! (
|
|||||||
GOTO eof
|
GOTO eof
|
||||||
)
|
)
|
||||||
|
|
||||||
IF NOT "!FILENAME:update=!"=="!FILENAME!" (
|
CALL :LOG_MESSAGE DEBUG "Checking for metadata..."
|
||||||
CALL :LOG_MESSAGE DEBUG "We are working with a *update* file. !FILENAME!"
|
@REM Derive metadata filename from firmware filename.
|
||||||
CALL :LOG_MESSAGE INFO "Use script device-update.bat to flash update !FILENAME!."
|
SET "METAFILE=!FILENAME:.factory.bin=!.mt.json"
|
||||||
GOTO eof
|
IF EXIST !METAFILE! (
|
||||||
|
@REM Print parsed json with powershell
|
||||||
|
CALL :LOG_MESSAGE INFO "Firmware metadata: !METAFILE!"
|
||||||
|
powershell -NoProfile -Command "(Get-Content '!METAFILE!' | ConvertFrom-Json | Out-String).Trim()"
|
||||||
|
|
||||||
|
@REM Save metadata values to variables for later use.
|
||||||
|
FOR /f "usebackq" %%A IN (`powershell -NoProfile -Command ^
|
||||||
|
"(Get-Content '!METAFILE!' | ConvertFrom-Json).mcu"`) DO SET "MCU=%%A"
|
||||||
|
FOR /f "usebackq" %%A IN (`powershell -NoProfile -Command ^
|
||||||
|
"(Get-Content '!METAFILE!' | ConvertFrom-Json).part | Where-Object { $_.subtype -eq 'ota_1' } | Select-Object -ExpandProperty offset"`
|
||||||
|
) DO SET "OTA_OFFSET=%%A"
|
||||||
|
FOR /f "usebackq" %%A IN (`powershell -NoProfile -Command ^
|
||||||
|
"(Get-Content '!METAFILE!' | ConvertFrom-Json).part | Where-Object { $_.subtype -eq 'spiffs' } | Select-Object -ExpandProperty offset"`
|
||||||
|
) DO SET "SPIFFS_OFFSET=%%A"
|
||||||
) ELSE (
|
) ELSE (
|
||||||
CALL :LOG_MESSAGE DEBUG "We are NOT working with a *update* file. !FILENAME!"
|
CALL :LOG_MESSAGE ERROR "No metadata file found: !METAFILE!"
|
||||||
|
GOTO eof
|
||||||
)
|
)
|
||||||
|
|
||||||
:skip-filename
|
:skip-filename
|
||||||
@@ -108,7 +114,7 @@ IF NOT "__%PYTHON%__"=="____" (
|
|||||||
SET "ESPTOOL_CMD=!PYTHON! -m esptool"
|
SET "ESPTOOL_CMD=!PYTHON! -m esptool"
|
||||||
CALL :LOG_MESSAGE DEBUG "Python interpreter supplied."
|
CALL :LOG_MESSAGE DEBUG "Python interpreter supplied."
|
||||||
) ELSE (
|
) ELSE (
|
||||||
CALL :LOG_MESSAGE DEBUG "Python interpreter NOT supplied. Looking for esptool...
|
CALL :LOG_MESSAGE DEBUG "Python interpreter NOT supplied. Looking for esptool..."
|
||||||
WHERE esptool >nul 2>&1
|
WHERE esptool >nul 2>&1
|
||||||
IF %ERRORLEVEL% EQU 0 (
|
IF %ERRORLEVEL% EQU 0 (
|
||||||
@REM WHERE exits with code 0 if esptool is found.
|
@REM WHERE exits with code 0 if esptool is found.
|
||||||
@@ -146,100 +152,26 @@ IF %BPS_RESET% EQU 1 (
|
|||||||
GOTO eof
|
GOTO eof
|
||||||
)
|
)
|
||||||
|
|
||||||
@REM Check if FILENAME contains "-tft-" and set target partitionScheme accordingly.
|
@REM Extract PROGNAME from %FILENAME% for later use.
|
||||||
@REM https://github.com/meshtastic/web-flasher/blob/main/types/resources.ts#L3
|
SET "PROGNAME=!FILENAME:.factory.bin=!"
|
||||||
IF NOT "!FILENAME:-tft-=!"=="!FILENAME!" (
|
CALL :LOG_MESSAGE DEBUG "Computed PROGNAME: !PROGNAME!"
|
||||||
CALL :LOG_MESSAGE DEBUG "We are working with a *-tft-* file. !FILENAME!"
|
|
||||||
SET "TFT_BUILD=1"
|
IF "__!MCU!__" == "__esp32s3__" (
|
||||||
|
@REM We are working with ESP32-S3
|
||||||
|
SET "OTA_FILENAME=bleota-s3.bin"
|
||||||
|
) ELSE IF "__!MCU!__" == "__esp32c3__" (
|
||||||
|
@REM We are working with ESP32-C3
|
||||||
|
SET "OTA_FILENAME=bleota-c3.bin"
|
||||||
) ELSE (
|
) ELSE (
|
||||||
CALL :LOG_MESSAGE DEBUG "We are NOT working with a *-tft-* file. !FILENAME!"
|
@REM Everything else
|
||||||
|
SET "OTA_FILENAME=bleota.bin"
|
||||||
)
|
)
|
||||||
|
|
||||||
FOR %%a IN (%BIGDB_8MB%) DO (
|
|
||||||
IF NOT "!FILENAME:%%a=!"=="!FILENAME!" (
|
|
||||||
@REM We are working with any of %BIGDB_8MB%.
|
|
||||||
SET "BIGDB8=1"
|
|
||||||
GOTO end_loop_bigdb_8mb
|
|
||||||
)
|
|
||||||
)
|
|
||||||
:end_loop_bigdb_8mb
|
|
||||||
|
|
||||||
FOR %%a IN (%MUIDB_8MB%) DO (
|
|
||||||
IF NOT "!FILENAME:%%a=!"=="!FILENAME!" (
|
|
||||||
@REM We are working with any of %MUIDB_8MB%.
|
|
||||||
SET "MUIDB8=1"
|
|
||||||
GOTO end_loop_muidb_8mb
|
|
||||||
)
|
|
||||||
)
|
|
||||||
:end_loop_muidb_8mb
|
|
||||||
|
|
||||||
FOR %%a IN (%BIGDB_16MB%) DO (
|
|
||||||
IF NOT "!FILENAME:%%a=!"=="!FILENAME!" (
|
|
||||||
@REM We are working with any of %BIGDB_16MB%.
|
|
||||||
SET "BIGDB16=1"
|
|
||||||
GOTO end_loop_bigdb_16mb
|
|
||||||
)
|
|
||||||
)
|
|
||||||
:end_loop_bigdb_16mb
|
|
||||||
|
|
||||||
IF %BIGDB8% EQU 1 CALL :LOG_MESSAGE INFO "BigDB 8mb partition selected."
|
|
||||||
IF %MUIDB8% EQU 1 CALL :LOG_MESSAGE INFO "MUIDB 8mb partition selected."
|
|
||||||
IF %BIGDB16% EQU 1 CALL :LOG_MESSAGE INFO "BigDB 16mb partition selected."
|
|
||||||
|
|
||||||
@REM Extract BASENAME from %FILENAME% for later use.
|
|
||||||
SET "BASENAME=!FILENAME:firmware-=!"
|
|
||||||
CALL :LOG_MESSAGE DEBUG "Computed firmware basename: !BASENAME!"
|
|
||||||
|
|
||||||
@REM Account for S3 and C3 board's different OTA partition.
|
|
||||||
FOR %%a IN (%S3%) DO (
|
|
||||||
IF NOT "!FILENAME:%%a=!"=="!FILENAME!" (
|
|
||||||
@REM We are working with any of %S3%.
|
|
||||||
SET "OTA_FILENAME=bleota-s3.bin"
|
|
||||||
GOTO :end_loop_s3
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
FOR %%a IN (%C3%) DO (
|
|
||||||
IF NOT "!FILENAME:%%a=!"=="!FILENAME!" (
|
|
||||||
@REM We are working with any of %C3%.
|
|
||||||
SET "OTA_FILENAME=bleota-c3.bin"
|
|
||||||
GOTO :end_loop_c3
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@REM Everything else
|
|
||||||
SET "OTA_FILENAME=bleota.bin"
|
|
||||||
:end_loop_s3
|
|
||||||
:end_loop_c3
|
|
||||||
CALL :LOG_MESSAGE DEBUG "Set OTA_FILENAME to: !OTA_FILENAME!"
|
CALL :LOG_MESSAGE DEBUG "Set OTA_FILENAME to: !OTA_FILENAME!"
|
||||||
|
|
||||||
@REM Set SPIFFS filename with "littlefs-" prefix.
|
@REM Set SPIFFS filename with "littlefs-" prefix.
|
||||||
SET "SPIFFS_FILENAME=littlefs-%BASENAME%"
|
SET "SPIFFS_FILENAME=littlefs-!PROGNAME:firmware-=!.bin"
|
||||||
CALL :LOG_MESSAGE DEBUG "Set SPIFFS_FILENAME to: !SPIFFS_FILENAME!"
|
CALL :LOG_MESSAGE DEBUG "Set SPIFFS_FILENAME to: !SPIFFS_FILENAME!"
|
||||||
|
|
||||||
@REM Default offsets.
|
|
||||||
@REM https://github.com/meshtastic/web-flasher/blob/main/stores/firmwareStore.ts#L202
|
|
||||||
SET "OTA_OFFSET=0x260000"
|
|
||||||
SET "SPIFFS_OFFSET=0x300000"
|
|
||||||
|
|
||||||
@REM Offsets for BigDB 8mb.
|
|
||||||
IF %BIGDB8% EQU 1 (
|
|
||||||
SET "OTA_OFFSET=0x340000"
|
|
||||||
SET "SPIFFS_OFFSET=0x670000"
|
|
||||||
)
|
|
||||||
|
|
||||||
@REM Offsets for MUIDB 8mb.
|
|
||||||
IF %MUIDB8% EQU 1 (
|
|
||||||
SET "OTA_OFFSET=0x5D0000"
|
|
||||||
SET "SPIFFS_OFFSET=0x670000"
|
|
||||||
)
|
|
||||||
|
|
||||||
@REM Offsets for BigDB 16mb.
|
|
||||||
IF %BIGDB16% EQU 1 (
|
|
||||||
SET "OTA_OFFSET=0x650000"
|
|
||||||
SET "SPIFFS_OFFSET=0xc90000"
|
|
||||||
)
|
|
||||||
|
|
||||||
CALL :LOG_MESSAGE DEBUG "Set OTA_OFFSET to: !OTA_OFFSET!"
|
CALL :LOG_MESSAGE DEBUG "Set OTA_OFFSET to: !OTA_OFFSET!"
|
||||||
CALL :LOG_MESSAGE DEBUG "Set SPIFFS_OFFSET to: !SPIFFS_OFFSET!"
|
CALL :LOG_MESSAGE DEBUG "Set SPIFFS_OFFSET to: !SPIFFS_OFFSET!"
|
||||||
|
|
||||||
|
|||||||
@@ -2,69 +2,15 @@
|
|||||||
|
|
||||||
PYTHON=${PYTHON:-$(which python3 python | head -n 1)}
|
PYTHON=${PYTHON:-$(which python3 python | head -n 1)}
|
||||||
BPS_RESET=false
|
BPS_RESET=false
|
||||||
TFT_BUILD=false
|
|
||||||
MCU=""
|
MCU=""
|
||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
RESET_BAUD=1200
|
RESET_BAUD=1200
|
||||||
FIRMWARE_OFFSET=0x00
|
FIRMWARE_OFFSET=0x00
|
||||||
|
# Default littlefs* offset.
|
||||||
# Variant groups
|
OFFSET=0x300000
|
||||||
BIGDB_8MB=(
|
# Default OTA Offset
|
||||||
"crowpanel-esp32s3"
|
OTA_OFFSET=0x260000
|
||||||
"heltec_capsule_sensor_v3"
|
|
||||||
"heltec-v3"
|
|
||||||
"heltec-vision-master-e213"
|
|
||||||
"heltec-vision-master-e290"
|
|
||||||
"heltec-vision-master-t190"
|
|
||||||
"heltec-wireless-paper"
|
|
||||||
"heltec-wireless-tracker"
|
|
||||||
"heltec-wsl-v3"
|
|
||||||
"icarus"
|
|
||||||
"seeed-xiao-s3"
|
|
||||||
"tbeam-s3-core"
|
|
||||||
"tracksenger"
|
|
||||||
)
|
|
||||||
MUIDB_8MB=(
|
|
||||||
"picomputer-s3"
|
|
||||||
"unphone"
|
|
||||||
"seeed-sensecap-indicator"
|
|
||||||
)
|
|
||||||
BIGDB_16MB=(
|
|
||||||
"dreamcatcher"
|
|
||||||
"elecrow-adv"
|
|
||||||
"ESP32-S3-Pico"
|
|
||||||
"heltec-v4"
|
|
||||||
"m5stack-cores3"
|
|
||||||
"mesh-tab"
|
|
||||||
"station-g2"
|
|
||||||
"t-deck"
|
|
||||||
"t-energy-s3"
|
|
||||||
"t-eth-elite"
|
|
||||||
"t-watch-s3"
|
|
||||||
"tlora-pager"
|
|
||||||
)
|
|
||||||
S3_VARIANTS=(
|
|
||||||
"s3"
|
|
||||||
"-v3"
|
|
||||||
"-v4"
|
|
||||||
"t-deck"
|
|
||||||
"wireless-paper"
|
|
||||||
"wireless-tracker"
|
|
||||||
"station-g2"
|
|
||||||
"unphone"
|
|
||||||
"t-eth-elite"
|
|
||||||
"tlora-pager"
|
|
||||||
"mesh-tab"
|
|
||||||
"dreamcatcher"
|
|
||||||
"ESP32-S3-Pico"
|
|
||||||
"seeed-sensecap-indicator"
|
|
||||||
"heltec_capsule_sensor_v3"
|
|
||||||
"vision-master"
|
|
||||||
"icarus"
|
|
||||||
"tracksenger"
|
|
||||||
"elecrow-adv"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Determine the correct esptool command to use
|
# Determine the correct esptool command to use
|
||||||
if "$PYTHON" -m esptool version >/dev/null 2>&1; then
|
if "$PYTHON" -m esptool version >/dev/null 2>&1; then
|
||||||
@@ -78,6 +24,14 @@ else
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check for jq
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
echo "Error: jq not found" >&2
|
||||||
|
echo "Install jq with your package manager." >&2
|
||||||
|
echo "e.g. 'apt install jq', 'dnf install jq', 'brew install jq', etc." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Usage info
|
# Usage info
|
||||||
@@ -89,7 +43,7 @@ Flash image file to device, but first erasing and writing system information.
|
|||||||
-h Display this help and exit.
|
-h Display this help and exit.
|
||||||
-p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerous).
|
-p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerous).
|
||||||
-P PYTHON Specify alternate python interpreter to use to invoke esptool. (Default: "$PYTHON")
|
-P PYTHON Specify alternate python interpreter to use to invoke esptool. (Default: "$PYTHON")
|
||||||
-f FILENAME The firmware .bin file to flash. Custom to your device type and region.
|
-f FILENAME The firmware *.factory.bin file to flash. Custom to your device type and region.
|
||||||
--1200bps-reset Attempt to place the device in correct mode. Some hardware requires this twice. (1200bps Reset)
|
--1200bps-reset Attempt to place the device in correct mode. Some hardware requires this twice. (1200bps Reset)
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
@@ -138,69 +92,43 @@ fi
|
|||||||
shift
|
shift
|
||||||
}
|
}
|
||||||
|
|
||||||
if [[ "$FILENAME" != firmware-* ]]; then
|
if [[ $(basename "$FILENAME") != firmware-*.factory.bin ]]; then
|
||||||
echo "Filename must be a firmware-* file."
|
echo "Filename must be a firmware-*.factory.bin file."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if FILENAME contains "-tft-" and set target partitionScheme accordingly.
|
# Extract PROGNAME from %FILENAME% for later use.
|
||||||
if [[ "${FILENAME//-tft-/}" != "$FILENAME" ]]; then
|
PROGNAME="${FILENAME/.factory.bin/}"
|
||||||
TFT_BUILD=true
|
# Derive metadata filename from %PROGNAME%.
|
||||||
fi
|
METAFILE="${PROGNAME}.mt.json"
|
||||||
|
|
||||||
# Extract BASENAME from %FILENAME% for later use.
|
if [[ -f "$FILENAME" && "$FILENAME" == *.factory.bin ]]; then
|
||||||
BASENAME="${FILENAME/firmware-/}"
|
# Display metadata if it exists
|
||||||
|
if [[ -f "$METAFILE" ]]; then
|
||||||
if [ -f "${FILENAME}" ] && [ -n "${FILENAME##*"update"*}" ]; then
|
echo "Firmware metadata: ${METAFILE}"
|
||||||
# Default littlefs* offset.
|
jq . "$METAFILE"
|
||||||
OFFSET=0x300000
|
# Extract relevant fields from metadata
|
||||||
|
if [[ $(jq -r '.part' "$METAFILE") != "null" ]]; then
|
||||||
# Default OTA Offset
|
OTA_OFFSET=$(jq -r '.part[] | select(.subtype == "ota_1") | .offset' "$METAFILE")
|
||||||
OTA_OFFSET=0x260000
|
SPIFFS_OFFSET=$(jq -r '.part[] | select(.subtype == "spiffs") | .offset' "$METAFILE")
|
||||||
|
|
||||||
# littlefs* offset for BigDB 8mb and OTA OFFSET.
|
|
||||||
for variant in "${BIGDB_8MB[@]}"; do
|
|
||||||
if [ -z "${FILENAME##*"$variant"*}" ]; then
|
|
||||||
OFFSET=0x670000
|
|
||||||
OTA_OFFSET=0x340000
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
for variant in "${MUIDB_8MB[@]}"; do
|
|
||||||
if [ -z "${FILENAME##*"$variant"*}" ]; then
|
|
||||||
OFFSET=0x670000
|
|
||||||
OTA_OFFSET=0x5D0000
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# littlefs* offset for BigDB 16mb and OTA OFFSET.
|
|
||||||
for variant in "${BIGDB_16MB[@]}"; do
|
|
||||||
if [ -z "${FILENAME##*"$variant"*}" ]; then
|
|
||||||
OFFSET=0xc90000
|
|
||||||
OTA_OFFSET=0x650000
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Account for S3 board's different OTA partition
|
|
||||||
# FIXME: Use PlatformIO info to determine MCU type, this is unmaintainable
|
|
||||||
for variant in "${S3_VARIANTS[@]}"; do
|
|
||||||
if [ -z "${FILENAME##*"$variant"*}" ]; then
|
|
||||||
MCU="esp32s3"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$MCU" != "esp32s3" ]; then
|
|
||||||
if [ -n "${FILENAME##*"esp32c3"*}" ]; then
|
|
||||||
OTAFILE=bleota.bin
|
|
||||||
else
|
|
||||||
OTAFILE=bleota-c3.bin
|
|
||||||
fi
|
fi
|
||||||
|
MCU=$(jq -r '.mcu' "$METAFILE")
|
||||||
else
|
else
|
||||||
|
echo "ERROR: No metadata file found at ${METAFILE}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Determine OTA filename based on MCU type
|
||||||
|
if [ "$MCU" == "esp32s3" ]; then
|
||||||
OTAFILE=bleota-s3.bin
|
OTAFILE=bleota-s3.bin
|
||||||
|
elif [ "$MCU" == "esp32c3" ]; then
|
||||||
|
OTAFILE=bleota-c3.bin
|
||||||
|
else
|
||||||
|
OTAFILE=bleota.bin
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Set SPIFFS filename with "littlefs-" prefix.
|
# Set SPIFFS filename with "littlefs-" prefix.
|
||||||
SPIFFSFILE=littlefs-${BASENAME}
|
SPIFFSFILE="littlefs-${PROGNAME/firmware-/}.bin"
|
||||||
|
|
||||||
if [[ ! -f "$FILENAME" ]]; then
|
if [[ ! -f "$FILENAME" ]]; then
|
||||||
echo "Error: file ${FILENAME} wasn't found. Terminating."
|
echo "Error: file ${FILENAME} wasn't found. Terminating."
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ ECHO --change-mode Attempt to place the device in correct mode. (1200bps
|
|||||||
ECHO Some hardware requires this twice.
|
ECHO Some hardware requires this twice.
|
||||||
ECHO.
|
ECHO.
|
||||||
ECHO Example: %SCRIPT_NAME% -p COM17 --change-mode
|
ECHO Example: %SCRIPT_NAME% -p COM17 --change-mode
|
||||||
ECHO Example: %SCRIPT_NAME% -f firmware-t-deck-tft-2.6.0.0b106d4-update.bin -p COM11
|
ECHO Example: %SCRIPT_NAME% -f firmware-t-deck-tft-2.6.0.0b106d4.bin -p COM11
|
||||||
GOTO eof
|
GOTO eof
|
||||||
|
|
||||||
:version
|
:version
|
||||||
ECHO %SCRIPT_NAME% [Version 2.6.2]
|
ECHO %SCRIPT_NAME% [Version 2.7.0]
|
||||||
ECHO Meshtastic
|
ECHO Meshtastic
|
||||||
GOTO eof
|
GOTO eof
|
||||||
|
|
||||||
@@ -78,12 +78,12 @@ IF NOT EXIST !FILENAME! (
|
|||||||
GOTO eof
|
GOTO eof
|
||||||
)
|
)
|
||||||
|
|
||||||
IF "!FILENAME:update=!"=="!FILENAME!" (
|
IF NOT "__!FILENAME:.factory.bin=!__"=="__!FILENAME!__" (
|
||||||
CALL :LOG_MESSAGE DEBUG "We are NOT working with a *update* file. !FILENAME!"
|
CALL :LOG_MESSAGE DEBUG "We are working with a *.factory.bin* file. !FILENAME!"
|
||||||
CALL :LOG_MESSAGE INFO "Use script device-install.bat to flash !FILENAME!."
|
CALL :LOG_MESSAGE INFO "Use script device-install.bat to flash !FILENAME!."
|
||||||
GOTO eof
|
GOTO eof
|
||||||
) ELSE (
|
) ELSE (
|
||||||
CALL :LOG_MESSAGE DEBUG "We are working with a *update* file. !FILENAME!"
|
CALL :LOG_MESSAGE DEBUG "We are not working with a *.factory.bin* file. !FILENAME!"
|
||||||
)
|
)
|
||||||
|
|
||||||
:skip-filename
|
:skip-filename
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ Flash image file to device, leave existing system intact."
|
|||||||
-h Display this help and exit
|
-h Display this help and exit
|
||||||
-p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerous).
|
-p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerous).
|
||||||
-P PYTHON Specify alternate python interpreter to use to invoke esptool. (Default: "$PYTHON")
|
-P PYTHON Specify alternate python interpreter to use to invoke esptool. (Default: "$PYTHON")
|
||||||
-f FILENAME The *update.bin file to flash. Custom to your device type.
|
-f FILENAME The *.bin file to flash. Custom to your device type.
|
||||||
--change-mode Attempt to place the device in correct mode. Some hardware requires this twice. (1200bps Reset)
|
--change-mode Attempt to place the device in correct mode. Some hardware requires this twice. (1200bps Reset)
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
@@ -78,7 +78,7 @@ fi
|
|||||||
shift
|
shift
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ -f "${FILENAME}" ] && [ -z "${FILENAME##*"update"*}" ]; then
|
if [[ -f "$FILENAME" && "$FILENAME" != *.factory.bin ]]; then
|
||||||
echo "Trying to flash update ${FILENAME}"
|
echo "Trying to flash update ${FILENAME}"
|
||||||
$ESPTOOL_CMD --baud $FLASH_BAUD write-flash $UPDATE_OFFSET "${FILENAME}"
|
$ESPTOOL_CMD --baud $FLASH_BAUD write-flash $UPDATE_OFFSET "${FILENAME}"
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ TOOLS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BACKTRACE_REGEX = re.compile(
|
BACKTRACE_REGEX = re.compile(
|
||||||
r"(?:\s+(0x40[0-2](?:\d|[a-f]|[A-F]){5}):0x(?:\d|[a-f]|[A-F]){8})\b"
|
r"\b(0x4[0-9a-fA-F]{7,8}):0x[0-9a-fA-F]{8}\b"
|
||||||
)
|
)
|
||||||
EXCEPTION_REGEX = re.compile("^Exception \\((?P<exc>[0-9]*)\\):$")
|
EXCEPTION_REGEX = re.compile("^Exception \\((?P<exc>[0-9]*)\\):$")
|
||||||
COUNTER_REGEX = re.compile(
|
COUNTER_REGEX = re.compile(
|
||||||
@@ -89,7 +89,7 @@ POINTER_REGEX = re.compile(
|
|||||||
STACK_BEGIN = ">>>stack>>>"
|
STACK_BEGIN = ">>>stack>>>"
|
||||||
STACK_END = "<<<stack<<<"
|
STACK_END = "<<<stack<<<"
|
||||||
STACK_REGEX = re.compile(
|
STACK_REGEX = re.compile(
|
||||||
"^(?P<off>[0-9a-f]+):\W+(?P<c1>[0-9a-f]+) (?P<c2>[0-9a-f]+) (?P<c3>[0-9a-f]+) (?P<c4>[0-9a-f]+)(\W.*)?$"
|
r"^(?P<off>[0-9a-f]+):\W+(?P<c1>[0-9a-f]+) (?P<c2>[0-9a-f]+) (?P<c3>[0-9a-f]+) (?P<c4>[0-9a-f]+)(\W.*)?$"
|
||||||
)
|
)
|
||||||
|
|
||||||
StackLine = namedtuple("StackLine", ["offset", "content"])
|
StackLine = namedtuple("StackLine", ["offset", "content"])
|
||||||
@@ -223,7 +223,7 @@ class AddressResolver(object):
|
|||||||
if match is None:
|
if match is None:
|
||||||
if last is not None and line.startswith("(inlined by)"):
|
if last is not None and line.startswith("(inlined by)"):
|
||||||
line = line[12:].strip()
|
line = line[12:].strip()
|
||||||
self._address_map[last] += "\n \-> inlined by: " + line
|
self._address_map[last] += "\n \\-> inlined by: " + line
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if match.group("result") == "?? ??:0":
|
if match.group("result") == "?? ??:0":
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
pio run --environment native
|
pio run --environment native
|
||||||
gdbserver --once localhost:2345 .pio/build/native/program "$@"
|
gdbserver --once localhost:2345 .pio/build/native/meshtasticd "$@"
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
pio run --environment native
|
pio run --environment native
|
||||||
.pio/build/native/program "$@"
|
.pio/build/native/meshtasticd "$@"
|
||||||
|
|||||||
@@ -87,6 +87,9 @@
|
|||||||
</screenshots>
|
</screenshots>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="2.7.17" date="2025-11-28">
|
||||||
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.17</url>
|
||||||
|
</release>
|
||||||
<release version="2.7.16" date="2025-11-19">
|
<release version="2.7.16" date="2025-11-19">
|
||||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.16</url>
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.16</url>
|
||||||
</release>
|
</release>
|
||||||
|
|||||||
@@ -2,98 +2,82 @@
|
|||||||
# trunk-ignore-all(ruff/F821)
|
# trunk-ignore-all(ruff/F821)
|
||||||
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
||||||
import sys
|
import sys
|
||||||
from os.path import join
|
from os.path import join, basename, isfile
|
||||||
import subprocess
|
import subprocess
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import time
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from readprops import readProps
|
from readprops import readProps
|
||||||
|
|
||||||
Import("env")
|
Import("env")
|
||||||
platform = env.PioPlatform()
|
platform = env.PioPlatform()
|
||||||
|
progname = env.get("PROGNAME")
|
||||||
|
lfsbin = f"{progname.replace('firmware-', 'littlefs-')}.bin"
|
||||||
|
|
||||||
|
def manifest_gather(source, target, env):
|
||||||
def esp32_create_combined_bin(source, target, env):
|
out = []
|
||||||
# this sub is borrowed from ESPEasy build toolchain. It's licensed under GPL V3
|
board_platform = env.BoardConfig().get("platform")
|
||||||
# https://github.com/letscontrolit/ESPEasy/blob/mega/tools/pio/post_esp32.py
|
needs_ota_suffix = board_platform == "nordicnrf52"
|
||||||
print("Generating combined binary for serial flashing")
|
check_paths = [
|
||||||
|
progname,
|
||||||
app_offset = 0x10000
|
f"{progname}.elf",
|
||||||
|
f"{progname}.bin",
|
||||||
new_file_name = env.subst("$BUILD_DIR/${PROGNAME}.factory.bin")
|
f"{progname}.factory.bin",
|
||||||
sections = env.subst(env.get("FLASH_EXTRA_IMAGES"))
|
f"{progname}.hex",
|
||||||
firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin")
|
f"{progname}.merged.hex",
|
||||||
chip = env.get("BOARD_MCU")
|
f"{progname}.uf2",
|
||||||
flash_size = env.BoardConfig().get("upload.flash_size")
|
f"{progname}.factory.uf2",
|
||||||
flash_freq = env.BoardConfig().get("build.f_flash", "40m")
|
f"{progname}.zip",
|
||||||
flash_freq = flash_freq.replace("000000L", "m")
|
lfsbin
|
||||||
flash_mode = env.BoardConfig().get("build.flash_mode", "dio")
|
|
||||||
memory_type = env.BoardConfig().get("build.arduino.memory_type", "qio_qspi")
|
|
||||||
if flash_mode == "qio" or flash_mode == "qout":
|
|
||||||
flash_mode = "dio"
|
|
||||||
if memory_type == "opi_opi" or memory_type == "opi_qspi":
|
|
||||||
flash_mode = "dout"
|
|
||||||
cmd = [
|
|
||||||
"--chip",
|
|
||||||
chip,
|
|
||||||
"merge_bin",
|
|
||||||
"-o",
|
|
||||||
new_file_name,
|
|
||||||
"--flash_mode",
|
|
||||||
flash_mode,
|
|
||||||
"--flash_freq",
|
|
||||||
flash_freq,
|
|
||||||
"--flash_size",
|
|
||||||
flash_size,
|
|
||||||
]
|
]
|
||||||
|
for p in check_paths:
|
||||||
|
f = env.File(env.subst(f"$BUILD_DIR/{p}"))
|
||||||
|
if f.exists():
|
||||||
|
manifest_name = p
|
||||||
|
if needs_ota_suffix and p == f"{progname}.zip":
|
||||||
|
manifest_name = f"{progname}-ota.zip"
|
||||||
|
d = {
|
||||||
|
"name": manifest_name,
|
||||||
|
"md5": f.get_content_hash(), # Returns MD5 hash
|
||||||
|
"bytes": f.get_size() # Returns file size in bytes
|
||||||
|
}
|
||||||
|
out.append(d)
|
||||||
|
print(d)
|
||||||
|
manifest_write(out, env)
|
||||||
|
|
||||||
print(" Offset | File")
|
def manifest_write(files, env):
|
||||||
for section in sections:
|
manifest = {
|
||||||
sect_adr, sect_file = section.split(" ", 1)
|
"version": verObj["long"],
|
||||||
print(f" - {sect_adr} | {sect_file}")
|
"build_epoch": build_epoch,
|
||||||
cmd += [sect_adr, sect_file]
|
"board": env.get("PIOENV"),
|
||||||
|
"mcu": env.get("BOARD_MCU"),
|
||||||
|
"repo": repo_owner,
|
||||||
|
"files": files,
|
||||||
|
"part": None,
|
||||||
|
"has_mui": False,
|
||||||
|
"has_inkhud": False,
|
||||||
|
}
|
||||||
|
# Get partition table (generated in esp32_pre.py) if it exists
|
||||||
|
if env.get("custom_mtjson_part"):
|
||||||
|
# custom_mtjson_part is a JSON string, convert it back to a dict
|
||||||
|
pj = json.loads(env.get("custom_mtjson_part"))
|
||||||
|
manifest["part"] = pj
|
||||||
|
# Enable has_mui for TFT builds
|
||||||
|
if ("HAS_TFT", 1) in env.get("CPPDEFINES", []):
|
||||||
|
manifest["has_mui"] = True
|
||||||
|
if "MESHTASTIC_INCLUDE_INKHUD" in env.get("CPPDEFINES", []):
|
||||||
|
manifest["has_inkhud"] = True
|
||||||
|
|
||||||
print(f" - {hex(app_offset)} | {firmware_name}")
|
# Write the manifest to the build directory
|
||||||
cmd += [hex(app_offset), firmware_name]
|
with open(env.subst("$BUILD_DIR/${PROGNAME}.mt.json"), "w") as f:
|
||||||
|
json.dump(manifest, f, indent=2)
|
||||||
print("Using esptool.py arguments: %s" % " ".join(cmd))
|
|
||||||
|
|
||||||
esptool.main(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
if platform.name == "espressif32":
|
|
||||||
sys.path.append(join(platform.get_package_dir("tool-esptoolpy")))
|
|
||||||
import esptool
|
|
||||||
|
|
||||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin)
|
|
||||||
|
|
||||||
esp32_kind = env.GetProjectOption("custom_esp32_kind")
|
|
||||||
if esp32_kind == "esp32":
|
|
||||||
# Free up some IRAM by removing auxiliary SPI flash chip drivers.
|
|
||||||
# Wrapped stub symbols are defined in src/platform/esp32/iram-quirk.c.
|
|
||||||
env.Append(
|
|
||||||
LINKFLAGS=[
|
|
||||||
"-Wl,--wrap=esp_flash_chip_gd",
|
|
||||||
"-Wl,--wrap=esp_flash_chip_issi",
|
|
||||||
"-Wl,--wrap=esp_flash_chip_winbond",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# For newer ESP32 targets, using newlib nano works better.
|
|
||||||
env.Append(LINKFLAGS=["--specs=nano.specs", "-u", "_printf_float"])
|
|
||||||
|
|
||||||
if platform.name == "nordicnrf52":
|
|
||||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex",
|
|
||||||
env.VerboseAction(f"\"{sys.executable}\" ./bin/uf2conv.py \"$BUILD_DIR/firmware.hex\" -c -f 0xADA52840 -o \"$BUILD_DIR/firmware.uf2\"",
|
|
||||||
"Generating UF2 file"))
|
|
||||||
|
|
||||||
Import("projenv")
|
Import("projenv")
|
||||||
|
|
||||||
prefsLoc = projenv["PROJECT_DIR"] + "/version.properties"
|
prefsLoc = projenv["PROJECT_DIR"] + "/version.properties"
|
||||||
verObj = readProps(prefsLoc)
|
verObj = readProps(prefsLoc)
|
||||||
print("Using meshtastic platformio-custom.py, firmware version " + verObj["long"] + " on " + env.get("PIOENV"))
|
print(f"Using meshtastic platformio-custom.py, firmware version {verObj['long']} on {env.get('PIOENV')}")
|
||||||
|
|
||||||
# get repository owner if git is installed
|
# get repository owner if git is installed
|
||||||
try:
|
try:
|
||||||
@@ -139,10 +123,10 @@ flags = [
|
|||||||
"-DBUILD_EPOCH=" + str(build_epoch),
|
"-DBUILD_EPOCH=" + str(build_epoch),
|
||||||
] + pref_flags
|
] + pref_flags
|
||||||
|
|
||||||
print ("Using flags:")
|
print("Using flags:")
|
||||||
for flag in flags:
|
for flag in flags:
|
||||||
print(flag)
|
print(flag)
|
||||||
|
|
||||||
projenv.Append(
|
projenv.Append(
|
||||||
CCFLAGS=flags,
|
CCFLAGS=flags,
|
||||||
)
|
)
|
||||||
@@ -180,4 +164,22 @@ def load_boot_logo(source, target, env):
|
|||||||
|
|
||||||
# Load the boot logo on TFT builds
|
# Load the boot logo on TFT builds
|
||||||
if ("HAS_TFT", 1) in env.get("CPPDEFINES", []):
|
if ("HAS_TFT", 1) in env.get("CPPDEFINES", []):
|
||||||
env.AddPreAction('$BUILD_DIR/littlefs.bin', load_boot_logo)
|
env.AddPreAction(f"$BUILD_DIR/{lfsbin}", load_boot_logo)
|
||||||
|
|
||||||
|
mtjson_deps = ["buildprog"]
|
||||||
|
if platform.name == "espressif32":
|
||||||
|
# Build littlefs image as part of mtjson target
|
||||||
|
# Equivalent to `pio run -t buildfs`
|
||||||
|
target_lfs = env.DataToBin(
|
||||||
|
join("$BUILD_DIR", "${ESP32_FS_IMAGE_NAME}"), "$PROJECT_DATA_DIR"
|
||||||
|
)
|
||||||
|
mtjson_deps.append(target_lfs)
|
||||||
|
|
||||||
|
env.AddCustomTarget(
|
||||||
|
name="mtjson",
|
||||||
|
dependencies=mtjson_deps,
|
||||||
|
actions=[manifest_gather],
|
||||||
|
title="Meshtastic Manifest",
|
||||||
|
description="Generating Meshtastic manifest JSON + Checksums",
|
||||||
|
always_build=False,
|
||||||
|
)
|
||||||
|
|||||||
19
bin/platformio-pre.py
Normal file
19
bin/platformio-pre.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# trunk-ignore-all(ruff/F821)
|
||||||
|
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
||||||
|
Import("env")
|
||||||
|
platform = env.PioPlatform()
|
||||||
|
|
||||||
|
if platform.name == "native":
|
||||||
|
env.Replace(PROGNAME="meshtasticd")
|
||||||
|
else:
|
||||||
|
from readprops import readProps
|
||||||
|
prefsLoc = env["PROJECT_DIR"] + "/version.properties"
|
||||||
|
verObj = readProps(prefsLoc)
|
||||||
|
env.Replace(PROGNAME=f"firmware-{env.get('PIOENV')}-{verObj['long']}")
|
||||||
|
env.Replace(ESP32_FS_IMAGE_NAME=f"littlefs-{env.get('PIOENV')}-{verObj['long']}")
|
||||||
|
|
||||||
|
# Print the new program name for verification
|
||||||
|
print(f"PROGNAME: {env.get('PROGNAME')}")
|
||||||
|
if platform.name == "espressif32":
|
||||||
|
print(f"ESP32_FS_IMAGE_NAME: {env.get('ESP32_FS_IMAGE_NAME')}")
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "Starting simulator"
|
echo "Starting simulator"
|
||||||
.pio/build/native/program &
|
.pio/build/native/meshtasticd -s &
|
||||||
sleep 20 # 5 seconds was not enough
|
sleep 20 # 5 seconds was not enough
|
||||||
|
|
||||||
echo "Simulator started, launching python test..."
|
echo "Simulator started, launching python test..."
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"extra_flags": [
|
"extra_flags": [
|
||||||
"-DBOARD_HAS_PSRAM",
|
"-DBOARD_HAS_PSRAM",
|
||||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||||
"-DARDUINO_USB_MODE=0",
|
"-DARDUINO_USB_MODE=1",
|
||||||
"-DARDUINO_RUNNING_CORE=1",
|
"-DARDUINO_RUNNING_CORE=1",
|
||||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||||
],
|
],
|
||||||
|
|||||||
6
debian/changelog
vendored
6
debian/changelog
vendored
@@ -1,3 +1,9 @@
|
|||||||
|
meshtasticd (2.7.17.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Version 2.7.17
|
||||||
|
|
||||||
|
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Fri, 28 Nov 2025 15:11:34 +0000
|
||||||
|
|
||||||
meshtasticd (2.7.16.0) unstable; urgency=medium
|
meshtasticd (2.7.16.0) unstable; urgency=medium
|
||||||
|
|
||||||
* Version 2.7.16
|
* Version 2.7.16
|
||||||
|
|||||||
1
debian/rules
vendored
1
debian/rules
vendored
@@ -28,5 +28,4 @@ override_dh_auto_build:
|
|||||||
# Build with platformio
|
# Build with platformio
|
||||||
$(PIO_ENV) platformio run -e native-tft
|
$(PIO_ENV) platformio run -e native-tft
|
||||||
# Move the binary and default config to the correct name
|
# Move the binary and default config to the correct name
|
||||||
mv .pio/build/native-tft/program .pio/build/native-tft/meshtasticd
|
|
||||||
cp bin/config-dist.yaml bin/config.yaml
|
cp bin/config-dist.yaml bin/config.yaml
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
# trunk-ignore-all(flake8/F821)
|
# trunk-ignore-all(flake8/F821)
|
||||||
# trunk-ignore-all(ruff/F821)
|
# trunk-ignore-all(ruff/F821)
|
||||||
|
|
||||||
Import("env")
|
Import("env")
|
||||||
|
|
||||||
# NOTE: This is not currently used, but can serve as an example on how to write extra_scripts
|
|
||||||
|
|
||||||
# print("Current CLI targets", COMMAND_LINE_TARGETS)
|
# print("Current CLI targets", COMMAND_LINE_TARGETS)
|
||||||
# print("Current Build targets", BUILD_TARGETS)
|
# print("Current Build targets", BUILD_TARGETS)
|
||||||
# print("CPP defs", env.get("CPPDEFINES"))
|
# print("CPP defs", env.get("CPPDEFINES"))
|
||||||
|
|||||||
86
extra_scripts/esp32_extra.py
Executable file
86
extra_scripts/esp32_extra.py
Executable file
@@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# trunk-ignore-all(ruff/F821)
|
||||||
|
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
||||||
|
# trunk-ignore-all(ruff/E402): Hacky esptool import
|
||||||
|
# trunk-ignore-all(flake8/E402): Hacky esptool import
|
||||||
|
import sys
|
||||||
|
from os.path import join
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
platform = env.PioPlatform()
|
||||||
|
|
||||||
|
sys.path.append(join(platform.get_package_dir("tool-esptoolpy")))
|
||||||
|
# IntelHex workaround, remove after fixed upstream
|
||||||
|
# https://github.com/platformio/platform-espressif32/issues/1632
|
||||||
|
try:
|
||||||
|
import intelhex
|
||||||
|
except ImportError:
|
||||||
|
env.Execute("$PYTHONEXE -m pip install intelhex")
|
||||||
|
import esptool
|
||||||
|
|
||||||
|
|
||||||
|
def esp32_create_combined_bin(source, target, env):
|
||||||
|
# this sub is borrowed from ESPEasy build toolchain. It's licensed under GPL V3
|
||||||
|
# https://github.com/letscontrolit/ESPEasy/blob/mega/tools/pio/post_esp32.py
|
||||||
|
print("Generating combined binary for serial flashing")
|
||||||
|
|
||||||
|
app_offset = 0x10000
|
||||||
|
|
||||||
|
new_file_name = env.subst("$BUILD_DIR/${PROGNAME}.factory.bin")
|
||||||
|
sections = env.subst(env.get("FLASH_EXTRA_IMAGES"))
|
||||||
|
firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin")
|
||||||
|
chip = env.get("BOARD_MCU")
|
||||||
|
board = env.BoardConfig()
|
||||||
|
flash_size = board.get("upload.flash_size")
|
||||||
|
flash_freq = board.get("build.f_flash", "40m")
|
||||||
|
flash_freq = flash_freq.replace("000000L", "m")
|
||||||
|
flash_mode = board.get("build.flash_mode", "dio")
|
||||||
|
memory_type = board.get("build.arduino.memory_type", "qio_qspi")
|
||||||
|
if flash_mode == "qio" or flash_mode == "qout":
|
||||||
|
flash_mode = "dio"
|
||||||
|
if memory_type == "opi_opi" or memory_type == "opi_qspi":
|
||||||
|
flash_mode = "dout"
|
||||||
|
cmd = [
|
||||||
|
"--chip",
|
||||||
|
chip,
|
||||||
|
"merge_bin",
|
||||||
|
"-o",
|
||||||
|
new_file_name,
|
||||||
|
"--flash_mode",
|
||||||
|
flash_mode,
|
||||||
|
"--flash_freq",
|
||||||
|
flash_freq,
|
||||||
|
"--flash_size",
|
||||||
|
flash_size,
|
||||||
|
]
|
||||||
|
|
||||||
|
print(" Offset | File")
|
||||||
|
for section in sections:
|
||||||
|
sect_adr, sect_file = section.split(" ", 1)
|
||||||
|
print(f" - {sect_adr} | {sect_file}")
|
||||||
|
cmd += [sect_adr, sect_file]
|
||||||
|
|
||||||
|
print(f" - {hex(app_offset)} | {firmware_name}")
|
||||||
|
cmd += [hex(app_offset), firmware_name]
|
||||||
|
|
||||||
|
print("Using esptool.py arguments: %s" % " ".join(cmd))
|
||||||
|
|
||||||
|
esptool.main(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin)
|
||||||
|
|
||||||
|
esp32_kind = env.GetProjectOption("custom_esp32_kind")
|
||||||
|
if esp32_kind == "esp32":
|
||||||
|
# Free up some IRAM by removing auxiliary SPI flash chip drivers.
|
||||||
|
# Wrapped stub symbols are defined in src/platform/esp32/iram-quirk.c.
|
||||||
|
env.Append(
|
||||||
|
LINKFLAGS=[
|
||||||
|
"-Wl,--wrap=esp_flash_chip_gd",
|
||||||
|
"-Wl,--wrap=esp_flash_chip_issi",
|
||||||
|
"-Wl,--wrap=esp_flash_chip_winbond",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# For newer ESP32 targets, using newlib nano works better.
|
||||||
|
env.Append(LINKFLAGS=["--specs=nano.specs", "-u", "_printf_float"])
|
||||||
73
extra_scripts/esp32_pre.py
Executable file
73
extra_scripts/esp32_pre.py
Executable file
@@ -0,0 +1,73 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# trunk-ignore-all(ruff/F821)
|
||||||
|
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from os.path import isfile
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
|
||||||
|
# From https://github.com/platformio/platform-espressif32/blob/develop/builder/main.py
|
||||||
|
def _parse_size(value):
|
||||||
|
if isinstance(value, int):
|
||||||
|
return value
|
||||||
|
elif value.isdigit():
|
||||||
|
return int(value)
|
||||||
|
elif value.startswith("0x"):
|
||||||
|
return int(value, 16)
|
||||||
|
elif value[-1].upper() in ("K", "M"):
|
||||||
|
base = 1024 if value[-1].upper() == "K" else 1024 * 1024
|
||||||
|
return int(value[:-1]) * base
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_partitions(env):
|
||||||
|
partitions_csv = env.subst("$PARTITIONS_TABLE_CSV")
|
||||||
|
if not isfile(partitions_csv):
|
||||||
|
sys.stderr.write(
|
||||||
|
"Could not find the file %s with partitions " "table.\n" % partitions_csv
|
||||||
|
)
|
||||||
|
env.Exit(1)
|
||||||
|
return
|
||||||
|
|
||||||
|
result = []
|
||||||
|
# The first offset is 0x9000 because partition table is flashed to 0x8000 and
|
||||||
|
# occupies an entire flash sector, which size is 0x1000
|
||||||
|
next_offset = 0x9000
|
||||||
|
with open(partitions_csv) as fp:
|
||||||
|
for line in fp.readlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
tokens = [t.strip() for t in line.split(",")]
|
||||||
|
if len(tokens) < 5:
|
||||||
|
continue
|
||||||
|
|
||||||
|
bound = 0x10000 if tokens[1] in ("0", "app") else 4
|
||||||
|
calculated_offset = (next_offset + bound - 1) & ~(bound - 1)
|
||||||
|
partition = {
|
||||||
|
"name": tokens[0],
|
||||||
|
"type": tokens[1],
|
||||||
|
"subtype": tokens[2],
|
||||||
|
"offset": tokens[3] or calculated_offset,
|
||||||
|
"size": tokens[4],
|
||||||
|
"flags": tokens[5] if len(tokens) > 5 else None,
|
||||||
|
}
|
||||||
|
result.append(partition)
|
||||||
|
next_offset = _parse_size(partition["offset"]) + _parse_size(
|
||||||
|
partition["size"]
|
||||||
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def mtjson_esp32_part(target, source, env):
|
||||||
|
part = _parse_partitions(env)
|
||||||
|
pj = json.dumps(part)
|
||||||
|
# print(f"JSON_PARTITIONS: {pj}")
|
||||||
|
# Dump json string to 'custom_mtjson_part' variable to use later when writing the manifest
|
||||||
|
env.Replace(custom_mtjson_part=pj)
|
||||||
|
|
||||||
|
|
||||||
|
env.AddPreAction("mtjson", mtjson_esp32_part)
|
||||||
50
extra_scripts/nrf52_extra.py
Executable file
50
extra_scripts/nrf52_extra.py
Executable file
@@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# trunk-ignore-all(ruff/F821)
|
||||||
|
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from os.path import basename
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
|
||||||
|
# Custom HEX from ELF
|
||||||
|
# Convert hex to uf2 for nrf52
|
||||||
|
def nrf52_hex_to_uf2(source, target, env):
|
||||||
|
hex_path = target[0].get_abspath()
|
||||||
|
# When using merged hex, drop 'merged' from uf2 filename
|
||||||
|
uf2_path = hex_path.replace(".merged.", ".")
|
||||||
|
uf2_path = uf2_path.replace(".hex", ".uf2")
|
||||||
|
env.Execute(
|
||||||
|
env.VerboseAction(
|
||||||
|
f'"{sys.executable}" ./bin/uf2conv.py "{hex_path}" -c -f 0xADA52840 -o "{uf2_path}"',
|
||||||
|
f"Generating UF2 file from {basename(hex_path)}",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def nrf52_mergehex(source, target, env):
|
||||||
|
hex_path = target[0].get_abspath()
|
||||||
|
merged_hex_path = hex_path.replace(".hex", ".merged.hex")
|
||||||
|
merge_with = None
|
||||||
|
if "wio-sdk-wm1110" == str(env.get("PIOENV")):
|
||||||
|
merge_with = env.subst("$PROJECT_DIR/bin/s140_nrf52_7.3.0_softdevice.hex")
|
||||||
|
else:
|
||||||
|
print("merge_with not defined for this target")
|
||||||
|
|
||||||
|
if merge_with is not None:
|
||||||
|
env.Execute(
|
||||||
|
env.VerboseAction(
|
||||||
|
f'"$PROJECT_DIR/bin/mergehex" -m "{hex_path}" "{merge_with}" -o "{merged_hex_path}"',
|
||||||
|
"Merging HEX with SoftDevice",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
print(f'Merged file saved at "{basename(merged_hex_path)}"')
|
||||||
|
nrf52_hex_to_uf2([hex_path, merge_with], [env.File(merged_hex_path)], env)
|
||||||
|
|
||||||
|
|
||||||
|
# if WM1110 target, merge hex with softdevice 7.3.0
|
||||||
|
if "wio-sdk-wm1110" == env.get("PIOENV"):
|
||||||
|
env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex", nrf52_mergehex)
|
||||||
|
else:
|
||||||
|
env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex", nrf52_hex_to_uf2)
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
# trunk-ignore-all(ruff/F821)
|
# trunk-ignore-all(ruff/F821)
|
||||||
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
||||||
|
|
||||||
Import("env")
|
Import("env")
|
||||||
|
|
||||||
# Custom HEX from ELF
|
# Custom HEX from ELF
|
||||||
env.AddPostAction(
|
env.AddPostAction(
|
||||||
"$BUILD_DIR/${PROGNAME}.elf",
|
"$BUILD_DIR/${PROGNAME}.elf",
|
||||||
@@ -33,7 +33,6 @@ BuildRequires: python3dist(grpcio[protobuf])
|
|||||||
BuildRequires: python3dist(grpcio-tools)
|
BuildRequires: python3dist(grpcio-tools)
|
||||||
BuildRequires: git-core
|
BuildRequires: git-core
|
||||||
BuildRequires: gcc-c++
|
BuildRequires: gcc-c++
|
||||||
BuildRequires: (glibc-devel >= 2.38) or pkgconfig(libbsd-overlay)
|
|
||||||
BuildRequires: pkgconfig(yaml-cpp)
|
BuildRequires: pkgconfig(yaml-cpp)
|
||||||
BuildRequires: pkgconfig(libgpiod)
|
BuildRequires: pkgconfig(libgpiod)
|
||||||
BuildRequires: pkgconfig(bluez)
|
BuildRequires: pkgconfig(bluez)
|
||||||
@@ -77,7 +76,7 @@ platformio run -e native-tft
|
|||||||
%install
|
%install
|
||||||
# Install meshtasticd binary
|
# Install meshtasticd binary
|
||||||
mkdir -p %{buildroot}%{_bindir}
|
mkdir -p %{buildroot}%{_bindir}
|
||||||
install -m 0755 .pio/build/native-tft/program %{buildroot}%{_bindir}/meshtasticd
|
install -m 0755 .pio/build/native-tft/meshtasticd %{buildroot}%{_bindir}/meshtasticd
|
||||||
|
|
||||||
# Install portduino VFS dir
|
# Install portduino VFS dir
|
||||||
install -p -d -m 0770 %{buildroot}%{_localstatedir}/lib/meshtasticd
|
install -p -d -m 0770 %{buildroot}%{_localstatedir}/lib/meshtasticd
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
default_envs = tbeam
|
default_envs = tbeam
|
||||||
|
|
||||||
extra_configs =
|
extra_configs =
|
||||||
arch/*/*.ini
|
variants/*/*.ini
|
||||||
variants/*/*/platformio.ini
|
variants/*/*/platformio.ini
|
||||||
variants/*/diy/*/platformio.ini
|
variants/*/diy/*/platformio.ini
|
||||||
src/graphics/niche/InkHUD/PlatformioConfig.ini
|
src/graphics/niche/InkHUD/PlatformioConfig.ini
|
||||||
@@ -14,7 +14,9 @@ description = Meshtastic
|
|||||||
|
|
||||||
[env]
|
[env]
|
||||||
test_build_src = true
|
test_build_src = true
|
||||||
extra_scripts = bin/platformio-custom.py
|
extra_scripts =
|
||||||
|
pre:bin/platformio-pre.py
|
||||||
|
bin/platformio-custom.py
|
||||||
; note: we add src to our include search path so that lmic_project_config can override
|
; note: we add src to our include search path so that lmic_project_config can override
|
||||||
; note: TINYGPS_OPTION_NO_CUSTOM_FIELDS is VERY important. We don't use custom fields and somewhere in that pile
|
; note: TINYGPS_OPTION_NO_CUSTOM_FIELDS is VERY important. We don't use custom fields and somewhere in that pile
|
||||||
; of code is a heap corruption bug!
|
; of code is a heap corruption bug!
|
||||||
@@ -62,7 +64,7 @@ monitor_speed = 115200
|
|||||||
monitor_filters = direct
|
monitor_filters = direct
|
||||||
lib_deps =
|
lib_deps =
|
||||||
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
|
||||||
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/2887bf4a19f64d92c984dcc8fd5ca7429e425e4a.zip
|
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/b34c6817c25d6faabb3a8a162b5d14fb75395433.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
|
||||||
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
|
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
|
||||||
@@ -121,7 +123,7 @@ lib_deps =
|
|||||||
[device-ui_base]
|
[device-ui_base]
|
||||||
lib_deps =
|
lib_deps =
|
||||||
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
||||||
https://github.com/meshtastic/device-ui/archive/28167c67dfd13015a0b5eef1828f95fe8e3ab7c3.zip
|
https://github.com/meshtastic/device-ui/archive/a8e2f947f7abaf0c5ac8e6dd189a22156335beaa.zip
|
||||||
|
|
||||||
; Common libs for environmental measurements in telemetry module
|
; Common libs for environmental measurements in telemetry module
|
||||||
[environmental_base]
|
[environmental_base]
|
||||||
@@ -182,8 +184,8 @@ lib_deps =
|
|||||||
dfrobot/DFRobot_BMM150@1.0.0
|
dfrobot/DFRobot_BMM150@1.0.0
|
||||||
# renovate: datasource=custom.pio depName=Adafruit_TSL2561 packageName=adafruit/library/Adafruit TSL2561
|
# renovate: datasource=custom.pio depName=Adafruit_TSL2561 packageName=adafruit/library/Adafruit TSL2561
|
||||||
adafruit/Adafruit TSL2561@1.1.2
|
adafruit/Adafruit TSL2561@1.1.2
|
||||||
# renovate: datasource=custom.pio depName=BH1750_WE packageName=wollewald/BH1750_WE@^1.1.10
|
# renovate: datasource=custom.pio depName=BH1750_WE packageName=wollewald/library/BH1750_WE
|
||||||
wollewald/BH1750_WE@^1.1.10
|
wollewald/BH1750_WE@1.1.10
|
||||||
|
|
||||||
; (not included in native / portduino)
|
; (not included in native / portduino)
|
||||||
[environmental_extra]
|
[environmental_extra]
|
||||||
@@ -205,7 +207,7 @@ lib_deps =
|
|||||||
# renovate: datasource=custom.pio depName=SparkFun Qwiic Scale NAU7802 packageName=sparkfun/library/SparkFun Qwiic Scale NAU7802 Arduino Library
|
# renovate: datasource=custom.pio depName=SparkFun Qwiic Scale NAU7802 packageName=sparkfun/library/SparkFun Qwiic Scale NAU7802 Arduino Library
|
||||||
sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@1.0.6
|
sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@1.0.6
|
||||||
# renovate: datasource=custom.pio depName=ClosedCube OPT3001 packageName=closedcube/library/ClosedCube OPT3001
|
# renovate: datasource=custom.pio depName=ClosedCube OPT3001 packageName=closedcube/library/ClosedCube OPT3001
|
||||||
ClosedCube OPT3001@1.1.2
|
closedcube/ClosedCube OPT3001@1.1.2
|
||||||
# renovate: datasource=custom.pio depName=Bosch BSEC2 packageName=boschsensortec/library/bsec2
|
# renovate: datasource=custom.pio depName=Bosch BSEC2 packageName=boschsensortec/library/bsec2
|
||||||
boschsensortec/bsec2@1.10.2610
|
boschsensortec/bsec2@1.10.2610
|
||||||
# renovate: datasource=custom.pio depName=Bosch BME68x packageName=boschsensortec/library/BME68x Sensor Library
|
# renovate: datasource=custom.pio depName=Bosch BME68x packageName=boschsensortec/library/BME68x Sensor Library
|
||||||
|
|||||||
Submodule protobufs updated: 52fa252f1e...c2e45a3fc9
@@ -50,8 +50,11 @@ class AudioThread : public concurrency::OSThread
|
|||||||
delete i2sRtttl;
|
delete i2sRtttl;
|
||||||
i2sRtttl = nullptr;
|
i2sRtttl = nullptr;
|
||||||
}
|
}
|
||||||
delete rtttlFile;
|
|
||||||
rtttlFile = nullptr;
|
if (rtttlFile != nullptr) {
|
||||||
|
delete rtttlFile;
|
||||||
|
rtttlFile = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
setCPUFast(false);
|
setCPUFast(false);
|
||||||
#ifdef T_LORA_PAGER
|
#ifdef T_LORA_PAGER
|
||||||
@@ -99,9 +102,9 @@ class AudioThread : public concurrency::OSThread
|
|||||||
};
|
};
|
||||||
|
|
||||||
AudioGeneratorRTTTL *i2sRtttl = nullptr;
|
AudioGeneratorRTTTL *i2sRtttl = nullptr;
|
||||||
AudioOutputI2S *audioOut;
|
AudioOutputI2S *audioOut = nullptr;
|
||||||
|
|
||||||
AudioFileSourcePROGMEM *rtttlFile;
|
AudioFileSourcePROGMEM *rtttlFile = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaC
|
|||||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST:
|
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST:
|
||||||
return useShortName ? "LongF" : "LongFast";
|
return useShortName ? "LongF" : "LongFast";
|
||||||
break;
|
break;
|
||||||
|
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO:
|
||||||
|
return useShortName ? "LongT" : "LongTurbo";
|
||||||
|
break;
|
||||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
|
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
|
||||||
return useShortName ? "LongM" : "LongMod";
|
return useShortName ? "LongM" : "LongMod";
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ struct ToneDuration {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Some common frequencies.
|
// Some common frequencies.
|
||||||
|
#define NOTE_SILENT 1
|
||||||
#define NOTE_C3 131
|
#define NOTE_C3 131
|
||||||
#define NOTE_CS3 139
|
#define NOTE_CS3 139
|
||||||
#define NOTE_D3 147
|
#define NOTE_D3 147
|
||||||
@@ -29,11 +30,16 @@ struct ToneDuration {
|
|||||||
#define NOTE_AS3 233
|
#define NOTE_AS3 233
|
||||||
#define NOTE_B3 247
|
#define NOTE_B3 247
|
||||||
#define NOTE_CS4 277
|
#define NOTE_CS4 277
|
||||||
|
#define NOTE_B4 494
|
||||||
|
#define NOTE_F5 698
|
||||||
|
#define NOTE_G6 1568
|
||||||
|
#define NOTE_E7 2637
|
||||||
|
|
||||||
|
const int DURATION_1_16 = 62; // 1/16 note
|
||||||
const int DURATION_1_8 = 125; // 1/8 note
|
const int DURATION_1_8 = 125; // 1/8 note
|
||||||
const int DURATION_1_4 = 250; // 1/4 note
|
const int DURATION_1_4 = 250; // 1/4 note
|
||||||
const int DURATION_1_2 = 500; // 1/2 note
|
const int DURATION_1_2 = 500; // 1/2 note
|
||||||
const int DURATION_3_4 = 750; // 1/4 note
|
const int DURATION_3_4 = 750; // 3/4 note
|
||||||
const int DURATION_1_1 = 1000; // 1/1 note
|
const int DURATION_1_1 = 1000; // 1/1 note
|
||||||
|
|
||||||
void playTones(const ToneDuration *tone_durations, int size)
|
void playTones(const ToneDuration *tone_durations, int size)
|
||||||
@@ -71,13 +77,24 @@ void playLongBeep()
|
|||||||
|
|
||||||
void playGPSEnableBeep()
|
void playGPSEnableBeep()
|
||||||
{
|
{
|
||||||
|
#if defined(R1_NEO) || defined(MUZI_BASE)
|
||||||
|
ToneDuration melody[] = {
|
||||||
|
{NOTE_F5, DURATION_1_2}, {NOTE_G6, DURATION_1_8}, {NOTE_E7, DURATION_1_4}, {NOTE_SILENT, DURATION_1_2}};
|
||||||
|
#else
|
||||||
ToneDuration melody[] = {{NOTE_C3, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_CS4, DURATION_1_4}};
|
ToneDuration melody[] = {{NOTE_C3, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_CS4, DURATION_1_4}};
|
||||||
|
#endif
|
||||||
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
||||||
}
|
}
|
||||||
|
|
||||||
void playGPSDisableBeep()
|
void playGPSDisableBeep()
|
||||||
{
|
{
|
||||||
|
#if defined(R1_NEO) || defined(MUZI_BASE)
|
||||||
|
ToneDuration melody[] = {{NOTE_B4, DURATION_1_16}, {NOTE_B4, DURATION_1_16}, {NOTE_SILENT, DURATION_1_8},
|
||||||
|
{NOTE_F3, DURATION_1_16}, {NOTE_F3, DURATION_1_16}, {NOTE_SILENT, DURATION_1_8},
|
||||||
|
{NOTE_C3, DURATION_1_1}, {NOTE_SILENT, DURATION_1_1}};
|
||||||
|
#else
|
||||||
ToneDuration melody[] = {{NOTE_CS4, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_C3, DURATION_1_4}};
|
ToneDuration melody[] = {{NOTE_CS4, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_C3, DURATION_1_4}};
|
||||||
|
#endif
|
||||||
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,29 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
/* Offer chance for variant-specific defines */
|
/* Offer chance for variant-specific defines */
|
||||||
#include "variant.h"
|
#include "variant.h"
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Display feature overrides
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Allow build environments to opt-in explicitly to the E-Ink UI stack while
|
||||||
|
// keeping headless targets slim by default. Existing variants that already
|
||||||
|
// define USE_EINK continue to work without additional flags.
|
||||||
|
#ifndef MESHTASTIC_USE_EINK_UI
|
||||||
|
#ifdef USE_EINK
|
||||||
|
#define MESHTASTIC_USE_EINK_UI 1
|
||||||
|
#else
|
||||||
|
#define MESHTASTIC_USE_EINK_UI 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MESHTASTIC_USE_EINK_UI
|
||||||
|
#ifndef USE_EINK
|
||||||
|
#define USE_EINK
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#undef USE_EINK
|
||||||
|
#endif
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Version
|
// Version
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -371,6 +394,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#ifndef HAS_BLUETOOTH
|
#ifndef HAS_BLUETOOTH
|
||||||
#define HAS_BLUETOOTH 0
|
#define HAS_BLUETOOTH 0
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef USE_TFTDISPLAY
|
||||||
|
#define USE_TFTDISPLAY 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef HW_VENDOR
|
#ifndef HW_VENDOR
|
||||||
#error HW_VENDOR must be defined
|
#error HW_VENDOR must be defined
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ bool EInkDisplay::connect()
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(TTGO_T_ECHO) || defined(ELECROW_ThinkNode_M1) || defined(T_ECHO_LITE)
|
#if defined(TTGO_T_ECHO) || defined(ELECROW_ThinkNode_M1) || defined(T_ECHO_LITE) || defined(TTGO_T_ECHO_PLUS)
|
||||||
{
|
{
|
||||||
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1);
|
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1);
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,11 @@ using graphics::Emote;
|
|||||||
using graphics::emotes;
|
using graphics::emotes;
|
||||||
using graphics::numEmotes;
|
using graphics::numEmotes;
|
||||||
|
|
||||||
|
#if USE_TFTDISPLAY
|
||||||
extern uint16_t TFT_MESH;
|
extern uint16_t TFT_MESH;
|
||||||
|
#else
|
||||||
|
uint16_t TFT_MESH = COLOR565(0x67, 0xEA, 0x94);
|
||||||
|
#endif
|
||||||
|
|
||||||
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
|
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
|
||||||
#include "mesh/wifi/WiFiAPClient.h"
|
#include "mesh/wifi/WiFiAPClient.h"
|
||||||
@@ -226,24 +230,9 @@ void Screen::showTextInput(const char *header, const char *initialText, uint32_t
|
|||||||
{
|
{
|
||||||
LOG_INFO("showTextInput called with header='%s', durationMs=%d", header ? header : "NULL", durationMs);
|
LOG_INFO("showTextInput called with header='%s', durationMs=%d", header ? header : "NULL", durationMs);
|
||||||
|
|
||||||
if (NotificationRenderer::virtualKeyboard) {
|
// Start OnScreenKeyboardModule session (non-touch variant)
|
||||||
delete NotificationRenderer::virtualKeyboard;
|
OnScreenKeyboardModule::instance().start(header, initialText, durationMs, textCallback);
|
||||||
NotificationRenderer::virtualKeyboard = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
NotificationRenderer::textInputCallback = nullptr;
|
|
||||||
|
|
||||||
NotificationRenderer::virtualKeyboard = new VirtualKeyboard();
|
|
||||||
if (header) {
|
|
||||||
NotificationRenderer::virtualKeyboard->setHeader(header);
|
|
||||||
}
|
|
||||||
if (initialText) {
|
|
||||||
NotificationRenderer::virtualKeyboard->setInputText(initialText);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up callback with safer cleanup mechanism
|
|
||||||
NotificationRenderer::textInputCallback = textCallback;
|
NotificationRenderer::textInputCallback = textCallback;
|
||||||
NotificationRenderer::virtualKeyboard->setCallback([textCallback](const std::string &text) { textCallback(text); });
|
|
||||||
|
|
||||||
// Store the message and set the expiration timestamp (use same pattern as other notifications)
|
// Store the message and set the expiration timestamp (use same pattern as other notifications)
|
||||||
strncpy(NotificationRenderer::alertBannerMessage, header ? header : "Text Input", 255);
|
strncpy(NotificationRenderer::alertBannerMessage, header ? header : "Text Input", 255);
|
||||||
@@ -1509,14 +1498,14 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
|||||||
// Incoming message
|
// Incoming message
|
||||||
devicestate.has_rx_text_message = true; // Needed to include the message frame
|
devicestate.has_rx_text_message = true; // Needed to include the message frame
|
||||||
hasUnreadMessage = true; // Enables mail icon in the header
|
hasUnreadMessage = true; // Enables mail icon in the header
|
||||||
setFrames(FOCUS_PRESERVE); // Refresh frame list without switching view
|
setFrames(FOCUS_PRESERVE); // Refresh frame list without switching view (no-op during text_input)
|
||||||
|
|
||||||
// Only wake/force display if the configuration allows it
|
// Only wake/force display if the configuration allows it
|
||||||
if (shouldWakeOnReceivedMessage()) {
|
if (shouldWakeOnReceivedMessage()) {
|
||||||
setOn(true); // Wake up the screen first
|
setOn(true); // Wake up the screen first
|
||||||
forceDisplay(); // Forces screen redraw
|
forceDisplay(); // Forces screen redraw
|
||||||
}
|
}
|
||||||
// === Prepare banner content ===
|
// === Prepare banner/popup content ===
|
||||||
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
|
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
|
||||||
const meshtastic_Channel channel =
|
const meshtastic_Channel channel =
|
||||||
channels.getByIndex(packet->channel ? packet->channel : channels.getPrimaryIndex());
|
channels.getByIndex(packet->channel ? packet->channel : channels.getPrimaryIndex());
|
||||||
@@ -1540,38 +1529,84 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
|||||||
|
|
||||||
// Unlike generic messages, alerts (when enabled via the ext notif module) ignore any
|
// Unlike generic messages, alerts (when enabled via the ext notif module) ignore any
|
||||||
// 'mute' preferences set to any specific node or channel.
|
// 'mute' preferences set to any specific node or channel.
|
||||||
if (isAlert) {
|
// If on-screen keyboard is active, show a transient popup over keyboard instead of interrupting it
|
||||||
if (longName && longName[0]) {
|
if (NotificationRenderer::current_notification_type == notificationTypeEnum::text_input) {
|
||||||
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
|
// Wake and force redraw so popup is visible immediately
|
||||||
} else {
|
if (shouldWakeOnReceivedMessage()) {
|
||||||
strcpy(banner, "Alert Received");
|
setOn(true);
|
||||||
|
forceDisplay();
|
||||||
}
|
}
|
||||||
screen->showSimpleBanner(banner, 3000);
|
|
||||||
} else if (!channel.settings.has_module_settings || !channel.settings.module_settings.is_muted) {
|
|
||||||
if (longName && longName[0]) {
|
|
||||||
#if defined(M5STACK_UNITC6L)
|
|
||||||
strcpy(banner, "New Message");
|
|
||||||
#else
|
|
||||||
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
// Build popup: title = message source name, content = message text (sanitized)
|
||||||
|
// Title
|
||||||
|
char titleBuf[64] = {0};
|
||||||
|
if (longName && longName[0]) {
|
||||||
|
// Sanitize sender name
|
||||||
|
std::string t = sanitizeString(longName);
|
||||||
|
strncpy(titleBuf, t.c_str(), sizeof(titleBuf) - 1);
|
||||||
} else {
|
} else {
|
||||||
strcpy(banner, "New Message");
|
strncpy(titleBuf, "Message", sizeof(titleBuf) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Content: payload bytes may not be null-terminated, remove ASCII_BELL and sanitize
|
||||||
|
char content[256] = {0};
|
||||||
|
{
|
||||||
|
std::string raw;
|
||||||
|
raw.reserve(packet->decoded.payload.size);
|
||||||
|
for (size_t i = 0; i < packet->decoded.payload.size; ++i) {
|
||||||
|
char c = msgRaw[i];
|
||||||
|
if (c == ASCII_BELL)
|
||||||
|
continue; // strip bell
|
||||||
|
raw.push_back(c);
|
||||||
|
}
|
||||||
|
std::string sanitized = sanitizeString(raw);
|
||||||
|
strncpy(content, sanitized.c_str(), sizeof(content) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationRenderer::showKeyboardMessagePopupWithTitle(titleBuf, content, 3000);
|
||||||
|
|
||||||
|
// Maintain existing buzzer behavior on M5 if applicable
|
||||||
#if defined(M5STACK_UNITC6L)
|
#if defined(M5STACK_UNITC6L)
|
||||||
screen->setOn(true);
|
|
||||||
screen->showSimpleBanner(banner, 1500);
|
|
||||||
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
|
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
|
||||||
(isAlert && moduleConfig.external_notification.alert_bell_buzzer) ||
|
(isAlert && moduleConfig.external_notification.alert_bell_buzzer) ||
|
||||||
(!isBroadcast(packet->to) && isToUs(packet))) {
|
(!isBroadcast(packet->to) && isToUs(packet))) {
|
||||||
// Beep if not in DIRECT_MSG_ONLY mode or if in DIRECT_MSG_ONLY mode and either
|
|
||||||
// - packet contains an alert and alert bell buzzer is enabled
|
|
||||||
// - packet is a non-broadcast that is addressed to this node
|
|
||||||
playLongBeep();
|
playLongBeep();
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
screen->showSimpleBanner(banner, 3000);
|
|
||||||
#endif
|
#endif
|
||||||
|
} else {
|
||||||
|
// No keyboard active: use regular banner flow, respecting mute settings
|
||||||
|
if (isAlert) {
|
||||||
|
if (longName && longName[0]) {
|
||||||
|
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
|
||||||
|
} else {
|
||||||
|
strcpy(banner, "Alert Received");
|
||||||
|
}
|
||||||
|
screen->showSimpleBanner(banner, 3000);
|
||||||
|
} else if (!channel.settings.has_module_settings || !channel.settings.module_settings.is_muted) {
|
||||||
|
if (longName && longName[0]) {
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
strcpy(banner, "New Message");
|
||||||
|
#else
|
||||||
|
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
strcpy(banner, "New Message");
|
||||||
|
}
|
||||||
|
#if defined(M5STACK_UNITC6L)
|
||||||
|
screen->setOn(true);
|
||||||
|
screen->showSimpleBanner(banner, 1500);
|
||||||
|
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
|
||||||
|
(isAlert && moduleConfig.external_notification.alert_bell_buzzer) ||
|
||||||
|
(!isBroadcast(packet->to) && isToUs(packet))) {
|
||||||
|
// Beep if not in DIRECT_MSG_ONLY mode or if in DIRECT_MSG_ONLY mode and either
|
||||||
|
// - packet contains an alert and alert bell buzzer is enabled
|
||||||
|
// - packet is a non-broadcast that is addressed to this node
|
||||||
|
playLongBeep();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
screen->showSimpleBanner(banner, 3000);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1654,6 +1689,12 @@ int Screen::handleInputEvent(const InputEvent *event)
|
|||||||
showPrevFrame();
|
showPrevFrame();
|
||||||
} else if (event->inputEvent == INPUT_BROKER_RIGHT || event->inputEvent == INPUT_BROKER_USER_PRESS) {
|
} else if (event->inputEvent == INPUT_BROKER_RIGHT || event->inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||||
showNextFrame();
|
showNextFrame();
|
||||||
|
} else if (event->inputEvent == INPUT_BROKER_UP_LONG) {
|
||||||
|
// Long press up button for fast frame switching
|
||||||
|
showPrevFrame();
|
||||||
|
} else if (event->inputEvent == INPUT_BROKER_DOWN_LONG) {
|
||||||
|
// Long press down button for fast frame switching
|
||||||
|
showNextFrame();
|
||||||
} else if (event->inputEvent == INPUT_BROKER_SELECT) {
|
} else if (event->inputEvent == INPUT_BROKER_SELECT) {
|
||||||
if (this->ui->getUiState()->currentFrame == framesetInfo.positions.home) {
|
if (this->ui->getUiState()->currentFrame == framesetInfo.positions.home) {
|
||||||
menuHandler::homeBaseMenu();
|
menuHandler::homeBaseMenu();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
#if USE_TFTDISPLAY
|
||||||
|
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
#include "platform/portduino/PortduinoGlue.h"
|
#include "platform/portduino/PortduinoGlue.h"
|
||||||
@@ -1138,9 +1139,6 @@ static LGFX *tft = nullptr;
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ST7789_CS) || defined(ST7796_CS) || defined(ILI9341_DRIVER) || \
|
|
||||||
defined(ILI9342_DRIVER) || defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST72xx_DE) || \
|
|
||||||
(ARCH_PORTDUINO && HAS_SCREEN != 0) || defined(HACKADAY_COMMUNICATOR)
|
|
||||||
#include "SPILock.h"
|
#include "SPILock.h"
|
||||||
#include "TFTDisplay.h"
|
#include "TFTDisplay.h"
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
@@ -1518,4 +1516,4 @@ bool TFTDisplay::connect()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif // USE_TFTDISPLAY
|
||||||
|
|||||||
@@ -506,6 +506,9 @@ void VirtualKeyboard::drawKey(OLEDDisplay *display, const VirtualKey &key, bool
|
|||||||
centeredTextY -= 1;
|
centeredTextY -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef MUZI_BASE // Correct issue with character vertical position on MUZI_BASE
|
||||||
|
centeredTextY -= 2;
|
||||||
|
#endif
|
||||||
display->drawString(textX, centeredTextY, keyText.c_str());
|
display->drawString(textX, centeredTextY, keyText.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -532,8 +532,10 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
|
|||||||
const int labelX = x;
|
const int labelX = x;
|
||||||
int barsOffset = (isHighResolution) ? 24 : 0;
|
int barsOffset = (isHighResolution) ? 24 : 0;
|
||||||
#ifdef USE_EINK
|
#ifdef USE_EINK
|
||||||
|
#ifndef T_DECK_PRO
|
||||||
barsOffset -= 12;
|
barsOffset -= 12;
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
#if defined(M5STACK_UNITC6L)
|
#if defined(M5STACK_UNITC6L)
|
||||||
const int barX = x + 45 + barsOffset;
|
const int barX = x + 45 + barsOffset;
|
||||||
#else
|
#else
|
||||||
@@ -574,7 +576,7 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
|
|||||||
#endif
|
#endif
|
||||||
// Value string
|
// Value string
|
||||||
display->setTextAlignment(TEXT_ALIGN_RIGHT);
|
display->setTextAlignment(TEXT_ALIGN_RIGHT);
|
||||||
display->drawString(SCREEN_WIDTH - 2, getTextPositions(display)[line], combinedStr);
|
display->drawString(SCREEN_WIDTH, getTextPositions(display)[line], combinedStr);
|
||||||
};
|
};
|
||||||
|
|
||||||
// === Memory values ===
|
// === Memory values ===
|
||||||
|
|||||||
@@ -13,18 +13,48 @@
|
|||||||
#include "input/RotaryEncoderInterruptImpl1.h"
|
#include "input/RotaryEncoderInterruptImpl1.h"
|
||||||
#include "input/UpDownInterruptImpl1.h"
|
#include "input/UpDownInterruptImpl1.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
#include "mesh/Default.h"
|
||||||
#include "mesh/MeshTypes.h"
|
#include "mesh/MeshTypes.h"
|
||||||
#include "modules/AdminModule.h"
|
#include "modules/AdminModule.h"
|
||||||
#include "modules/CannedMessageModule.h"
|
#include "modules/CannedMessageModule.h"
|
||||||
#include "modules/KeyVerificationModule.h"
|
#include "modules/KeyVerificationModule.h"
|
||||||
|
|
||||||
#include "modules/TraceRouteModule.h"
|
#include "modules/TraceRouteModule.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
extern uint16_t TFT_MESH;
|
extern uint16_t TFT_MESH;
|
||||||
|
|
||||||
namespace graphics
|
namespace graphics
|
||||||
{
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
// Caller must ensure the provided options array outlives the banner callback.
|
||||||
|
template <typename T, size_t N, typename Callback>
|
||||||
|
BannerOverlayOptions createStaticBannerOptions(const char *message, const MenuOption<T> (&options)[N],
|
||||||
|
std::array<const char *, N> &labels, Callback &&onSelection)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < N; ++i) {
|
||||||
|
labels[i] = options[i].label;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MenuOption<T> *optionsPtr = options;
|
||||||
|
auto callback = std::function<void(const MenuOption<T> &, int)>(std::forward<Callback>(onSelection));
|
||||||
|
|
||||||
|
BannerOverlayOptions bannerOptions;
|
||||||
|
bannerOptions.message = message;
|
||||||
|
bannerOptions.optionsArrayPtr = labels.data();
|
||||||
|
bannerOptions.optionsCount = static_cast<uint8_t>(N);
|
||||||
|
bannerOptions.bannerCallback = [optionsPtr, callback](int selected) -> void { callback(optionsPtr[selected], selected); };
|
||||||
|
return bannerOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
menuHandler::screenMenus menuHandler::menuQueue = menu_none;
|
menuHandler::screenMenus menuHandler::menuQueue = menu_none;
|
||||||
bool test_enabled = false;
|
bool test_enabled = false;
|
||||||
uint8_t test_count = 0;
|
uint8_t test_count = 0;
|
||||||
@@ -196,48 +226,38 @@ void menuHandler::DeviceRolePicker()
|
|||||||
|
|
||||||
void menuHandler::RadioPresetPicker()
|
void menuHandler::RadioPresetPicker()
|
||||||
{
|
{
|
||||||
static const char *optionsArray[] = {"Back", "LongSlow", "LongModerate", "LongFast", "MediumSlow",
|
static const RadioPresetOption presetOptions[] = {
|
||||||
"MediumFast", "ShortSlow", "ShortFast", "ShortTurbo"};
|
{"Back", OptionsAction::Back},
|
||||||
enum optionsNumbers {
|
{"LongTurbo", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO},
|
||||||
Back = 0,
|
{"LongModerate", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE},
|
||||||
radiopreset_LongSlow = 1,
|
{"LongFast", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST},
|
||||||
radiopreset_LongModerate = 2,
|
{"MediumSlow", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW},
|
||||||
radiopreset_LongFast = 3,
|
{"MediumFast", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST},
|
||||||
radiopreset_MediumSlow = 4,
|
{"ShortSlow", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW},
|
||||||
radiopreset_MediumFast = 5,
|
{"ShortFast", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST},
|
||||||
radiopreset_ShortSlow = 6,
|
{"ShortTurbo", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO},
|
||||||
radiopreset_ShortFast = 7,
|
|
||||||
radiopreset_ShortTurbo = 8
|
|
||||||
};
|
|
||||||
BannerOverlayOptions bannerOptions;
|
|
||||||
bannerOptions.message = "Radio Preset";
|
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
|
||||||
bannerOptions.optionsCount = 9;
|
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
|
||||||
if (selected == Back) {
|
|
||||||
menuHandler::menuQueue = menuHandler::lora_Menu;
|
|
||||||
screen->runNow();
|
|
||||||
return;
|
|
||||||
} else if (selected == radiopreset_LongSlow) {
|
|
||||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW;
|
|
||||||
} else if (selected == radiopreset_LongModerate) {
|
|
||||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE;
|
|
||||||
} else if (selected == radiopreset_LongFast) {
|
|
||||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST;
|
|
||||||
} else if (selected == radiopreset_MediumSlow) {
|
|
||||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW;
|
|
||||||
} else if (selected == radiopreset_MediumFast) {
|
|
||||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST;
|
|
||||||
} else if (selected == radiopreset_ShortSlow) {
|
|
||||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW;
|
|
||||||
} else if (selected == radiopreset_ShortFast) {
|
|
||||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST;
|
|
||||||
} else if (selected == radiopreset_ShortTurbo) {
|
|
||||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO;
|
|
||||||
}
|
|
||||||
service->reloadConfig(SEGMENT_CONFIG);
|
|
||||||
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr size_t presetCount = sizeof(presetOptions) / sizeof(presetOptions[0]);
|
||||||
|
static std::array<const char *, presetCount> presetLabels{};
|
||||||
|
|
||||||
|
auto bannerOptions =
|
||||||
|
createStaticBannerOptions("Radio Preset", presetOptions, presetLabels, [](const RadioPresetOption &option, int) -> void {
|
||||||
|
if (option.action == OptionsAction::Back) {
|
||||||
|
menuHandler::menuQueue = menuHandler::lora_Menu;
|
||||||
|
screen->runNow();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!option.hasValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.lora.modem_preset = option.value;
|
||||||
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
|
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
|
||||||
|
});
|
||||||
|
|
||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1040,12 +1060,13 @@ void menuHandler::switchToMUIMenu()
|
|||||||
|
|
||||||
void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
|
void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
|
||||||
{
|
{
|
||||||
static const char *optionsArray[] = {"Back", "Default", "Meshtastic Green", "Yellow", "Red", "Orange", "Purple", "Teal",
|
static const char *optionsArray[] = {
|
||||||
"Pink", "White"};
|
"Back", "Default", "Meshtastic Green", "Yellow", "Red", "Orange", "Purple", "Blue", "Teal", "Cyan", "Ice", "Pink",
|
||||||
|
"White", "Gray"};
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
bannerOptions.message = "Select Screen Color";
|
bannerOptions.message = "Select Screen Color";
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 10;
|
bannerOptions.optionsCount = 14;
|
||||||
bannerOptions.bannerCallback = [display](int selected) -> void {
|
bannerOptions.bannerCallback = [display](int selected) -> void {
|
||||||
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || \
|
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || \
|
||||||
HAS_TFT || defined(HACKADAY_COMMUNICATOR)
|
HAS_TFT || defined(HACKADAY_COMMUNICATOR)
|
||||||
@@ -1081,20 +1102,40 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
|
|||||||
TFT_MESH_g = 153;
|
TFT_MESH_g = 153;
|
||||||
TFT_MESH_b = 255;
|
TFT_MESH_b = 255;
|
||||||
} else if (selected == 7) {
|
} else if (selected == 7) {
|
||||||
LOG_INFO("Setting color to Teal");
|
LOG_INFO("Setting color to Blue");
|
||||||
TFT_MESH_r = 64;
|
TFT_MESH_r = 0;
|
||||||
TFT_MESH_g = 224;
|
TFT_MESH_g = 0;
|
||||||
TFT_MESH_b = 208;
|
TFT_MESH_b = 255;
|
||||||
} else if (selected == 8) {
|
} else if (selected == 8) {
|
||||||
|
LOG_INFO("Setting color to Teal");
|
||||||
|
TFT_MESH_r = 16;
|
||||||
|
TFT_MESH_g = 102;
|
||||||
|
TFT_MESH_b = 102;
|
||||||
|
} else if (selected == 9) {
|
||||||
|
LOG_INFO("Setting color to Cyan");
|
||||||
|
TFT_MESH_r = 0;
|
||||||
|
TFT_MESH_g = 255;
|
||||||
|
TFT_MESH_b = 255;
|
||||||
|
} else if (selected == 10) {
|
||||||
|
LOG_INFO("Setting color to Ice");
|
||||||
|
TFT_MESH_r = 173;
|
||||||
|
TFT_MESH_g = 216;
|
||||||
|
TFT_MESH_b = 230;
|
||||||
|
} else if (selected == 11) {
|
||||||
LOG_INFO("Setting color to Pink");
|
LOG_INFO("Setting color to Pink");
|
||||||
TFT_MESH_r = 255;
|
TFT_MESH_r = 255;
|
||||||
TFT_MESH_g = 105;
|
TFT_MESH_g = 105;
|
||||||
TFT_MESH_b = 180;
|
TFT_MESH_b = 180;
|
||||||
} else if (selected == 9) {
|
} else if (selected == 12) {
|
||||||
LOG_INFO("Setting color to White");
|
LOG_INFO("Setting color to White");
|
||||||
TFT_MESH_r = 255;
|
TFT_MESH_r = 255;
|
||||||
TFT_MESH_g = 255;
|
TFT_MESH_g = 255;
|
||||||
TFT_MESH_b = 255;
|
TFT_MESH_b = 255;
|
||||||
|
} else if (selected == 13) {
|
||||||
|
LOG_INFO("Setting color to Gray");
|
||||||
|
TFT_MESH_r = 128;
|
||||||
|
TFT_MESH_g = 128;
|
||||||
|
TFT_MESH_b = 128;
|
||||||
} else {
|
} else {
|
||||||
menuQueue = system_base_menu;
|
menuQueue = system_base_menu;
|
||||||
screen->runNow();
|
screen->runNow();
|
||||||
|
|||||||
@@ -99,5 +99,24 @@ class menuHandler
|
|||||||
static void BluetoothToggleMenu();
|
static void BluetoothToggleMenu();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Generic Menu Options designations */
|
||||||
|
enum class OptionsAction { Back, Select };
|
||||||
|
|
||||||
|
template <typename T> struct MenuOption {
|
||||||
|
const char *label;
|
||||||
|
OptionsAction action;
|
||||||
|
bool hasValue;
|
||||||
|
T value;
|
||||||
|
|
||||||
|
MenuOption(const char *labelIn, OptionsAction actionIn, T valueIn)
|
||||||
|
: label(labelIn), action(actionIn), hasValue(true), value(valueIn)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuOption(const char *labelIn, OptionsAction actionIn) : label(labelIn), action(actionIn), hasValue(false), value() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
using RadioPresetOption = MenuOption<meshtastic_Config_LoRaConfig_ModemPreset>;
|
||||||
|
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
#endif
|
#endif
|
||||||
@@ -85,9 +85,13 @@ void NotificationRenderer::drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiStat
|
|||||||
|
|
||||||
void NotificationRenderer::resetBanner()
|
void NotificationRenderer::resetBanner()
|
||||||
{
|
{
|
||||||
|
notificationTypeEnum previousType = current_notification_type;
|
||||||
|
|
||||||
alertBannerMessage[0] = '\0';
|
alertBannerMessage[0] = '\0';
|
||||||
current_notification_type = notificationTypeEnum::none;
|
current_notification_type = notificationTypeEnum::none;
|
||||||
|
|
||||||
|
OnScreenKeyboardModule::instance().clearPopup();
|
||||||
|
|
||||||
inEvent.inputEvent = INPUT_BROKER_NONE;
|
inEvent.inputEvent = INPUT_BROKER_NONE;
|
||||||
inEvent.kbchar = 0;
|
inEvent.kbchar = 0;
|
||||||
curSelected = 0;
|
curSelected = 0;
|
||||||
@@ -100,6 +104,13 @@ void NotificationRenderer::resetBanner()
|
|||||||
currentNumber = 0;
|
currentNumber = 0;
|
||||||
|
|
||||||
nodeDB->pause_sort(false);
|
nodeDB->pause_sort(false);
|
||||||
|
|
||||||
|
// If we're exiting from text_input (virtual keyboard), stop module and trigger frame update
|
||||||
|
// to ensure any messages received during keyboard use are now displayed
|
||||||
|
if (previousType == notificationTypeEnum::text_input && screen) {
|
||||||
|
OnScreenKeyboardModule::instance().stop(false);
|
||||||
|
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotificationRenderer::drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state)
|
void NotificationRenderer::drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state)
|
||||||
@@ -163,13 +174,15 @@ void NotificationRenderer::drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiS
|
|||||||
// modulo to extract
|
// modulo to extract
|
||||||
uint8_t this_digit = (currentNumber % (pow_of_10(numDigits - curSelected))) / (pow_of_10(numDigits - curSelected - 1));
|
uint8_t this_digit = (currentNumber % (pow_of_10(numDigits - curSelected))) / (pow_of_10(numDigits - curSelected - 1));
|
||||||
// Handle input
|
// Handle input
|
||||||
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS ||
|
||||||
|
inEvent.inputEvent == INPUT_BROKER_UP_LONG) {
|
||||||
if (this_digit == 9) {
|
if (this_digit == 9) {
|
||||||
currentNumber -= 9 * (pow_of_10(numDigits - curSelected - 1));
|
currentNumber -= 9 * (pow_of_10(numDigits - curSelected - 1));
|
||||||
} else {
|
} else {
|
||||||
currentNumber += (pow_of_10(numDigits - curSelected - 1));
|
currentNumber += (pow_of_10(numDigits - curSelected - 1));
|
||||||
}
|
}
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS ||
|
||||||
|
inEvent.inputEvent == INPUT_BROKER_DOWN_LONG) {
|
||||||
if (this_digit == 0) {
|
if (this_digit == 0) {
|
||||||
currentNumber += 9 * (pow_of_10(numDigits - curSelected - 1));
|
currentNumber += 9 * (pow_of_10(numDigits - curSelected - 1));
|
||||||
} else {
|
} else {
|
||||||
@@ -251,10 +264,10 @@ void NotificationRenderer::drawNodePicker(OLEDDisplay *display, OLEDDisplayUiSta
|
|||||||
|
|
||||||
// Handle input
|
// Handle input
|
||||||
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_LEFT ||
|
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_LEFT ||
|
||||||
inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
inEvent.inputEvent == INPUT_BROKER_ALT_PRESS || inEvent.inputEvent == INPUT_BROKER_UP_LONG) {
|
||||||
curSelected--;
|
curSelected--;
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_RIGHT ||
|
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_RIGHT ||
|
||||||
inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
inEvent.inputEvent == INPUT_BROKER_USER_PRESS || inEvent.inputEvent == INPUT_BROKER_DOWN_LONG) {
|
||||||
curSelected++;
|
curSelected++;
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
||||||
alertBannerCallback(selectedNodenum);
|
alertBannerCallback(selectedNodenum);
|
||||||
@@ -368,10 +381,10 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
|
|||||||
// Handle input
|
// Handle input
|
||||||
if (alertBannerOptions > 0) {
|
if (alertBannerOptions > 0) {
|
||||||
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_LEFT ||
|
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_LEFT ||
|
||||||
inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
inEvent.inputEvent == INPUT_BROKER_ALT_PRESS || inEvent.inputEvent == INPUT_BROKER_UP_LONG) {
|
||||||
curSelected--;
|
curSelected--;
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_RIGHT ||
|
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_RIGHT ||
|
||||||
inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
inEvent.inputEvent == INPUT_BROKER_USER_PRESS || inEvent.inputEvent == INPUT_BROKER_DOWN_LONG) {
|
||||||
curSelected++;
|
curSelected++;
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
||||||
if (optionsEnumPtr != nullptr) {
|
if (optionsEnumPtr != nullptr) {
|
||||||
@@ -769,40 +782,8 @@ void NotificationRenderer::drawTextInput(OLEDDisplay *display, OLEDDisplayUiStat
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (inEvent.inputEvent != INPUT_BROKER_NONE) {
|
if (inEvent.inputEvent != INPUT_BROKER_NONE) {
|
||||||
if (inEvent.inputEvent == INPUT_BROKER_UP) {
|
bool handled = OnScreenKeyboardModule::processVirtualKeyboardInput(inEvent, virtualKeyboard);
|
||||||
// high frequency for move cursor left/right than up/down with encoders
|
if (!handled && inEvent.inputEvent == INPUT_BROKER_CANCEL) {
|
||||||
extern ::RotaryEncoderInterruptImpl1 *rotaryEncoderInterruptImpl1;
|
|
||||||
extern ::UpDownInterruptImpl1 *upDownInterruptImpl1;
|
|
||||||
if (::rotaryEncoderInterruptImpl1 || ::upDownInterruptImpl1) {
|
|
||||||
virtualKeyboard->moveCursorLeft();
|
|
||||||
} else {
|
|
||||||
virtualKeyboard->moveCursorUp();
|
|
||||||
}
|
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN) {
|
|
||||||
extern ::RotaryEncoderInterruptImpl1 *rotaryEncoderInterruptImpl1;
|
|
||||||
extern ::UpDownInterruptImpl1 *upDownInterruptImpl1;
|
|
||||||
if (::rotaryEncoderInterruptImpl1 || ::upDownInterruptImpl1) {
|
|
||||||
virtualKeyboard->moveCursorRight();
|
|
||||||
} else {
|
|
||||||
virtualKeyboard->moveCursorDown();
|
|
||||||
}
|
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_LEFT) {
|
|
||||||
virtualKeyboard->moveCursorLeft();
|
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_RIGHT) {
|
|
||||||
virtualKeyboard->moveCursorRight();
|
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_UP_LONG) {
|
|
||||||
virtualKeyboard->moveCursorUp();
|
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN_LONG) {
|
|
||||||
virtualKeyboard->moveCursorDown();
|
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
|
||||||
virtualKeyboard->moveCursorLeft();
|
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
|
||||||
virtualKeyboard->moveCursorRight();
|
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
|
||||||
virtualKeyboard->handlePress();
|
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT_LONG) {
|
|
||||||
virtualKeyboard->handleLongPress();
|
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_CANCEL) {
|
|
||||||
auto callback = textInputCallback;
|
auto callback = textInputCallback;
|
||||||
delete virtualKeyboard;
|
delete virtualKeyboard;
|
||||||
virtualKeyboard = nullptr;
|
virtualKeyboard = nullptr;
|
||||||
@@ -821,12 +802,28 @@ void NotificationRenderer::drawTextInput(OLEDDisplay *display, OLEDDisplayUiStat
|
|||||||
inEvent.inputEvent = INPUT_BROKER_NONE;
|
inEvent.inputEvent = INPUT_BROKER_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-check pointer before drawing to avoid use-after-free and crashes
|
||||||
|
if (!virtualKeyboard) {
|
||||||
|
// Ensure we exit text_input state and restore frames
|
||||||
|
if (current_notification_type == notificationTypeEnum::text_input) {
|
||||||
|
resetBanner();
|
||||||
|
}
|
||||||
|
if (screen) {
|
||||||
|
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
|
||||||
|
}
|
||||||
|
// If screen is null, do nothing (safe fallback)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Clear the screen to avoid overlapping with underlying frames or overlays
|
// Clear the screen to avoid overlapping with underlying frames or overlays
|
||||||
display->setColor(BLACK);
|
display->setColor(BLACK);
|
||||||
display->fillRect(0, 0, display->getWidth(), display->getHeight());
|
display->fillRect(0, 0, display->getWidth(), display->getHeight());
|
||||||
display->setColor(WHITE);
|
display->setColor(WHITE);
|
||||||
// Draw the virtual keyboard
|
// Draw the virtual keyboard
|
||||||
virtualKeyboard->draw(display, 0, 0);
|
virtualKeyboard->draw(display, 0, 0);
|
||||||
|
|
||||||
|
// Draw transient popup overlay (if any) managed by OnScreenKeyboardModule
|
||||||
|
OnScreenKeyboardModule::instance().drawPopupOverlay(display);
|
||||||
} else {
|
} else {
|
||||||
// If virtualKeyboard is null, reset the banner to avoid getting stuck
|
// If virtualKeyboard is null, reset the banner to avoid getting stuck
|
||||||
LOG_INFO("Virtual keyboard is null - resetting banner");
|
LOG_INFO("Virtual keyboard is null - resetting banner");
|
||||||
@@ -839,5 +836,12 @@ bool NotificationRenderer::isOverlayBannerShowing()
|
|||||||
return strlen(alertBannerMessage) > 0 && (alertBannerUntil == 0 || millis() <= alertBannerUntil);
|
return strlen(alertBannerMessage) > 0 && (alertBannerUntil == 0 || millis() <= alertBannerUntil);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NotificationRenderer::showKeyboardMessagePopupWithTitle(const char *title, const char *content, uint32_t durationMs)
|
||||||
|
{
|
||||||
|
if (!title || !content || current_notification_type != notificationTypeEnum::text_input)
|
||||||
|
return;
|
||||||
|
OnScreenKeyboardModule::instance().showPopup(title, content, durationMs);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
#endif
|
#endif
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "OLEDDisplayUi.h"
|
#include "OLEDDisplayUi.h"
|
||||||
#include "graphics/Screen.h"
|
#include "graphics/Screen.h"
|
||||||
#include "graphics/VirtualKeyboard.h"
|
#include "graphics/VirtualKeyboard.h"
|
||||||
|
#include "modules/OnScreenKeyboardModule.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#define MAX_LINES 5
|
#define MAX_LINES 5
|
||||||
@@ -31,6 +32,7 @@ class NotificationRenderer
|
|||||||
static bool pauseBanner;
|
static bool pauseBanner;
|
||||||
|
|
||||||
static void resetBanner();
|
static void resetBanner();
|
||||||
|
static void showKeyboardMessagePopupWithTitle(const char *title, const char *content, uint32_t durationMs);
|
||||||
static void drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state);
|
static void drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state);
|
||||||
static void drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisplayUiState *state);
|
static void drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisplayUiState *state);
|
||||||
static void drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiState *state);
|
static void drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiState *state);
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ uint32_t InkHUD::AppletFont::toUtf32(std::string utf8)
|
|||||||
utf32 |= (utf8.at(3) & 0b00111111);
|
utf32 |= (utf8.at(3) & 0b00111111);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return utf32;
|
return utf32;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#ifdef MESHTASTIC_INCLUDE_INKHUD
|
#ifdef MESHTASTIC_INCLUDE_INKHUD
|
||||||
|
|
||||||
#include "./PositionsApplet.h"
|
#include "./PositionsApplet.h"
|
||||||
|
#include "NodeDB.h"
|
||||||
|
|
||||||
using namespace NicheGraphics;
|
using namespace NicheGraphics;
|
||||||
|
|
||||||
@@ -49,8 +50,8 @@ ProcessMessage InkHUD::PositionsApplet::handleReceived(const meshtastic_MeshPack
|
|||||||
if (!hasPosition)
|
if (!hasPosition)
|
||||||
return ProcessMessage::CONTINUE;
|
return ProcessMessage::CONTINUE;
|
||||||
|
|
||||||
bool hasHopsAway = (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start); // From NodeDB::updateFrom
|
const int8_t hopsAway = getHopsAway(mp);
|
||||||
uint8_t hopsAway = mp.hop_start - mp.hop_limit;
|
const bool hasHopsAway = hopsAway >= 0;
|
||||||
|
|
||||||
// Determine if the position packet would change anything on-screen
|
// Determine if the position packet would change anything on-screen
|
||||||
// -----------------------------------------------------------------
|
// -----------------------------------------------------------------
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ bool ButtonThread::initButton(const ButtonConfig &config)
|
|||||||
_activeLow = config.activeLow;
|
_activeLow = config.activeLow;
|
||||||
_touchQuirk = config.touchQuirk;
|
_touchQuirk = config.touchQuirk;
|
||||||
_intRoutine = config.intRoutine;
|
_intRoutine = config.intRoutine;
|
||||||
|
_pressHandler = config.onPress;
|
||||||
|
_releaseHandler = config.onRelease;
|
||||||
|
_suppressLeadUp = config.suppressLeadUpSound;
|
||||||
_longLongPress = config.longLongPress;
|
_longLongPress = config.longLongPress;
|
||||||
|
|
||||||
userButton = OneButton(config.pinNumber, config.activeLow, config.activePullup);
|
userButton = OneButton(config.pinNumber, config.activeLow, config.activePullup);
|
||||||
@@ -133,6 +136,8 @@ int32_t ButtonThread::runOnce()
|
|||||||
|
|
||||||
// Detect start of button press
|
// Detect start of button press
|
||||||
if (buttonCurrentlyPressed && !buttonWasPressed) {
|
if (buttonCurrentlyPressed && !buttonWasPressed) {
|
||||||
|
if (_pressHandler)
|
||||||
|
_pressHandler();
|
||||||
buttonPressStartTime = millis();
|
buttonPressStartTime = millis();
|
||||||
leadUpPlayed = false;
|
leadUpPlayed = false;
|
||||||
leadUpSequenceActive = false;
|
leadUpSequenceActive = false;
|
||||||
@@ -140,7 +145,7 @@ int32_t ButtonThread::runOnce()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Progressive lead-up sound system
|
// Progressive lead-up sound system
|
||||||
if (buttonCurrentlyPressed && (millis() - buttonPressStartTime) >= BUTTON_LEADUP_MS) {
|
if (!_suppressLeadUp && buttonCurrentlyPressed && (millis() - buttonPressStartTime) >= BUTTON_LEADUP_MS) {
|
||||||
|
|
||||||
// Start the progressive sequence if not already active
|
// Start the progressive sequence if not already active
|
||||||
if (!leadUpSequenceActive) {
|
if (!leadUpSequenceActive) {
|
||||||
@@ -160,6 +165,8 @@ int32_t ButtonThread::runOnce()
|
|||||||
|
|
||||||
// Reset when button is released
|
// Reset when button is released
|
||||||
if (!buttonCurrentlyPressed && buttonWasPressed) {
|
if (!buttonCurrentlyPressed && buttonWasPressed) {
|
||||||
|
if (_releaseHandler)
|
||||||
|
_releaseHandler();
|
||||||
leadUpSequenceActive = false;
|
leadUpSequenceActive = false;
|
||||||
resetLeadUpSequence();
|
resetLeadUpSequence();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ struct ButtonConfig {
|
|||||||
bool activePullup = true;
|
bool activePullup = true;
|
||||||
uint32_t pullupSense = 0;
|
uint32_t pullupSense = 0;
|
||||||
voidFuncPtr intRoutine = nullptr;
|
voidFuncPtr intRoutine = nullptr;
|
||||||
|
voidFuncPtr onPress = nullptr; // Optional edge callbacks
|
||||||
|
voidFuncPtr onRelease = nullptr; // Optional edge callbacks
|
||||||
|
bool suppressLeadUpSound = false;
|
||||||
input_broker_event singlePress = INPUT_BROKER_NONE;
|
input_broker_event singlePress = INPUT_BROKER_NONE;
|
||||||
input_broker_event longPress = INPUT_BROKER_NONE;
|
input_broker_event longPress = INPUT_BROKER_NONE;
|
||||||
uint16_t longPressTime = 500;
|
uint16_t longPressTime = 500;
|
||||||
@@ -94,6 +97,9 @@ class ButtonThread : public Observable<const InputEvent *>, public concurrency::
|
|||||||
input_broker_event _shortLong = INPUT_BROKER_NONE;
|
input_broker_event _shortLong = INPUT_BROKER_NONE;
|
||||||
|
|
||||||
voidFuncPtr _intRoutine = nullptr;
|
voidFuncPtr _intRoutine = nullptr;
|
||||||
|
voidFuncPtr _pressHandler = nullptr;
|
||||||
|
voidFuncPtr _releaseHandler = nullptr;
|
||||||
|
bool _suppressLeadUp = false;
|
||||||
uint16_t _longPressTime = 500;
|
uint16_t _longPressTime = 500;
|
||||||
uint16_t _longLongPressTime = 3900;
|
uint16_t _longLongPressTime = 3900;
|
||||||
int _pinNum = 0;
|
int _pinNum = 0;
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ typedef struct _InputEvent {
|
|||||||
class InputPollable
|
class InputPollable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
virtual ~InputPollable() = default;
|
||||||
virtual void pollOnce() = 0;
|
virtual void pollOnce() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
#include "RotaryEncoderImpl.h"
|
#include "RotaryEncoderImpl.h"
|
||||||
#include "InputBroker.h"
|
#include "InputBroker.h"
|
||||||
#include "RotaryEncoder.h"
|
#include "RotaryEncoder.h"
|
||||||
|
#ifdef ARCH_ESP32
|
||||||
|
#include "sleep.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#define ORIGIN_NAME "RotaryEncoder"
|
#define ORIGIN_NAME "RotaryEncoder"
|
||||||
|
|
||||||
@@ -11,6 +14,20 @@ RotaryEncoderImpl *rotaryEncoderImpl;
|
|||||||
RotaryEncoderImpl::RotaryEncoderImpl()
|
RotaryEncoderImpl::RotaryEncoderImpl()
|
||||||
{
|
{
|
||||||
rotary = nullptr;
|
rotary = nullptr;
|
||||||
|
#ifdef ARCH_ESP32
|
||||||
|
isFirstInit = true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
RotaryEncoderImpl::~RotaryEncoderImpl()
|
||||||
|
{
|
||||||
|
LOG_DEBUG("RotaryEncoderImpl destructor");
|
||||||
|
detachRotaryEncoderInterrupts();
|
||||||
|
|
||||||
|
if (rotary != nullptr) {
|
||||||
|
delete rotary;
|
||||||
|
rotary = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RotaryEncoderImpl::init()
|
bool RotaryEncoderImpl::init()
|
||||||
@@ -25,15 +42,22 @@ bool RotaryEncoderImpl::init()
|
|||||||
eventCcw = static_cast<input_broker_event>(moduleConfig.canned_message.inputbroker_event_ccw);
|
eventCcw = static_cast<input_broker_event>(moduleConfig.canned_message.inputbroker_event_ccw);
|
||||||
eventPressed = static_cast<input_broker_event>(moduleConfig.canned_message.inputbroker_event_press);
|
eventPressed = static_cast<input_broker_event>(moduleConfig.canned_message.inputbroker_event_press);
|
||||||
|
|
||||||
rotary = new RotaryEncoder(moduleConfig.canned_message.inputbroker_pin_a, moduleConfig.canned_message.inputbroker_pin_b,
|
if (rotary == nullptr) {
|
||||||
moduleConfig.canned_message.inputbroker_pin_press);
|
rotary = new RotaryEncoder(moduleConfig.canned_message.inputbroker_pin_a, moduleConfig.canned_message.inputbroker_pin_b,
|
||||||
rotary->resetButton();
|
moduleConfig.canned_message.inputbroker_pin_press);
|
||||||
|
}
|
||||||
|
|
||||||
interruptInstance = this;
|
attachRotaryEncoderInterrupts();
|
||||||
auto interruptHandler = []() { inputBroker->requestPollSoon(interruptInstance); };
|
|
||||||
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_a, interruptHandler, CHANGE);
|
#ifdef ARCH_ESP32
|
||||||
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_b, interruptHandler, CHANGE);
|
// Register callbacks for before and after lightsleep
|
||||||
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_press, interruptHandler, CHANGE);
|
// Used to detach and reattach interrupts
|
||||||
|
if (isFirstInit) {
|
||||||
|
lsObserver.observe(¬ifyLightSleep);
|
||||||
|
lsEndObserver.observe(¬ifyLightSleepEnd);
|
||||||
|
isFirstInit = false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
LOG_INFO("RotaryEncoder initialized pins(%d, %d, %d), events(%d, %d, %d)", moduleConfig.canned_message.inputbroker_pin_a,
|
LOG_INFO("RotaryEncoder initialized pins(%d, %d, %d), events(%d, %d, %d)", moduleConfig.canned_message.inputbroker_pin_a,
|
||||||
moduleConfig.canned_message.inputbroker_pin_b, moduleConfig.canned_message.inputbroker_pin_press, eventCw, eventCcw,
|
moduleConfig.canned_message.inputbroker_pin_b, moduleConfig.canned_message.inputbroker_pin_press, eventCw, eventCcw,
|
||||||
@@ -71,6 +95,50 @@ void RotaryEncoderImpl::pollOnce()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RotaryEncoderImpl::detachRotaryEncoderInterrupts()
|
||||||
|
{
|
||||||
|
LOG_DEBUG("RotaryEncoderImpl detach button interrupts");
|
||||||
|
if (interruptInstance == this) {
|
||||||
|
detachInterrupt(moduleConfig.canned_message.inputbroker_pin_a);
|
||||||
|
detachInterrupt(moduleConfig.canned_message.inputbroker_pin_b);
|
||||||
|
detachInterrupt(moduleConfig.canned_message.inputbroker_pin_press);
|
||||||
|
interruptInstance = nullptr;
|
||||||
|
} else {
|
||||||
|
LOG_WARN("RotaryEncoderImpl: interrupts already detached");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RotaryEncoderImpl::attachRotaryEncoderInterrupts()
|
||||||
|
{
|
||||||
|
LOG_DEBUG("RotaryEncoderImpl attach button interrupts");
|
||||||
|
if (rotary != nullptr && interruptInstance == nullptr) {
|
||||||
|
rotary->resetButton();
|
||||||
|
|
||||||
|
interruptInstance = this;
|
||||||
|
auto interruptHandler = []() { inputBroker->requestPollSoon(interruptInstance); };
|
||||||
|
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_a, interruptHandler, CHANGE);
|
||||||
|
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_b, interruptHandler, CHANGE);
|
||||||
|
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_press, interruptHandler, CHANGE);
|
||||||
|
} else {
|
||||||
|
LOG_WARN("RotaryEncoderImpl: interrupts already attached");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ARCH_ESP32
|
||||||
|
|
||||||
|
int RotaryEncoderImpl::beforeLightSleep(void *unused)
|
||||||
|
{
|
||||||
|
detachRotaryEncoderInterrupts();
|
||||||
|
return 0; // Indicates success;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RotaryEncoderImpl::afterLightSleep(esp_sleep_wakeup_cause_t cause)
|
||||||
|
{
|
||||||
|
attachRotaryEncoderInterrupts();
|
||||||
|
return 0; // Indicates success;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
RotaryEncoderImpl *RotaryEncoderImpl::interruptInstance;
|
RotaryEncoderImpl *RotaryEncoderImpl::interruptInstance;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -8,12 +8,18 @@
|
|||||||
|
|
||||||
class RotaryEncoder;
|
class RotaryEncoder;
|
||||||
|
|
||||||
class RotaryEncoderImpl : public InputPollable
|
class RotaryEncoderImpl final : public InputPollable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RotaryEncoderImpl();
|
RotaryEncoderImpl();
|
||||||
bool init(void);
|
~RotaryEncoderImpl() override;
|
||||||
|
bool init();
|
||||||
virtual void pollOnce() override;
|
virtual void pollOnce() override;
|
||||||
|
// Disconnect and reconnect interrupts for light sleep
|
||||||
|
#ifdef ARCH_ESP32
|
||||||
|
int beforeLightSleep(void *unused);
|
||||||
|
int afterLightSleep(esp_sleep_wakeup_cause_t cause);
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static RotaryEncoderImpl *interruptInstance;
|
static RotaryEncoderImpl *interruptInstance;
|
||||||
@@ -23,6 +29,21 @@ class RotaryEncoderImpl : public InputPollable
|
|||||||
input_broker_event eventPressed = INPUT_BROKER_NONE;
|
input_broker_event eventPressed = INPUT_BROKER_NONE;
|
||||||
|
|
||||||
RotaryEncoder *rotary;
|
RotaryEncoder *rotary;
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef ARCH_ESP32
|
||||||
|
bool isFirstInit;
|
||||||
|
#endif
|
||||||
|
void detachRotaryEncoderInterrupts();
|
||||||
|
void attachRotaryEncoderInterrupts();
|
||||||
|
|
||||||
|
#ifdef ARCH_ESP32
|
||||||
|
// Get notified when lightsleep begins and ends
|
||||||
|
CallbackObserver<RotaryEncoderImpl, void *> lsObserver =
|
||||||
|
CallbackObserver<RotaryEncoderImpl, void *>(this, &RotaryEncoderImpl::beforeLightSleep);
|
||||||
|
CallbackObserver<RotaryEncoderImpl, esp_sleep_wakeup_cause_t> lsEndObserver =
|
||||||
|
CallbackObserver<RotaryEncoderImpl, esp_sleep_wakeup_cause_t>(this, &RotaryEncoderImpl::afterLightSleep);
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
extern RotaryEncoderImpl *rotaryEncoderImpl;
|
extern RotaryEncoderImpl *rotaryEncoderImpl;
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ bool RotaryEncoderInterruptImpl1::init()
|
|||||||
RotaryEncoderInterruptImpl1::handleIntA, RotaryEncoderInterruptImpl1::handleIntB,
|
RotaryEncoderInterruptImpl1::handleIntA, RotaryEncoderInterruptImpl1::handleIntB,
|
||||||
RotaryEncoderInterruptImpl1::handleIntPressed);
|
RotaryEncoderInterruptImpl1::handleIntPressed);
|
||||||
inputBroker->registerSource(this);
|
inputBroker->registerSource(this);
|
||||||
|
#ifndef HAS_PHYSICAL_KEYBOARD
|
||||||
osk_found = true;
|
osk_found = true;
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,9 @@ void TrackballInterruptBase::init(uint8_t pinDown, uint8_t pinUp, uint8_t pinLef
|
|||||||
|
|
||||||
LOG_DEBUG("Trackball GPIO initialized - UP:%d DOWN:%d LEFT:%d RIGHT:%d PRESS:%d", this->_pinUp, this->_pinDown,
|
LOG_DEBUG("Trackball GPIO initialized - UP:%d DOWN:%d LEFT:%d RIGHT:%d PRESS:%d", this->_pinUp, this->_pinDown,
|
||||||
this->_pinLeft, this->_pinRight, pinPress);
|
this->_pinLeft, this->_pinRight, pinPress);
|
||||||
|
#ifndef HAS_PHYSICAL_KEYBOARD
|
||||||
osk_found = true;
|
osk_found = true;
|
||||||
|
#endif
|
||||||
this->setInterval(100);
|
this->setInterval(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,6 +90,50 @@ int32_t TrackballInterruptBase::runOnce()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (directionDetected && directionStartTime > 0) {
|
||||||
|
uint32_t directionDuration = millis() - directionStartTime;
|
||||||
|
uint8_t directionPressedNow = 0;
|
||||||
|
directionInterval++;
|
||||||
|
|
||||||
|
if (!digitalRead(_pinUp)) {
|
||||||
|
directionPressedNow = TB_ACTION_UP;
|
||||||
|
} else if (!digitalRead(_pinDown)) {
|
||||||
|
directionPressedNow = TB_ACTION_DOWN;
|
||||||
|
} else if (!digitalRead(_pinLeft)) {
|
||||||
|
directionPressedNow = TB_ACTION_LEFT;
|
||||||
|
} else if (!digitalRead(_pinRight)) {
|
||||||
|
directionPressedNow = TB_ACTION_RIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t DIRECTION_REPEAT_THRESHOLD = 3;
|
||||||
|
|
||||||
|
if (directionPressedNow == TB_ACTION_NONE) {
|
||||||
|
// Reset state
|
||||||
|
directionDetected = false;
|
||||||
|
directionStartTime = 0;
|
||||||
|
directionInterval = 0;
|
||||||
|
this->action = TB_ACTION_NONE;
|
||||||
|
} else if (directionDuration >= LONG_PRESS_DURATION && directionInterval >= DIRECTION_REPEAT_THRESHOLD) {
|
||||||
|
// repeat event when long press these direction.
|
||||||
|
switch (directionPressedNow) {
|
||||||
|
case TB_ACTION_UP:
|
||||||
|
e.inputEvent = this->_eventUp;
|
||||||
|
break;
|
||||||
|
case TB_ACTION_DOWN:
|
||||||
|
e.inputEvent = this->_eventDown;
|
||||||
|
break;
|
||||||
|
case TB_ACTION_LEFT:
|
||||||
|
e.inputEvent = this->_eventLeft;
|
||||||
|
break;
|
||||||
|
case TB_ACTION_RIGHT:
|
||||||
|
e.inputEvent = this->_eventRight;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
directionInterval = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(T_DECK) // T-deck gets a super-simple debounce on trackball
|
#if defined(T_DECK) // T-deck gets a super-simple debounce on trackball
|
||||||
if (this->action == TB_ACTION_PRESSED && !pressDetected) {
|
if (this->action == TB_ACTION_PRESSED && !pressDetected) {
|
||||||
// Start long press detection
|
// Start long press detection
|
||||||
@@ -113,17 +159,22 @@ int32_t TrackballInterruptBase::runOnce()
|
|||||||
pressDetected = true;
|
pressDetected = true;
|
||||||
pressStartTime = millis();
|
pressStartTime = millis();
|
||||||
// Don't send event yet, wait to see if it's a long press
|
// Don't send event yet, wait to see if it's a long press
|
||||||
} else if (this->action == TB_ACTION_UP && !digitalRead(_pinUp)) {
|
} else if (this->action == TB_ACTION_UP && !digitalRead(_pinUp) && !directionDetected) {
|
||||||
// LOG_DEBUG("Trackball event UP");
|
directionDetected = true;
|
||||||
|
directionStartTime = millis();
|
||||||
e.inputEvent = this->_eventUp;
|
e.inputEvent = this->_eventUp;
|
||||||
} else if (this->action == TB_ACTION_DOWN && !digitalRead(_pinDown)) {
|
// send event first,will automatically trigger every 50ms * 3 after 500ms
|
||||||
// LOG_DEBUG("Trackball event DOWN");
|
} else if (this->action == TB_ACTION_DOWN && !digitalRead(_pinDown) && !directionDetected) {
|
||||||
|
directionDetected = true;
|
||||||
|
directionStartTime = millis();
|
||||||
e.inputEvent = this->_eventDown;
|
e.inputEvent = this->_eventDown;
|
||||||
} else if (this->action == TB_ACTION_LEFT && !digitalRead(_pinLeft)) {
|
} else if (this->action == TB_ACTION_LEFT && !digitalRead(_pinLeft) && !directionDetected) {
|
||||||
// LOG_DEBUG("Trackball event LEFT");
|
directionDetected = true;
|
||||||
|
directionStartTime = millis();
|
||||||
e.inputEvent = this->_eventLeft;
|
e.inputEvent = this->_eventLeft;
|
||||||
} else if (this->action == TB_ACTION_RIGHT && !digitalRead(_pinRight)) {
|
} else if (this->action == TB_ACTION_RIGHT && !digitalRead(_pinRight) && !directionDetected) {
|
||||||
// LOG_DEBUG("Trackball event RIGHT");
|
directionDetected = true;
|
||||||
|
directionStartTime = millis();
|
||||||
e.inputEvent = this->_eventRight;
|
e.inputEvent = this->_eventRight;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -49,10 +49,14 @@ class TrackballInterruptBase : public Observable<const InputEvent *>, public con
|
|||||||
|
|
||||||
// Long press detection for press button
|
// Long press detection for press button
|
||||||
uint32_t pressStartTime = 0;
|
uint32_t pressStartTime = 0;
|
||||||
|
uint32_t directionStartTime = 0;
|
||||||
|
uint8_t directionInterval = 0;
|
||||||
bool pressDetected = false;
|
bool pressDetected = false;
|
||||||
|
bool directionDetected = false;
|
||||||
uint32_t lastLongPressEventTime = 0;
|
uint32_t lastLongPressEventTime = 0;
|
||||||
|
uint32_t lastDirectionPressEventTime = 0;
|
||||||
static const uint32_t LONG_PRESS_DURATION = 500; // ms
|
static const uint32_t LONG_PRESS_DURATION = 500; // ms
|
||||||
static const uint32_t LONG_PRESS_REPEAT_INTERVAL = 500; // ms - interval between repeated long press events
|
static const uint32_t LONG_PRESS_REPEAT_INTERVAL = 300; // ms - interval between repeated long press events
|
||||||
|
|
||||||
private:
|
private:
|
||||||
input_broker_event _eventDown = INPUT_BROKER_NONE;
|
input_broker_event _eventDown = INPUT_BROKER_NONE;
|
||||||
|
|||||||
@@ -3,6 +3,14 @@
|
|||||||
#include "InputBroker.h"
|
#include "InputBroker.h"
|
||||||
#include "mesh/NodeDB.h"
|
#include "mesh/NodeDB.h"
|
||||||
|
|
||||||
|
#ifndef UPDOWN_LONG_PRESS_DURATION
|
||||||
|
#define UPDOWN_LONG_PRESS_DURATION 300
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef UPDOWN_LONG_PRESS_REPEAT_INTERVAL
|
||||||
|
#define UPDOWN_LONG_PRESS_REPEAT_INTERVAL 300
|
||||||
|
#endif
|
||||||
|
|
||||||
class UpDownInterruptBase : public Observable<const InputEvent *>, public concurrency::OSThread
|
class UpDownInterruptBase : public Observable<const InputEvent *>, public concurrency::OSThread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -40,8 +48,8 @@ class UpDownInterruptBase : public Observable<const InputEvent *>, public concur
|
|||||||
uint32_t lastPressLongEventTime = 0;
|
uint32_t lastPressLongEventTime = 0;
|
||||||
uint32_t lastUpLongEventTime = 0;
|
uint32_t lastUpLongEventTime = 0;
|
||||||
uint32_t lastDownLongEventTime = 0;
|
uint32_t lastDownLongEventTime = 0;
|
||||||
static const uint32_t LONG_PRESS_DURATION = 300;
|
static const uint32_t LONG_PRESS_DURATION = UPDOWN_LONG_PRESS_DURATION;
|
||||||
static const uint32_t LONG_PRESS_REPEAT_INTERVAL = 300;
|
static const uint32_t LONG_PRESS_REPEAT_INTERVAL = UPDOWN_LONG_PRESS_REPEAT_INTERVAL;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t _pinDown = 0;
|
uint8_t _pinDown = 0;
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ bool UpDownInterruptImpl1::init()
|
|||||||
eventDownLong, UpDownInterruptImpl1::handleIntDown, UpDownInterruptImpl1::handleIntUp,
|
eventDownLong, UpDownInterruptImpl1::handleIntDown, UpDownInterruptImpl1::handleIntUp,
|
||||||
UpDownInterruptImpl1::handleIntPressed);
|
UpDownInterruptImpl1::handleIntPressed);
|
||||||
inputBroker->registerSource(this);
|
inputBroker->registerSource(this);
|
||||||
|
#ifndef HAS_PHYSICAL_KEYBOARD
|
||||||
osk_found = true;
|
osk_found = true;
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
43
src/main.cpp
43
src/main.cpp
@@ -107,6 +107,10 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr;
|
|||||||
|
|
||||||
#if defined(BUTTON_PIN_TOUCH)
|
#if defined(BUTTON_PIN_TOUCH)
|
||||||
ButtonThread *TouchButtonThread = nullptr;
|
ButtonThread *TouchButtonThread = nullptr;
|
||||||
|
#if defined(TTGO_T_ECHO_PLUS) && defined(PIN_EINK_EN)
|
||||||
|
static bool touchBacklightWasOn = false;
|
||||||
|
static bool touchBacklightActive = false;
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO)
|
#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO)
|
||||||
@@ -205,7 +209,7 @@ ScanI2C::FoundDevice rgb_found = ScanI2C::FoundDevice(ScanI2C::DeviceType::NONE,
|
|||||||
/// The I2C address of our Air Quality Indicator (if found)
|
/// The I2C address of our Air Quality Indicator (if found)
|
||||||
ScanI2C::DeviceAddress aqi_found = ScanI2C::ADDRESS_NONE;
|
ScanI2C::DeviceAddress aqi_found = ScanI2C::ADDRESS_NONE;
|
||||||
|
|
||||||
#if defined(T_WATCH_S3) || defined(T_LORA_PAGER)
|
#ifdef HAS_DRV2605
|
||||||
Adafruit_DRV2605 drv;
|
Adafruit_DRV2605 drv;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -439,6 +443,13 @@ void setup()
|
|||||||
|
|
||||||
LOG_INFO("\n\n//\\ E S H T /\\ S T / C\n");
|
LOG_INFO("\n\n//\\ E S H T /\\ S T / C\n");
|
||||||
|
|
||||||
|
#if defined(ARCH_ESP32) && defined(BOARD_HAS_PSRAM)
|
||||||
|
#ifndef SENSECAP_INDICATOR
|
||||||
|
// use PSRAM for malloc calls > 256 bytes
|
||||||
|
heap_caps_malloc_extmem_enable(256);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(DEBUG_MUTE) && defined(DEBUG_PORT)
|
#if defined(DEBUG_MUTE) && defined(DEBUG_PORT)
|
||||||
DEBUG_PORT.printf("\r\n\r\n//\\ E S H T /\\ S T / C\r\n");
|
DEBUG_PORT.printf("\r\n\r\n//\\ E S H T /\\ S T / C\r\n");
|
||||||
DEBUG_PORT.printf("Version %s for %s from %s\r\n", optstr(APP_VERSION), optstr(APP_ENV), optstr(APP_REPO));
|
DEBUG_PORT.printf("Version %s for %s from %s\r\n", optstr(APP_VERSION), optstr(APP_ENV), optstr(APP_REPO));
|
||||||
@@ -781,7 +792,6 @@ void setup()
|
|||||||
// We do this as early as possible because this loads preferences from flash
|
// We do this as early as possible because this loads preferences from flash
|
||||||
// but we need to do this after main cpu init (esp32setup), because we need the random seed set
|
// but we need to do this after main cpu init (esp32setup), because we need the random seed set
|
||||||
nodeDB = new NodeDB;
|
nodeDB = new NodeDB;
|
||||||
|
|
||||||
#if HAS_TFT
|
#if HAS_TFT
|
||||||
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||||
tftSetup();
|
tftSetup();
|
||||||
@@ -827,7 +837,12 @@ void setup()
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(T_WATCH_S3) || defined(T_LORA_PAGER)
|
#ifdef HAS_DRV2605
|
||||||
|
#if defined(PIN_DRV_EN)
|
||||||
|
pinMode(PIN_DRV_EN, OUTPUT);
|
||||||
|
digitalWrite(PIN_DRV_EN, HIGH);
|
||||||
|
delay(10);
|
||||||
|
#endif
|
||||||
drv.begin();
|
drv.begin();
|
||||||
drv.selectLibrary(1);
|
drv.selectLibrary(1);
|
||||||
// I2C trigger by sending 'go' command
|
// I2C trigger by sending 'go' command
|
||||||
@@ -863,7 +878,7 @@ void setup()
|
|||||||
SPI.begin();
|
SPI.begin();
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
// ESP32
|
// ESP32
|
||||||
#if defined(HW_SPI1_DEVICE)
|
#if defined(HW_SPI1_DEVICE)
|
||||||
SPI1.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
|
SPI1.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
|
||||||
LOG_DEBUG("SPI1.begin(SCK=%d, MISO=%d, MOSI=%d, NSS=%d)", LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
|
LOG_DEBUG("SPI1.begin(SCK=%d, MISO=%d, MOSI=%d, NSS=%d)", LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
|
||||||
@@ -1032,6 +1047,24 @@ void setup()
|
|||||||
};
|
};
|
||||||
touchConfig.singlePress = INPUT_BROKER_NONE;
|
touchConfig.singlePress = INPUT_BROKER_NONE;
|
||||||
touchConfig.longPress = INPUT_BROKER_BACK;
|
touchConfig.longPress = INPUT_BROKER_BACK;
|
||||||
|
#if defined(TTGO_T_ECHO_PLUS) && defined(PIN_EINK_EN)
|
||||||
|
// On T-Echo Plus the touch pad should only drive the backlight, not UI navigation/sounds
|
||||||
|
touchConfig.longPress = INPUT_BROKER_NONE;
|
||||||
|
touchConfig.suppressLeadUpSound = true;
|
||||||
|
touchConfig.onPress = []() {
|
||||||
|
touchBacklightWasOn = uiconfig.screen_brightness == 1;
|
||||||
|
if (!touchBacklightWasOn) {
|
||||||
|
digitalWrite(PIN_EINK_EN, HIGH);
|
||||||
|
}
|
||||||
|
touchBacklightActive = true;
|
||||||
|
};
|
||||||
|
touchConfig.onRelease = []() {
|
||||||
|
if (touchBacklightActive && !touchBacklightWasOn) {
|
||||||
|
digitalWrite(PIN_EINK_EN, LOW);
|
||||||
|
}
|
||||||
|
touchBacklightActive = false;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
TouchButtonThread->initButton(touchConfig);
|
TouchButtonThread->initButton(touchConfig);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1451,8 +1484,10 @@ void setup()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HAS_TRACKBALL) || (defined(INPUTDRIVER_ENCODER_TYPE) && INPUTDRIVER_ENCODER_TYPE == 2)
|
#if defined(HAS_TRACKBALL) || (defined(INPUTDRIVER_ENCODER_TYPE) && INPUTDRIVER_ENCODER_TYPE == 2)
|
||||||
|
#ifndef HAS_PHYSICAL_KEYBOARD
|
||||||
osk_found = true;
|
osk_found = true;
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WEBSERVER
|
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WEBSERVER
|
||||||
// Start web server thread.
|
// Start web server thread.
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ extern bool eink_found;
|
|||||||
extern bool pmu_found;
|
extern bool pmu_found;
|
||||||
extern bool isUSBPowered;
|
extern bool isUSBPowered;
|
||||||
|
|
||||||
#if defined(T_WATCH_S3) || defined(T_LORA_PAGER)
|
#ifdef HAS_DRV2605
|
||||||
#include <Adafruit_DRV2605.h>
|
#include <Adafruit_DRV2605.h>
|
||||||
extern Adafruit_DRV2605 drv;
|
extern Adafruit_DRV2605 drv;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -57,14 +57,7 @@ class Default
|
|||||||
// Note: Kept as uint32_t to match the public API parameter type
|
// Note: Kept as uint32_t to match the public API parameter type
|
||||||
static float congestionScalingCoefficient(uint32_t numOnlineNodes)
|
static float congestionScalingCoefficient(uint32_t numOnlineNodes)
|
||||||
{
|
{
|
||||||
// Increase frequency of broadcasts for small networks regardless of preset
|
if (numOnlineNodes <= 40) {
|
||||||
if (numOnlineNodes <= 10) {
|
|
||||||
return 0.6;
|
|
||||||
} else if (numOnlineNodes <= 20) {
|
|
||||||
return 0.7;
|
|
||||||
} else if (numOnlineNodes <= 30) {
|
|
||||||
return 0.8;
|
|
||||||
} else if (numOnlineNodes <= 40) {
|
|
||||||
return 1.0;
|
return 1.0;
|
||||||
} else {
|
} else {
|
||||||
float throttlingFactor = 0.075;
|
float throttlingFactor = 0.075;
|
||||||
|
|||||||
@@ -124,6 +124,10 @@ void FloodingRouter::perhapsCancelDupe(const meshtastic_MeshPacket *p)
|
|||||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_LATE && iface) {
|
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_LATE && iface) {
|
||||||
iface->clampToLateRebroadcastWindow(getFrom(p), p->id);
|
iface->clampToLateRebroadcastWindow(getFrom(p), p->id);
|
||||||
}
|
}
|
||||||
|
if (config.device.role == meshtastic_Config_DeviceConfig_Role_CLIENT_BASE && iface && nodeDB &&
|
||||||
|
nodeDB->isFromOrToFavoritedNode(*p)) {
|
||||||
|
iface->clampToLateRebroadcastWindow(getFrom(p), p->id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FloodingRouter::isRebroadcaster()
|
bool FloodingRouter::isRebroadcaster()
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src)
|
|||||||
// but opted NOT TO. Because it is not a good idea to let remote nodes 'probe' to find out which PSKs were "good" vs
|
// but opted NOT TO. Because it is not a good idea to let remote nodes 'probe' to find out which PSKs were "good" vs
|
||||||
// bad.
|
// bad.
|
||||||
routingModule->sendAckNak(meshtastic_Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel,
|
routingModule->sendAckNak(meshtastic_Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel,
|
||||||
routingModule->getHopLimitForResponse(mp.hop_start, mp.hop_limit));
|
routingModule->getHopLimitForResponse(mp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,7 +235,7 @@ void setReplyTo(meshtastic_MeshPacket *p, const meshtastic_MeshPacket &to)
|
|||||||
assert(p->which_payload_variant == meshtastic_MeshPacket_decoded_tag); // Should already be set by now
|
assert(p->which_payload_variant == meshtastic_MeshPacket_decoded_tag); // Should already be set by now
|
||||||
p->to = getFrom(&to); // Make sure that if we are sending to the local node, we use our local node addr, not 0
|
p->to = getFrom(&to); // Make sure that if we are sending to the local node, we use our local node addr, not 0
|
||||||
p->channel = to.channel; // Use the same channel that the request came in on
|
p->channel = to.channel; // Use the same channel that the request came in on
|
||||||
p->hop_limit = routingModule->getHopLimitForResponse(to.hop_start, to.hop_limit);
|
p->hop_limit = routingModule->getHopLimitForResponse(to);
|
||||||
|
|
||||||
// No need for an ack if we are just delivering locally (it just generates an ignored ack)
|
// No need for an ack if we are just delivering locally (it just generates an ignored ack)
|
||||||
p->want_ack = (to.from != 0) ? to.want_ack : false;
|
p->want_ack = (to.from != 0) ? to.want_ack : false;
|
||||||
|
|||||||
@@ -225,4 +225,4 @@ class MeshModule
|
|||||||
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
|
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
|
||||||
* This ensures that if the request packet was sent reliably, the reply is sent that way as well.
|
* This ensures that if the request packet was sent reliably, the reply is sent that way as well.
|
||||||
*/
|
*/
|
||||||
void setReplyTo(meshtastic_MeshPacket *p, const meshtastic_MeshPacket &to);
|
void setReplyTo(meshtastic_MeshPacket *p, const meshtastic_MeshPacket &to);
|
||||||
|
|||||||
@@ -93,11 +93,8 @@ int MeshService::handleFromRadio(const meshtastic_MeshPacket *mp)
|
|||||||
} else if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB->getMeshNode(mp->from)->has_user &&
|
} else if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB->getMeshNode(mp->from)->has_user &&
|
||||||
nodeInfoModule && !isPreferredRebroadcaster && !nodeDB->isFull()) {
|
nodeInfoModule && !isPreferredRebroadcaster && !nodeDB->isFull()) {
|
||||||
if (airTime->isTxAllowedChannelUtil(true)) {
|
if (airTime->isTxAllowedChannelUtil(true)) {
|
||||||
// Hops used by the request. If somebody in between running modified firmware modified it, ignore it
|
const int8_t hopsUsed = getHopsAway(*mp, config.lora.hop_limit);
|
||||||
auto hopStart = mp->hop_start;
|
if (hopsUsed > (int32_t)(config.lora.hop_limit + 2)) {
|
||||||
auto hopLimit = mp->hop_limit;
|
|
||||||
uint8_t hopsUsed = hopStart < hopLimit ? config.lora.hop_limit : hopStart - hopLimit;
|
|
||||||
if (hopsUsed > config.lora.hop_limit + 2) {
|
|
||||||
LOG_DEBUG("Skip send NodeInfo: %d hops away is too far away", hopsUsed);
|
LOG_DEBUG("Skip send NodeInfo: %d hops away is too far away", hopsUsed);
|
||||||
} else {
|
} else {
|
||||||
LOG_INFO("Heard new node on ch. %d, send NodeInfo and ask for response", mp->channel);
|
LOG_INFO("Heard new node on ch. %d, send NodeInfo and ask for response", mp->channel);
|
||||||
@@ -276,6 +273,10 @@ bool MeshService::trySendPosition(NodeNum dest, bool wantReplies)
|
|||||||
if (nodeDB->hasValidPosition(node)) {
|
if (nodeDB->hasValidPosition(node)) {
|
||||||
#if HAS_GPS && !MESHTASTIC_EXCLUDE_GPS
|
#if HAS_GPS && !MESHTASTIC_EXCLUDE_GPS
|
||||||
if (positionModule) {
|
if (positionModule) {
|
||||||
|
if (!config.position.fixed_position && !nodeDB->hasLocalPositionSinceBoot()) {
|
||||||
|
LOG_DEBUG("Skip position ping; no fresh position since boot");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
LOG_INFO("Send position ping to 0x%x, wantReplies=%d, channel=%d", dest, wantReplies, node->channel);
|
LOG_INFO("Send position ping to 0x%x, wantReplies=%d, channel=%d", dest, wantReplies, node->channel);
|
||||||
positionModule->sendOurPosition(dest, wantReplies, node->channel);
|
positionModule->sendOurPosition(dest, wantReplies, node->channel);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ bool NextHopRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
|
|||||||
perhapsRebroadcast(p);
|
perhapsRebroadcast(p);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bool isRepeated = p->hop_start > 0 && p->hop_start == p->hop_limit;
|
bool isRepeated = getHopsAway(*p) == 0;
|
||||||
// If repeated and not in Tx queue anymore, try relaying again, or if we are the destination, send the ACK again
|
// If repeated and not in Tx queue anymore, try relaying again, or if we are the destination, send the ACK again
|
||||||
if (isRepeated) {
|
if (isRepeated) {
|
||||||
if (!findInTxQueue(p->from, p->id)) {
|
if (!findInTxQueue(p->from, p->id)) {
|
||||||
@@ -101,8 +101,7 @@ void NextHopRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtast
|
|||||||
bool wasAlreadyRelayer = wasRelayer(p->relay_node, p->decoded.request_id, p->to);
|
bool wasAlreadyRelayer = wasRelayer(p->relay_node, p->decoded.request_id, p->to);
|
||||||
bool weWereSoleRelayer = false;
|
bool weWereSoleRelayer = false;
|
||||||
bool weWereRelayer = wasRelayer(ourRelayID, p->decoded.request_id, p->to, &weWereSoleRelayer);
|
bool weWereRelayer = wasRelayer(ourRelayID, p->decoded.request_id, p->to, &weWereSoleRelayer);
|
||||||
if ((weWereRelayer && wasAlreadyRelayer) ||
|
if ((weWereRelayer && wasAlreadyRelayer) || (getHopsAway(*p) == 0 && weWereSoleRelayer)) {
|
||||||
(p->hop_start != 0 && p->hop_start == p->hop_limit && weWereSoleRelayer)) {
|
|
||||||
if (origTx->next_hop != p->relay_node) { // Not already set
|
if (origTx->next_hop != p->relay_node) { // Not already set
|
||||||
LOG_INFO("Update next hop of 0x%x to 0x%x based on ACK/reply (was relayer %d we were sole %d)", p->from,
|
LOG_INFO("Update next hop of 0x%x to 0x%x based on ACK/reply (was relayer %d we were sole %d)", p->from,
|
||||||
p->relay_node, wasAlreadyRelayer, weWereSoleRelayer);
|
p->relay_node, wasAlreadyRelayer, weWereSoleRelayer);
|
||||||
|
|||||||
@@ -805,11 +805,16 @@ void NodeDB::installDefaultModuleConfig()
|
|||||||
moduleConfig.external_notification.output_ms = 500;
|
moduleConfig.external_notification.output_ms = 500;
|
||||||
moduleConfig.external_notification.nag_timeout = 2;
|
moduleConfig.external_notification.nag_timeout = 2;
|
||||||
#endif
|
#endif
|
||||||
#if defined(RAK4630) || defined(RAK11310) || defined(RAK3312)
|
#if defined(RAK4630) || defined(RAK11310) || defined(RAK3312) || defined(MUZI_BASE) || defined(ELECROW_ThinkNode_M3) || \
|
||||||
// Default to RAK led pin 2 (blue)
|
defined(ELECROW_ThinkNode_M6)
|
||||||
|
// Default to PIN_LED2 for external notification output (LED color depends on device variant)
|
||||||
moduleConfig.external_notification.enabled = true;
|
moduleConfig.external_notification.enabled = true;
|
||||||
moduleConfig.external_notification.output = PIN_LED2;
|
moduleConfig.external_notification.output = PIN_LED2;
|
||||||
|
#if defined(MUZI_BASE) || defined(ELECROW_ThinkNode_M3)
|
||||||
|
moduleConfig.external_notification.active = false;
|
||||||
|
#else
|
||||||
moduleConfig.external_notification.active = true;
|
moduleConfig.external_notification.active = true;
|
||||||
|
#endif
|
||||||
moduleConfig.external_notification.alert_message = true;
|
moduleConfig.external_notification.alert_message = true;
|
||||||
moduleConfig.external_notification.output_ms = 1000;
|
moduleConfig.external_notification.output_ms = 1000;
|
||||||
moduleConfig.external_notification.nag_timeout = default_ringtone_nag_secs;
|
moduleConfig.external_notification.nag_timeout = default_ringtone_nag_secs;
|
||||||
@@ -1039,6 +1044,7 @@ void NodeDB::clearLocalPosition()
|
|||||||
node->position.altitude = 0;
|
node->position.altitude = 0;
|
||||||
node->position.time = 0;
|
node->position.time = 0;
|
||||||
setLocalPosition(meshtastic_Position_init_default);
|
setLocalPosition(meshtastic_Position_init_default);
|
||||||
|
localPositionUpdatedSinceBoot = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeDB::cleanupMeshDB()
|
void NodeDB::cleanupMeshDB()
|
||||||
@@ -1543,6 +1549,23 @@ uint32_t sinceReceived(const meshtastic_MeshPacket *p)
|
|||||||
return delta;
|
return delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int8_t getHopsAway(const meshtastic_MeshPacket &p, int8_t defaultIfUnknown)
|
||||||
|
{
|
||||||
|
// Firmware prior to 2.3.0 (585805c) lacked a hop_start field. Firmware version 2.5.0 (bf34329) introduced a
|
||||||
|
// bitfield that is always present. Use the presence of the bitfield to determine if the origin's firmware
|
||||||
|
// version is guaranteed to have hop_start populated. Note that this can only be done for decoded packets as
|
||||||
|
// the bitfield is encrypted under the channel encryption key. For encrypted packets, this returns
|
||||||
|
// defaultIfUnknown when hop_start is 0.
|
||||||
|
if (p.hop_start == 0 && !(p.which_payload_variant == meshtastic_MeshPacket_decoded_tag && p.decoded.has_bitfield))
|
||||||
|
return defaultIfUnknown; // Cannot reliably determine the number of hops.
|
||||||
|
|
||||||
|
// Guard against invalid values.
|
||||||
|
if (p.hop_start < p.hop_limit)
|
||||||
|
return defaultIfUnknown;
|
||||||
|
|
||||||
|
return p.hop_start - p.hop_limit;
|
||||||
|
}
|
||||||
|
|
||||||
#define NUM_ONLINE_SECS (60 * 60 * 2) // 2 hrs to consider someone offline
|
#define NUM_ONLINE_SECS (60 * 60 * 2) // 2 hrs to consider someone offline
|
||||||
|
|
||||||
size_t NodeDB::getNumOnlineMeshNodes(bool localOnly)
|
size_t NodeDB::getNumOnlineMeshNodes(bool localOnly)
|
||||||
@@ -1795,9 +1818,10 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp)
|
|||||||
info->via_mqtt = mp.via_mqtt; // Store if we received this packet via MQTT
|
info->via_mqtt = mp.via_mqtt; // Store if we received this packet via MQTT
|
||||||
|
|
||||||
// If hopStart was set and there wasn't someone messing with the limit in the middle, add hopsAway
|
// If hopStart was set and there wasn't someone messing with the limit in the middle, add hopsAway
|
||||||
if (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start) {
|
const int8_t hopsAway = getHopsAway(mp);
|
||||||
|
if (hopsAway >= 0) {
|
||||||
info->has_hops_away = true;
|
info->has_hops_away = true;
|
||||||
info->hops_away = mp.hop_start - mp.hop_limit;
|
info->hops_away = hopsAway;
|
||||||
}
|
}
|
||||||
sortMeshDB();
|
sortMeshDB();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,6 +110,10 @@ uint32_t sinceLastSeen(const meshtastic_NodeInfoLite *n);
|
|||||||
/// Given a packet, return how many seconds in the past (vs now) it was received
|
/// Given a packet, return how many seconds in the past (vs now) it was received
|
||||||
uint32_t sinceReceived(const meshtastic_MeshPacket *p);
|
uint32_t sinceReceived(const meshtastic_MeshPacket *p);
|
||||||
|
|
||||||
|
/// Given a packet, return the number of hops used to reach this node.
|
||||||
|
/// Returns defaultIfUnknown if the number of hops couldn't be determined.
|
||||||
|
int8_t getHopsAway(const meshtastic_MeshPacket &p, int8_t defaultIfUnknown = -1);
|
||||||
|
|
||||||
enum LoadFileResult {
|
enum LoadFileResult {
|
||||||
// Successfully opened the file
|
// Successfully opened the file
|
||||||
LOAD_SUCCESS = 1,
|
LOAD_SUCCESS = 1,
|
||||||
@@ -279,9 +283,13 @@ class NodeDB
|
|||||||
LOG_DEBUG("Set local position: lat=%i lon=%i time=%u timestamp=%u", position.latitude_i, position.longitude_i,
|
LOG_DEBUG("Set local position: lat=%i lon=%i time=%u timestamp=%u", position.latitude_i, position.longitude_i,
|
||||||
position.time, position.timestamp);
|
position.time, position.timestamp);
|
||||||
localPosition = position;
|
localPosition = position;
|
||||||
|
if (position.latitude_i != 0 || position.longitude_i != 0) {
|
||||||
|
localPositionUpdatedSinceBoot = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasValidPosition(const meshtastic_NodeInfoLite *n);
|
bool hasValidPosition(const meshtastic_NodeInfoLite *n);
|
||||||
|
bool hasLocalPositionSinceBoot() const { return localPositionUpdatedSinceBoot; }
|
||||||
|
|
||||||
#if !defined(MESHTASTIC_EXCLUDE_PKI)
|
#if !defined(MESHTASTIC_EXCLUDE_PKI)
|
||||||
bool checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest);
|
bool checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest);
|
||||||
@@ -301,6 +309,7 @@ class NodeDB
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool duplicateWarned = false;
|
bool duplicateWarned = false;
|
||||||
|
bool localPositionUpdatedSinceBoot = false;
|
||||||
uint32_t lastNodeDbSave = 0; // when we last saved our db to flash
|
uint32_t lastNodeDbSave = 0; // when we last saved our db to flash
|
||||||
uint32_t lastBackupAttempt = 0; // when we last tried a backup automatically or manually
|
uint32_t lastBackupAttempt = 0; // when we last tried a backup automatically or manually
|
||||||
uint32_t lastSort = 0; // When last sorted the nodeDB
|
uint32_t lastSort = 0; // When last sorted the nodeDB
|
||||||
|
|||||||
@@ -296,11 +296,6 @@ bool RadioInterface::shouldRebroadcastEarlyLikeRouter(meshtastic_MeshPacket *p)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are a CLIENT_BASE and the packet is from or to a favorited node, we should rebroadcast early
|
|
||||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_CLIENT_BASE) {
|
|
||||||
return nodeDB->isFromOrToFavoritedNode(*p);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,6 +498,11 @@ void RadioInterface::applyModemConfig()
|
|||||||
cr = 5;
|
cr = 5;
|
||||||
sf = 10;
|
sf = 10;
|
||||||
break;
|
break;
|
||||||
|
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO:
|
||||||
|
bw = (myRegion->wideLora) ? 1625.0 : 500;
|
||||||
|
cr = 8;
|
||||||
|
sf = 11;
|
||||||
|
break;
|
||||||
default: // Config_LoRaConfig_ModemPreset_LONG_FAST is default. Gracefully use this is preset is something illegal.
|
default: // Config_LoRaConfig_ModemPreset_LONG_FAST is default. Gracefully use this is preset is something illegal.
|
||||||
bw = (myRegion->wideLora) ? 812.5 : 250;
|
bw = (myRegion->wideLora) ? 812.5 : 250;
|
||||||
cr = 5;
|
cr = 5;
|
||||||
@@ -539,13 +539,26 @@ void RadioInterface::applyModemConfig()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((myRegion->freqEnd - myRegion->freqStart) < bw / 1000) {
|
if ((myRegion->freqEnd - myRegion->freqStart) < bw / 1000) {
|
||||||
static const char *err_string = "Regional frequency range is smaller than bandwidth. Fall back to default preset";
|
const float regionSpanKHz = (myRegion->freqEnd - myRegion->freqStart) * 1000.0f;
|
||||||
LOG_ERROR(err_string);
|
const float requestedBwKHz = bw;
|
||||||
|
const bool isWideRequest = requestedBwKHz >= 499.5f; // treat as 500 kHz preset
|
||||||
|
const char *presetName =
|
||||||
|
DisplayFormatters::getModemPresetDisplayName(loraConfig.modem_preset, false, loraConfig.use_preset);
|
||||||
|
|
||||||
|
char err_string[160];
|
||||||
|
if (isWideRequest) {
|
||||||
|
snprintf(err_string, sizeof(err_string), "%s region too narrow for 500kHz preset (%s). Falling back to LongFast.",
|
||||||
|
myRegion->name, presetName);
|
||||||
|
} else {
|
||||||
|
snprintf(err_string, sizeof(err_string), "%s region span %.0fkHz < requested %.0fkHz. Falling back to LongFast.",
|
||||||
|
myRegion->name, regionSpanKHz, requestedBwKHz);
|
||||||
|
}
|
||||||
|
LOG_ERROR("%s", err_string);
|
||||||
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
|
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
|
||||||
|
|
||||||
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
|
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
|
||||||
cn->level = meshtastic_LogRecord_Level_ERROR;
|
cn->level = meshtastic_LogRecord_Level_ERROR;
|
||||||
sprintf(cn->message, err_string);
|
snprintf(cn->message, sizeof(cn->message), "%s", err_string);
|
||||||
service->sendClientNotification(cn);
|
service->sendClientNotification(cn);
|
||||||
|
|
||||||
// Set to default modem preset
|
// Set to default modem preset
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "ReliableRouter.h"
|
#include "ReliableRouter.h"
|
||||||
#include "Default.h"
|
#include "Default.h"
|
||||||
#include "MeshTypes.h"
|
#include "MeshTypes.h"
|
||||||
|
#include "NodeDB.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "memGet.h"
|
#include "memGet.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
@@ -108,12 +109,12 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
|
|||||||
// If this packet should always be ACKed reliably with want_ack back to the original sender, make sure we
|
// If this packet should always be ACKed reliably with want_ack back to the original sender, make sure we
|
||||||
// do that unconditionally.
|
// do that unconditionally.
|
||||||
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel,
|
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel,
|
||||||
routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit), true);
|
routingModule->getHopLimitForResponse(*p), true);
|
||||||
} else if (!p->decoded.request_id && !p->decoded.reply_id) {
|
} else if (!p->decoded.request_id && !p->decoded.reply_id) {
|
||||||
// If it's not an ACK or a reply, send an ACK.
|
// If it's not an ACK or a reply, send an ACK.
|
||||||
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel,
|
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel,
|
||||||
routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit));
|
routingModule->getHopLimitForResponse(*p));
|
||||||
} else if ((p->hop_start > 0 && p->hop_start == p->hop_limit) || p->next_hop != NO_NEXT_HOP_PREFERENCE) {
|
} else if ((getHopsAway(*p) == 0) || p->next_hop != NO_NEXT_HOP_PREFERENCE) {
|
||||||
// If we received the packet directly from the original sender, send a 0-hop ACK since the original sender
|
// If we received the packet directly from the original sender, send a 0-hop ACK since the original sender
|
||||||
// won't overhear any implicit ACKs. If we received the packet via NextHopRouter, also send a 0-hop ACK to
|
// won't overhear any implicit ACKs. If we received the packet via NextHopRouter, also send a 0-hop ACK to
|
||||||
// stop the immediate relayer's retransmissions.
|
// stop the immediate relayer's retransmissions.
|
||||||
@@ -123,11 +124,11 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
|
|||||||
(nodeDB->getMeshNode(p->from) == nullptr || nodeDB->getMeshNode(p->from)->user.public_key.size == 0)) {
|
(nodeDB->getMeshNode(p->from) == nullptr || nodeDB->getMeshNode(p->from)->user.public_key.size == 0)) {
|
||||||
LOG_INFO("PKI packet from unknown node, send PKI_UNKNOWN_PUBKEY");
|
LOG_INFO("PKI packet from unknown node, send PKI_UNKNOWN_PUBKEY");
|
||||||
sendAckNak(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY, getFrom(p), p->id, channels.getPrimaryIndex(),
|
sendAckNak(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY, getFrom(p), p->id, channels.getPrimaryIndex(),
|
||||||
routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit));
|
routingModule->getHopLimitForResponse(*p));
|
||||||
} else {
|
} else {
|
||||||
// Send a 'NO_CHANNEL' error on the primary channel if want_ack packet destined for us cannot be decoded
|
// Send a 'NO_CHANNEL' error on the primary channel if want_ack packet destined for us cannot be decoded
|
||||||
sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex(),
|
sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex(),
|
||||||
routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit));
|
routingModule->getHopLimitForResponse(*p));
|
||||||
}
|
}
|
||||||
} else if (p->next_hop == nodeDB->getLastByteOfNodeNum(getNodeNum()) && p->hop_limit > 0) {
|
} else if (p->next_hop == nodeDB->getLastByteOfNodeNum(getNodeNum()) && p->hop_limit > 0) {
|
||||||
// No wantAck, but we need to ACK with hop limit of 0 if we were the next hop to stop their retransmissions
|
// No wantAck, but we need to ACK with hop limit of 0 if we were the next hop to stop their retransmissions
|
||||||
@@ -150,7 +151,9 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
|
|||||||
PacketId nakId = (c && c->error_reason != meshtastic_Routing_Error_NONE) ? p->decoded.request_id : 0;
|
PacketId nakId = (c && c->error_reason != meshtastic_Routing_Error_NONE) ? p->decoded.request_id : 0;
|
||||||
|
|
||||||
// We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records
|
// We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records
|
||||||
if (ackId || nakId) {
|
if ((ackId || nakId) &&
|
||||||
|
// Implicit ACKs from MQTT should not stop retransmissions
|
||||||
|
!(isFromUs(p) && p->transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MQTT)) {
|
||||||
LOG_DEBUG("Received a %s for 0x%x, stopping retransmissions", ackId ? "ACK" : "NAK", ackId);
|
LOG_DEBUG("Received a %s for 0x%x, stopping retransmissions", ackId ? "ACK" : "NAK", ackId);
|
||||||
if (ackId) {
|
if (ackId) {
|
||||||
stopRetransmission(p->to, ackId);
|
stopRetransmission(p->to, ackId);
|
||||||
|
|||||||
@@ -37,8 +37,8 @@
|
|||||||
|
|
||||||
static MemoryDynamic<meshtastic_MeshPacket> dynamicPool;
|
static MemoryDynamic<meshtastic_MeshPacket> dynamicPool;
|
||||||
Allocator<meshtastic_MeshPacket> &packetPool = dynamicPool;
|
Allocator<meshtastic_MeshPacket> &packetPool = dynamicPool;
|
||||||
#elif defined(ARCH_STM32WL)
|
#elif defined(ARCH_STM32WL) || defined(BOARD_HAS_PSRAM)
|
||||||
// On STM32 there isn't enough heap left over for the rest of the firmware if we allocate this statically.
|
// On STM32 and boards with PSRAM, there isn't enough heap left over for the rest of the firmware if we allocate this statically.
|
||||||
// For now, make it dynamic again.
|
// For now, make it dynamic again.
|
||||||
#define MAX_PACKETS \
|
#define MAX_PACKETS \
|
||||||
(MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + \
|
(MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + \
|
||||||
@@ -81,8 +81,7 @@ Router::Router() : concurrency::OSThread("Router"), fromRadioQueue(MAX_RX_FROMRA
|
|||||||
bool Router::shouldDecrementHopLimit(const meshtastic_MeshPacket *p)
|
bool Router::shouldDecrementHopLimit(const meshtastic_MeshPacket *p)
|
||||||
{
|
{
|
||||||
// First hop MUST always decrement to prevent retry issues
|
// First hop MUST always decrement to prevent retry issues
|
||||||
bool isFirstHop = (p->hop_start != 0 && p->hop_start == p->hop_limit);
|
if (getHopsAway(*p) == 0) {
|
||||||
if (isFirstHop) {
|
|
||||||
return true; // Always decrement on first hop
|
return true; // Always decrement on first hop
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,6 +525,10 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p)
|
|||||||
#elif ARCH_PORTDUINO
|
#elif ARCH_PORTDUINO
|
||||||
if (portduino_config.traceFilename != "" || portduino_config.logoutputlevel == level_trace) {
|
if (portduino_config.traceFilename != "" || portduino_config.logoutputlevel == level_trace) {
|
||||||
LOG_TRACE("%s", MeshPacketSerializer::JsonSerialize(p, false).c_str());
|
LOG_TRACE("%s", MeshPacketSerializer::JsonSerialize(p, false).c_str());
|
||||||
|
} else if (portduino_config.JSONFilename != "") {
|
||||||
|
if (portduino_config.JSONFilter == (_meshtastic_PortNum)0 || portduino_config.JSONFilter == p->decoded.portnum) {
|
||||||
|
JSONFile << MeshPacketSerializer::JsonSerialize(p, false) << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return DecodeState::DECODE_SUCCESS;
|
return DecodeState::DECODE_SUCCESS;
|
||||||
@@ -741,15 +744,19 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
|
|||||||
MeshModule::callModules(*p, src);
|
MeshModule::callModules(*p, src);
|
||||||
|
|
||||||
#if !MESHTASTIC_EXCLUDE_MQTT
|
#if !MESHTASTIC_EXCLUDE_MQTT
|
||||||
// Mark as pki_encrypted if it is not yet decoded and MQTT encryption is also enabled, hash matches and it's a DM not to
|
if (p_encrypted == nullptr) {
|
||||||
// us (because we would be able to decrypt it)
|
LOG_WARN("p_encrypted is null, skipping MQTT publish");
|
||||||
if (decodedState == DecodeState::DECODE_FAILURE && moduleConfig.mqtt.encryption_enabled && p->channel == 0x00 &&
|
} else {
|
||||||
!isBroadcast(p->to) && !isToUs(p))
|
// Mark as pki_encrypted if it is not yet decoded and MQTT encryption is also enabled, hash matches and it's a DM not
|
||||||
p_encrypted->pki_encrypted = true;
|
// to us (because we would be able to decrypt it)
|
||||||
// After potentially altering it, publish received message to MQTT if we're not the original transmitter of the packet
|
if (decodedState == DecodeState::DECODE_FAILURE && moduleConfig.mqtt.encryption_enabled && p->channel == 0x00 &&
|
||||||
if ((decodedState == DecodeState::DECODE_SUCCESS || p_encrypted->pki_encrypted) && moduleConfig.mqtt.enabled &&
|
!isBroadcast(p->to) && !isToUs(p))
|
||||||
!isFromUs(p) && mqtt)
|
p_encrypted->pki_encrypted = true;
|
||||||
mqtt->onSend(*p_encrypted, *p, p->channel);
|
// After potentially altering it, publish received message to MQTT if we're not the original transmitter of the packet
|
||||||
|
if ((decodedState == DecodeState::DECODE_SUCCESS || p_encrypted->pki_encrypted) && moduleConfig.mqtt.enabled &&
|
||||||
|
!isFromUs(p) && mqtt)
|
||||||
|
mqtt->onSend(*p_encrypted, *p, p->channel);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ PB_BIND(meshtastic_AdminMessage, meshtastic_AdminMessage, 2)
|
|||||||
PB_BIND(meshtastic_AdminMessage_InputEvent, meshtastic_AdminMessage_InputEvent, AUTO)
|
PB_BIND(meshtastic_AdminMessage_InputEvent, meshtastic_AdminMessage_InputEvent, AUTO)
|
||||||
|
|
||||||
|
|
||||||
|
PB_BIND(meshtastic_AdminMessage_OTAEvent, meshtastic_AdminMessage_OTAEvent, AUTO)
|
||||||
|
|
||||||
|
|
||||||
PB_BIND(meshtastic_HamParameters, meshtastic_HamParameters, AUTO)
|
PB_BIND(meshtastic_HamParameters, meshtastic_HamParameters, AUTO)
|
||||||
|
|
||||||
|
|
||||||
@@ -33,3 +36,5 @@ PB_BIND(meshtastic_KeyVerificationAdmin, meshtastic_KeyVerificationAdmin, AUTO)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,16 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Enum definitions */
|
/* Enum definitions */
|
||||||
|
/* Firmware update mode for OTA updates */
|
||||||
|
typedef enum _meshtastic_OTAMode {
|
||||||
|
/* Do not reboot into OTA mode */
|
||||||
|
meshtastic_OTAMode_NO_REBOOT_OTA = 0,
|
||||||
|
/* Reboot into OTA mode for BLE firmware update */
|
||||||
|
meshtastic_OTAMode_OTA_BLE = 1,
|
||||||
|
/* Reboot into OTA mode for WiFi firmware update */
|
||||||
|
meshtastic_OTAMode_OTA_WIFI = 2
|
||||||
|
} meshtastic_OTAMode;
|
||||||
|
|
||||||
/* TODO: REPLACE */
|
/* TODO: REPLACE */
|
||||||
typedef enum _meshtastic_AdminMessage_ConfigType {
|
typedef enum _meshtastic_AdminMessage_ConfigType {
|
||||||
/* TODO: REPLACE */
|
/* TODO: REPLACE */
|
||||||
@@ -103,6 +113,17 @@ typedef struct _meshtastic_AdminMessage_InputEvent {
|
|||||||
uint16_t touch_y;
|
uint16_t touch_y;
|
||||||
} meshtastic_AdminMessage_InputEvent;
|
} meshtastic_AdminMessage_InputEvent;
|
||||||
|
|
||||||
|
typedef PB_BYTES_ARRAY_T(32) meshtastic_AdminMessage_OTAEvent_ota_hash_t;
|
||||||
|
/* User is requesting an over the air update.
|
||||||
|
Node will reboot into the OTA loader */
|
||||||
|
typedef struct _meshtastic_AdminMessage_OTAEvent {
|
||||||
|
/* Tell the node to reboot into OTA mode for firmware update via BLE or WiFi (ESP32 only for now) */
|
||||||
|
meshtastic_OTAMode reboot_ota_mode;
|
||||||
|
/* A 32 byte hash of the OTA firmware.
|
||||||
|
Used to verify the integrity of the firmware before applying an update. */
|
||||||
|
meshtastic_AdminMessage_OTAEvent_ota_hash_t ota_hash;
|
||||||
|
} meshtastic_AdminMessage_OTAEvent;
|
||||||
|
|
||||||
/* Parameters for setting up Meshtastic for ameteur radio usage */
|
/* Parameters for setting up Meshtastic for ameteur radio usage */
|
||||||
typedef struct _meshtastic_HamParameters {
|
typedef struct _meshtastic_HamParameters {
|
||||||
/* Amateur radio call sign, eg. KD2ABC */
|
/* Amateur radio call sign, eg. KD2ABC */
|
||||||
@@ -261,7 +282,8 @@ typedef struct _meshtastic_AdminMessage {
|
|||||||
/* Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared. */
|
/* Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared. */
|
||||||
int32_t factory_reset_device;
|
int32_t factory_reset_device;
|
||||||
/* Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot)
|
/* Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot)
|
||||||
Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. */
|
Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth.
|
||||||
|
Deprecated in favor of reboot_ota_mode in 2.7.17 */
|
||||||
int32_t reboot_ota_seconds;
|
int32_t reboot_ota_seconds;
|
||||||
/* This message is only supported for the simulator Portduino build.
|
/* This message is only supported for the simulator Portduino build.
|
||||||
If received the simulator will exit successfully. */
|
If received the simulator will exit successfully. */
|
||||||
@@ -275,6 +297,8 @@ typedef struct _meshtastic_AdminMessage {
|
|||||||
/* Tell the node to reset the nodedb.
|
/* Tell the node to reset the nodedb.
|
||||||
When true, favorites are preserved through reset. */
|
When true, favorites are preserved through reset. */
|
||||||
bool nodedb_reset;
|
bool nodedb_reset;
|
||||||
|
/* Tell the node to reset into the OTA Loader */
|
||||||
|
meshtastic_AdminMessage_OTAEvent ota_request;
|
||||||
};
|
};
|
||||||
/* The node generates this key and sends it with any get_x_response packets.
|
/* The node generates this key and sends it with any get_x_response packets.
|
||||||
The client MUST include the same key with any set_x commands. Key expires after 300 seconds.
|
The client MUST include the same key with any set_x commands. Key expires after 300 seconds.
|
||||||
@@ -288,6 +312,10 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Helper constants for enums */
|
/* Helper constants for enums */
|
||||||
|
#define _meshtastic_OTAMode_MIN meshtastic_OTAMode_NO_REBOOT_OTA
|
||||||
|
#define _meshtastic_OTAMode_MAX meshtastic_OTAMode_OTA_WIFI
|
||||||
|
#define _meshtastic_OTAMode_ARRAYSIZE ((meshtastic_OTAMode)(meshtastic_OTAMode_OTA_WIFI+1))
|
||||||
|
|
||||||
#define _meshtastic_AdminMessage_ConfigType_MIN meshtastic_AdminMessage_ConfigType_DEVICE_CONFIG
|
#define _meshtastic_AdminMessage_ConfigType_MIN meshtastic_AdminMessage_ConfigType_DEVICE_CONFIG
|
||||||
#define _meshtastic_AdminMessage_ConfigType_MAX meshtastic_AdminMessage_ConfigType_DEVICEUI_CONFIG
|
#define _meshtastic_AdminMessage_ConfigType_MAX meshtastic_AdminMessage_ConfigType_DEVICEUI_CONFIG
|
||||||
#define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_DEVICEUI_CONFIG+1))
|
#define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_DEVICEUI_CONFIG+1))
|
||||||
@@ -311,6 +339,8 @@ extern "C" {
|
|||||||
#define meshtastic_AdminMessage_payload_variant_remove_backup_preferences_ENUMTYPE meshtastic_AdminMessage_BackupLocation
|
#define meshtastic_AdminMessage_payload_variant_remove_backup_preferences_ENUMTYPE meshtastic_AdminMessage_BackupLocation
|
||||||
|
|
||||||
|
|
||||||
|
#define meshtastic_AdminMessage_OTAEvent_reboot_ota_mode_ENUMTYPE meshtastic_OTAMode
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -320,12 +350,14 @@ extern "C" {
|
|||||||
/* Initializer values for message structs */
|
/* Initializer values for message structs */
|
||||||
#define meshtastic_AdminMessage_init_default {0, {0}, {0, {0}}}
|
#define meshtastic_AdminMessage_init_default {0, {0}, {0, {0}}}
|
||||||
#define meshtastic_AdminMessage_InputEvent_init_default {0, 0, 0, 0}
|
#define meshtastic_AdminMessage_InputEvent_init_default {0, 0, 0, 0}
|
||||||
|
#define meshtastic_AdminMessage_OTAEvent_init_default {_meshtastic_OTAMode_MIN, {0, {0}}}
|
||||||
#define meshtastic_HamParameters_init_default {"", 0, 0, ""}
|
#define meshtastic_HamParameters_init_default {"", 0, 0, ""}
|
||||||
#define meshtastic_NodeRemoteHardwarePinsResponse_init_default {0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}}
|
#define meshtastic_NodeRemoteHardwarePinsResponse_init_default {0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}}
|
||||||
#define meshtastic_SharedContact_init_default {0, false, meshtastic_User_init_default, 0, 0}
|
#define meshtastic_SharedContact_init_default {0, false, meshtastic_User_init_default, 0, 0}
|
||||||
#define meshtastic_KeyVerificationAdmin_init_default {_meshtastic_KeyVerificationAdmin_MessageType_MIN, 0, 0, false, 0}
|
#define meshtastic_KeyVerificationAdmin_init_default {_meshtastic_KeyVerificationAdmin_MessageType_MIN, 0, 0, false, 0}
|
||||||
#define meshtastic_AdminMessage_init_zero {0, {0}, {0, {0}}}
|
#define meshtastic_AdminMessage_init_zero {0, {0}, {0, {0}}}
|
||||||
#define meshtastic_AdminMessage_InputEvent_init_zero {0, 0, 0, 0}
|
#define meshtastic_AdminMessage_InputEvent_init_zero {0, 0, 0, 0}
|
||||||
|
#define meshtastic_AdminMessage_OTAEvent_init_zero {_meshtastic_OTAMode_MIN, {0, {0}}}
|
||||||
#define meshtastic_HamParameters_init_zero {"", 0, 0, ""}
|
#define meshtastic_HamParameters_init_zero {"", 0, 0, ""}
|
||||||
#define meshtastic_NodeRemoteHardwarePinsResponse_init_zero {0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}}
|
#define meshtastic_NodeRemoteHardwarePinsResponse_init_zero {0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}}
|
||||||
#define meshtastic_SharedContact_init_zero {0, false, meshtastic_User_init_zero, 0, 0}
|
#define meshtastic_SharedContact_init_zero {0, false, meshtastic_User_init_zero, 0, 0}
|
||||||
@@ -336,6 +368,8 @@ extern "C" {
|
|||||||
#define meshtastic_AdminMessage_InputEvent_kb_char_tag 2
|
#define meshtastic_AdminMessage_InputEvent_kb_char_tag 2
|
||||||
#define meshtastic_AdminMessage_InputEvent_touch_x_tag 3
|
#define meshtastic_AdminMessage_InputEvent_touch_x_tag 3
|
||||||
#define meshtastic_AdminMessage_InputEvent_touch_y_tag 4
|
#define meshtastic_AdminMessage_InputEvent_touch_y_tag 4
|
||||||
|
#define meshtastic_AdminMessage_OTAEvent_reboot_ota_mode_tag 1
|
||||||
|
#define meshtastic_AdminMessage_OTAEvent_ota_hash_tag 2
|
||||||
#define meshtastic_HamParameters_call_sign_tag 1
|
#define meshtastic_HamParameters_call_sign_tag 1
|
||||||
#define meshtastic_HamParameters_tx_power_tag 2
|
#define meshtastic_HamParameters_tx_power_tag 2
|
||||||
#define meshtastic_HamParameters_frequency_tag 3
|
#define meshtastic_HamParameters_frequency_tag 3
|
||||||
@@ -403,6 +437,7 @@ extern "C" {
|
|||||||
#define meshtastic_AdminMessage_shutdown_seconds_tag 98
|
#define meshtastic_AdminMessage_shutdown_seconds_tag 98
|
||||||
#define meshtastic_AdminMessage_factory_reset_config_tag 99
|
#define meshtastic_AdminMessage_factory_reset_config_tag 99
|
||||||
#define meshtastic_AdminMessage_nodedb_reset_tag 100
|
#define meshtastic_AdminMessage_nodedb_reset_tag 100
|
||||||
|
#define meshtastic_AdminMessage_ota_request_tag 102
|
||||||
#define meshtastic_AdminMessage_session_passkey_tag 101
|
#define meshtastic_AdminMessage_session_passkey_tag 101
|
||||||
|
|
||||||
/* Struct field encoding specification for nanopb */
|
/* Struct field encoding specification for nanopb */
|
||||||
@@ -461,7 +496,8 @@ X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_seconds,reboot_second
|
|||||||
X(a, STATIC, ONEOF, INT32, (payload_variant,shutdown_seconds,shutdown_seconds), 98) \
|
X(a, STATIC, ONEOF, INT32, (payload_variant,shutdown_seconds,shutdown_seconds), 98) \
|
||||||
X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_config,factory_reset_config), 99) \
|
X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_config,factory_reset_config), 99) \
|
||||||
X(a, STATIC, ONEOF, BOOL, (payload_variant,nodedb_reset,nodedb_reset), 100) \
|
X(a, STATIC, ONEOF, BOOL, (payload_variant,nodedb_reset,nodedb_reset), 100) \
|
||||||
X(a, STATIC, SINGULAR, BYTES, session_passkey, 101)
|
X(a, STATIC, SINGULAR, BYTES, session_passkey, 101) \
|
||||||
|
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,ota_request,ota_request), 102)
|
||||||
#define meshtastic_AdminMessage_CALLBACK NULL
|
#define meshtastic_AdminMessage_CALLBACK NULL
|
||||||
#define meshtastic_AdminMessage_DEFAULT NULL
|
#define meshtastic_AdminMessage_DEFAULT NULL
|
||||||
#define meshtastic_AdminMessage_payload_variant_get_channel_response_MSGTYPE meshtastic_Channel
|
#define meshtastic_AdminMessage_payload_variant_get_channel_response_MSGTYPE meshtastic_Channel
|
||||||
@@ -482,6 +518,7 @@ X(a, STATIC, SINGULAR, BYTES, session_passkey, 101)
|
|||||||
#define meshtastic_AdminMessage_payload_variant_store_ui_config_MSGTYPE meshtastic_DeviceUIConfig
|
#define meshtastic_AdminMessage_payload_variant_store_ui_config_MSGTYPE meshtastic_DeviceUIConfig
|
||||||
#define meshtastic_AdminMessage_payload_variant_add_contact_MSGTYPE meshtastic_SharedContact
|
#define meshtastic_AdminMessage_payload_variant_add_contact_MSGTYPE meshtastic_SharedContact
|
||||||
#define meshtastic_AdminMessage_payload_variant_key_verification_MSGTYPE meshtastic_KeyVerificationAdmin
|
#define meshtastic_AdminMessage_payload_variant_key_verification_MSGTYPE meshtastic_KeyVerificationAdmin
|
||||||
|
#define meshtastic_AdminMessage_payload_variant_ota_request_MSGTYPE meshtastic_AdminMessage_OTAEvent
|
||||||
|
|
||||||
#define meshtastic_AdminMessage_InputEvent_FIELDLIST(X, a) \
|
#define meshtastic_AdminMessage_InputEvent_FIELDLIST(X, a) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, event_code, 1) \
|
X(a, STATIC, SINGULAR, UINT32, event_code, 1) \
|
||||||
@@ -491,6 +528,12 @@ X(a, STATIC, SINGULAR, UINT32, touch_y, 4)
|
|||||||
#define meshtastic_AdminMessage_InputEvent_CALLBACK NULL
|
#define meshtastic_AdminMessage_InputEvent_CALLBACK NULL
|
||||||
#define meshtastic_AdminMessage_InputEvent_DEFAULT NULL
|
#define meshtastic_AdminMessage_InputEvent_DEFAULT NULL
|
||||||
|
|
||||||
|
#define meshtastic_AdminMessage_OTAEvent_FIELDLIST(X, a) \
|
||||||
|
X(a, STATIC, SINGULAR, UENUM, reboot_ota_mode, 1) \
|
||||||
|
X(a, STATIC, SINGULAR, BYTES, ota_hash, 2)
|
||||||
|
#define meshtastic_AdminMessage_OTAEvent_CALLBACK NULL
|
||||||
|
#define meshtastic_AdminMessage_OTAEvent_DEFAULT NULL
|
||||||
|
|
||||||
#define meshtastic_HamParameters_FIELDLIST(X, a) \
|
#define meshtastic_HamParameters_FIELDLIST(X, a) \
|
||||||
X(a, STATIC, SINGULAR, STRING, call_sign, 1) \
|
X(a, STATIC, SINGULAR, STRING, call_sign, 1) \
|
||||||
X(a, STATIC, SINGULAR, INT32, tx_power, 2) \
|
X(a, STATIC, SINGULAR, INT32, tx_power, 2) \
|
||||||
@@ -524,6 +567,7 @@ X(a, STATIC, OPTIONAL, UINT32, security_number, 4)
|
|||||||
|
|
||||||
extern const pb_msgdesc_t meshtastic_AdminMessage_msg;
|
extern const pb_msgdesc_t meshtastic_AdminMessage_msg;
|
||||||
extern const pb_msgdesc_t meshtastic_AdminMessage_InputEvent_msg;
|
extern const pb_msgdesc_t meshtastic_AdminMessage_InputEvent_msg;
|
||||||
|
extern const pb_msgdesc_t meshtastic_AdminMessage_OTAEvent_msg;
|
||||||
extern const pb_msgdesc_t meshtastic_HamParameters_msg;
|
extern const pb_msgdesc_t meshtastic_HamParameters_msg;
|
||||||
extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePinsResponse_msg;
|
extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePinsResponse_msg;
|
||||||
extern const pb_msgdesc_t meshtastic_SharedContact_msg;
|
extern const pb_msgdesc_t meshtastic_SharedContact_msg;
|
||||||
@@ -532,6 +576,7 @@ extern const pb_msgdesc_t meshtastic_KeyVerificationAdmin_msg;
|
|||||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||||
#define meshtastic_AdminMessage_fields &meshtastic_AdminMessage_msg
|
#define meshtastic_AdminMessage_fields &meshtastic_AdminMessage_msg
|
||||||
#define meshtastic_AdminMessage_InputEvent_fields &meshtastic_AdminMessage_InputEvent_msg
|
#define meshtastic_AdminMessage_InputEvent_fields &meshtastic_AdminMessage_InputEvent_msg
|
||||||
|
#define meshtastic_AdminMessage_OTAEvent_fields &meshtastic_AdminMessage_OTAEvent_msg
|
||||||
#define meshtastic_HamParameters_fields &meshtastic_HamParameters_msg
|
#define meshtastic_HamParameters_fields &meshtastic_HamParameters_msg
|
||||||
#define meshtastic_NodeRemoteHardwarePinsResponse_fields &meshtastic_NodeRemoteHardwarePinsResponse_msg
|
#define meshtastic_NodeRemoteHardwarePinsResponse_fields &meshtastic_NodeRemoteHardwarePinsResponse_msg
|
||||||
#define meshtastic_SharedContact_fields &meshtastic_SharedContact_msg
|
#define meshtastic_SharedContact_fields &meshtastic_SharedContact_msg
|
||||||
@@ -540,6 +585,7 @@ extern const pb_msgdesc_t meshtastic_KeyVerificationAdmin_msg;
|
|||||||
/* Maximum encoded size of messages (where known) */
|
/* Maximum encoded size of messages (where known) */
|
||||||
#define MESHTASTIC_MESHTASTIC_ADMIN_PB_H_MAX_SIZE meshtastic_AdminMessage_size
|
#define MESHTASTIC_MESHTASTIC_ADMIN_PB_H_MAX_SIZE meshtastic_AdminMessage_size
|
||||||
#define meshtastic_AdminMessage_InputEvent_size 14
|
#define meshtastic_AdminMessage_InputEvent_size 14
|
||||||
|
#define meshtastic_AdminMessage_OTAEvent_size 36
|
||||||
#define meshtastic_AdminMessage_size 511
|
#define meshtastic_AdminMessage_size 511
|
||||||
#define meshtastic_HamParameters_size 31
|
#define meshtastic_HamParameters_size 31
|
||||||
#define meshtastic_KeyVerificationAdmin_size 25
|
#define meshtastic_KeyVerificationAdmin_size 25
|
||||||
|
|||||||
@@ -293,7 +293,8 @@ typedef enum _meshtastic_Config_LoRaConfig_RegionCode {
|
|||||||
typedef enum _meshtastic_Config_LoRaConfig_ModemPreset {
|
typedef enum _meshtastic_Config_LoRaConfig_ModemPreset {
|
||||||
/* Long Range - Fast */
|
/* Long Range - Fast */
|
||||||
meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST = 0,
|
meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST = 0,
|
||||||
/* Long Range - Slow */
|
/* Long Range - Slow
|
||||||
|
Deprecated in 2.7: Unpopular slow preset. */
|
||||||
meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW = 1,
|
meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW = 1,
|
||||||
/* Very Long Range - Slow
|
/* Very Long Range - Slow
|
||||||
Deprecated in 2.5: Works only with txco and is unusably slow */
|
Deprecated in 2.5: Works only with txco and is unusably slow */
|
||||||
@@ -311,7 +312,10 @@ typedef enum _meshtastic_Config_LoRaConfig_ModemPreset {
|
|||||||
/* Short Range - Turbo
|
/* Short Range - Turbo
|
||||||
This is the fastest preset and the only one with 500kHz bandwidth.
|
This is the fastest preset and the only one with 500kHz bandwidth.
|
||||||
It is not legal to use in all regions due to this wider bandwidth. */
|
It is not legal to use in all regions due to this wider bandwidth. */
|
||||||
meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO = 8
|
meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO = 8,
|
||||||
|
/* Long Range - Turbo
|
||||||
|
This preset performs similarly to LongFast, but with 500Khz bandwidth. */
|
||||||
|
meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO = 9
|
||||||
} meshtastic_Config_LoRaConfig_ModemPreset;
|
} meshtastic_Config_LoRaConfig_ModemPreset;
|
||||||
|
|
||||||
typedef enum _meshtastic_Config_BluetoothConfig_PairingMode {
|
typedef enum _meshtastic_Config_BluetoothConfig_PairingMode {
|
||||||
@@ -689,8 +693,8 @@ extern "C" {
|
|||||||
#define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_BR_902+1))
|
#define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_BR_902+1))
|
||||||
|
|
||||||
#define _meshtastic_Config_LoRaConfig_ModemPreset_MIN meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST
|
#define _meshtastic_Config_LoRaConfig_ModemPreset_MIN meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST
|
||||||
#define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO
|
#define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO
|
||||||
#define _meshtastic_Config_LoRaConfig_ModemPreset_ARRAYSIZE ((meshtastic_Config_LoRaConfig_ModemPreset)(meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO+1))
|
#define _meshtastic_Config_LoRaConfig_ModemPreset_ARRAYSIZE ((meshtastic_Config_LoRaConfig_ModemPreset)(meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO+1))
|
||||||
|
|
||||||
#define _meshtastic_Config_BluetoothConfig_PairingMode_MIN meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN
|
#define _meshtastic_Config_BluetoothConfig_PairingMode_MIN meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN
|
||||||
#define _meshtastic_Config_BluetoothConfig_PairingMode_MAX meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN
|
#define _meshtastic_Config_BluetoothConfig_PairingMode_MAX meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ PB_BIND(meshtastic_Data, meshtastic_Data, 2)
|
|||||||
PB_BIND(meshtastic_KeyVerification, meshtastic_KeyVerification, AUTO)
|
PB_BIND(meshtastic_KeyVerification, meshtastic_KeyVerification, AUTO)
|
||||||
|
|
||||||
|
|
||||||
|
PB_BIND(meshtastic_StoreForwardPlusPlus, meshtastic_StoreForwardPlusPlus, 2)
|
||||||
|
|
||||||
|
|
||||||
PB_BIND(meshtastic_Waypoint, meshtastic_Waypoint, AUTO)
|
PB_BIND(meshtastic_Waypoint, meshtastic_Waypoint, AUTO)
|
||||||
|
|
||||||
|
|
||||||
@@ -121,6 +124,8 @@ PB_BIND(meshtastic_ChunkedPayloadResponse, meshtastic_ChunkedPayloadResponse, AU
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -92,8 +92,8 @@ typedef enum _meshtastic_HardwareModel {
|
|||||||
Less common/prototype boards listed here (needs one more byte over the air)
|
Less common/prototype boards listed here (needs one more byte over the air)
|
||||||
--------------------------------------------------------------------------- */
|
--------------------------------------------------------------------------- */
|
||||||
meshtastic_HardwareModel_LORA_RELAY_V1 = 32,
|
meshtastic_HardwareModel_LORA_RELAY_V1 = 32,
|
||||||
/* TODO: REPLACE */
|
/* T-Echo Plus device from LilyGo */
|
||||||
meshtastic_HardwareModel_NRF52840DK = 33,
|
meshtastic_HardwareModel_T_ECHO_PLUS = 33,
|
||||||
/* TODO: REPLACE */
|
/* TODO: REPLACE */
|
||||||
meshtastic_HardwareModel_PPR = 34,
|
meshtastic_HardwareModel_PPR = 34,
|
||||||
/* TODO: REPLACE */
|
/* TODO: REPLACE */
|
||||||
@@ -237,8 +237,8 @@ typedef enum _meshtastic_HardwareModel {
|
|||||||
meshtastic_HardwareModel_T_ETH_ELITE = 91,
|
meshtastic_HardwareModel_T_ETH_ELITE = 91,
|
||||||
/* Heltec HRI-3621 industrial probe */
|
/* Heltec HRI-3621 industrial probe */
|
||||||
meshtastic_HardwareModel_HELTEC_SENSOR_HUB = 92,
|
meshtastic_HardwareModel_HELTEC_SENSOR_HUB = 92,
|
||||||
/* Reserved Fried Chicken ID for future use */
|
/* Muzi Works Muzi-Base device */
|
||||||
meshtastic_HardwareModel_RESERVED_FRIED_CHICKEN = 93,
|
meshtastic_HardwareModel_MUZI_BASE = 93,
|
||||||
/* Heltec Magnetic Power Bank with Meshtastic compatible */
|
/* Heltec Magnetic Power Bank with Meshtastic compatible */
|
||||||
meshtastic_HardwareModel_HELTEC_MESH_POCKET = 94,
|
meshtastic_HardwareModel_HELTEC_MESH_POCKET = 94,
|
||||||
/* Seeed Solar Node */
|
/* Seeed Solar Node */
|
||||||
@@ -475,9 +475,28 @@ typedef enum _meshtastic_Routing_Error {
|
|||||||
meshtastic_Routing_Error_ADMIN_PUBLIC_KEY_UNAUTHORIZED = 37,
|
meshtastic_Routing_Error_ADMIN_PUBLIC_KEY_UNAUTHORIZED = 37,
|
||||||
/* Airtime fairness rate limit exceeded for a packet
|
/* Airtime fairness rate limit exceeded for a packet
|
||||||
This typically enforced per portnum and is used to prevent a single node from monopolizing airtime */
|
This typically enforced per portnum and is used to prevent a single node from monopolizing airtime */
|
||||||
meshtastic_Routing_Error_RATE_LIMIT_EXCEEDED = 38
|
meshtastic_Routing_Error_RATE_LIMIT_EXCEEDED = 38,
|
||||||
|
/* PKI encryption failed, due to no public key for the remote node
|
||||||
|
This is different from PKI_UNKNOWN_PUBKEY which indicates a failure upon receiving a packet */
|
||||||
|
meshtastic_Routing_Error_PKI_SEND_FAIL_PUBLIC_KEY = 39
|
||||||
} meshtastic_Routing_Error;
|
} meshtastic_Routing_Error;
|
||||||
|
|
||||||
|
/* Enum of message types */
|
||||||
|
typedef enum _meshtastic_StoreForwardPlusPlus_SFPP_message_type {
|
||||||
|
/* Send an announcement of the canonical tip of a chain */
|
||||||
|
meshtastic_StoreForwardPlusPlus_SFPP_message_type_CANON_ANNOUNCE = 0,
|
||||||
|
/* Query whether a specific link is on the chain */
|
||||||
|
meshtastic_StoreForwardPlusPlus_SFPP_message_type_CHAIN_QUERY = 1,
|
||||||
|
/* Request the next link in the chain */
|
||||||
|
meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_REQUEST = 3,
|
||||||
|
/* Provide a link to add to the chain */
|
||||||
|
meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE = 4,
|
||||||
|
/* If we must fragment, send the first half */
|
||||||
|
meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_FIRSTHALF = 5,
|
||||||
|
/* If we must fragment, send the second half */
|
||||||
|
meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF = 6
|
||||||
|
} meshtastic_StoreForwardPlusPlus_SFPP_message_type;
|
||||||
|
|
||||||
/* The priority of this message for sending.
|
/* The priority of this message for sending.
|
||||||
Higher priorities are sent first (when managing the transmit queue).
|
Higher priorities are sent first (when managing the transmit queue).
|
||||||
This field is never sent over the air, it is only used internally inside of a local device node.
|
This field is never sent over the air, it is only used internally inside of a local device node.
|
||||||
@@ -782,6 +801,34 @@ typedef struct _meshtastic_KeyVerification {
|
|||||||
meshtastic_KeyVerification_hash2_t hash2;
|
meshtastic_KeyVerification_hash2_t hash2;
|
||||||
} meshtastic_KeyVerification;
|
} meshtastic_KeyVerification;
|
||||||
|
|
||||||
|
typedef PB_BYTES_ARRAY_T(32) meshtastic_StoreForwardPlusPlus_message_hash_t;
|
||||||
|
typedef PB_BYTES_ARRAY_T(32) meshtastic_StoreForwardPlusPlus_commit_hash_t;
|
||||||
|
typedef PB_BYTES_ARRAY_T(32) meshtastic_StoreForwardPlusPlus_root_hash_t;
|
||||||
|
typedef PB_BYTES_ARRAY_T(240) meshtastic_StoreForwardPlusPlus_message_t;
|
||||||
|
/* The actual over-the-mesh message doing store and forward++ */
|
||||||
|
typedef struct _meshtastic_StoreForwardPlusPlus {
|
||||||
|
/* Which message type is this */
|
||||||
|
meshtastic_StoreForwardPlusPlus_SFPP_message_type sfpp_message_type;
|
||||||
|
/* The hash of the specific message */
|
||||||
|
meshtastic_StoreForwardPlusPlus_message_hash_t message_hash;
|
||||||
|
/* The hash of a link on a chain */
|
||||||
|
meshtastic_StoreForwardPlusPlus_commit_hash_t commit_hash;
|
||||||
|
/* the root hash of a chain */
|
||||||
|
meshtastic_StoreForwardPlusPlus_root_hash_t root_hash;
|
||||||
|
/* The encrypted bytes from a message */
|
||||||
|
meshtastic_StoreForwardPlusPlus_message_t message;
|
||||||
|
/* Message ID of the contained message */
|
||||||
|
uint32_t encapsulated_id;
|
||||||
|
/* Destination of the contained message */
|
||||||
|
uint32_t encapsulated_to;
|
||||||
|
/* Sender of the contained message */
|
||||||
|
uint32_t encapsulated_from;
|
||||||
|
/* The receive time of the message in question */
|
||||||
|
uint32_t encapsulated_rxtime;
|
||||||
|
/* Used in a LINK_REQUEST to specify the message X spots back from head */
|
||||||
|
uint32_t chain_count;
|
||||||
|
} meshtastic_StoreForwardPlusPlus;
|
||||||
|
|
||||||
/* Waypoint message, used to share arbitrary locations across the mesh */
|
/* Waypoint message, used to share arbitrary locations across the mesh */
|
||||||
typedef struct _meshtastic_Waypoint {
|
typedef struct _meshtastic_Waypoint {
|
||||||
/* Id of the waypoint */
|
/* Id of the waypoint */
|
||||||
@@ -1307,8 +1354,12 @@ extern "C" {
|
|||||||
#define _meshtastic_Position_AltSource_ARRAYSIZE ((meshtastic_Position_AltSource)(meshtastic_Position_AltSource_ALT_BAROMETRIC+1))
|
#define _meshtastic_Position_AltSource_ARRAYSIZE ((meshtastic_Position_AltSource)(meshtastic_Position_AltSource_ALT_BAROMETRIC+1))
|
||||||
|
|
||||||
#define _meshtastic_Routing_Error_MIN meshtastic_Routing_Error_NONE
|
#define _meshtastic_Routing_Error_MIN meshtastic_Routing_Error_NONE
|
||||||
#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_RATE_LIMIT_EXCEEDED
|
#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_PKI_SEND_FAIL_PUBLIC_KEY
|
||||||
#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_RATE_LIMIT_EXCEEDED+1))
|
#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_PKI_SEND_FAIL_PUBLIC_KEY+1))
|
||||||
|
|
||||||
|
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN meshtastic_StoreForwardPlusPlus_SFPP_message_type_CANON_ANNOUNCE
|
||||||
|
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_MAX meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF
|
||||||
|
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_ARRAYSIZE ((meshtastic_StoreForwardPlusPlus_SFPP_message_type)(meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF+1))
|
||||||
|
|
||||||
#define _meshtastic_MeshPacket_Priority_MIN meshtastic_MeshPacket_Priority_UNSET
|
#define _meshtastic_MeshPacket_Priority_MIN meshtastic_MeshPacket_Priority_UNSET
|
||||||
#define _meshtastic_MeshPacket_Priority_MAX meshtastic_MeshPacket_Priority_MAX
|
#define _meshtastic_MeshPacket_Priority_MAX meshtastic_MeshPacket_Priority_MAX
|
||||||
@@ -1338,6 +1389,8 @@ extern "C" {
|
|||||||
#define meshtastic_Data_portnum_ENUMTYPE meshtastic_PortNum
|
#define meshtastic_Data_portnum_ENUMTYPE meshtastic_PortNum
|
||||||
|
|
||||||
|
|
||||||
|
#define meshtastic_StoreForwardPlusPlus_sfpp_message_type_ENUMTYPE meshtastic_StoreForwardPlusPlus_SFPP_message_type
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define meshtastic_MeshPacket_priority_ENUMTYPE meshtastic_MeshPacket_Priority
|
#define meshtastic_MeshPacket_priority_ENUMTYPE meshtastic_MeshPacket_Priority
|
||||||
@@ -1380,6 +1433,7 @@ extern "C" {
|
|||||||
#define meshtastic_Routing_init_default {0, {meshtastic_RouteDiscovery_init_default}}
|
#define meshtastic_Routing_init_default {0, {meshtastic_RouteDiscovery_init_default}}
|
||||||
#define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0}
|
#define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0}
|
||||||
#define meshtastic_KeyVerification_init_default {0, {0, {0}}, {0, {0}}}
|
#define meshtastic_KeyVerification_init_default {0, {0, {0}}, {0, {0}}}
|
||||||
|
#define meshtastic_StoreForwardPlusPlus_init_default {_meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_Waypoint_init_default {0, false, 0, false, 0, 0, 0, "", "", 0}
|
#define meshtastic_Waypoint_init_default {0, false, 0, false, 0, 0, 0, "", "", 0}
|
||||||
#define meshtastic_MqttClientProxyMessage_init_default {"", 0, {{0, {0}}}, 0}
|
#define meshtastic_MqttClientProxyMessage_init_default {"", 0, {{0, {0}}}, 0}
|
||||||
#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0, 0, _meshtastic_MeshPacket_TransportMechanism_MIN}
|
#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0, 0, _meshtastic_MeshPacket_TransportMechanism_MIN}
|
||||||
@@ -1411,6 +1465,7 @@ extern "C" {
|
|||||||
#define meshtastic_Routing_init_zero {0, {meshtastic_RouteDiscovery_init_zero}}
|
#define meshtastic_Routing_init_zero {0, {meshtastic_RouteDiscovery_init_zero}}
|
||||||
#define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0}
|
#define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0}
|
||||||
#define meshtastic_KeyVerification_init_zero {0, {0, {0}}, {0, {0}}}
|
#define meshtastic_KeyVerification_init_zero {0, {0, {0}}, {0, {0}}}
|
||||||
|
#define meshtastic_StoreForwardPlusPlus_init_zero {_meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0}
|
||||||
#define meshtastic_Waypoint_init_zero {0, false, 0, false, 0, 0, 0, "", "", 0}
|
#define meshtastic_Waypoint_init_zero {0, false, 0, false, 0, 0, 0, "", "", 0}
|
||||||
#define meshtastic_MqttClientProxyMessage_init_zero {"", 0, {{0, {0}}}, 0}
|
#define meshtastic_MqttClientProxyMessage_init_zero {"", 0, {{0, {0}}}, 0}
|
||||||
#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0, 0, _meshtastic_MeshPacket_TransportMechanism_MIN}
|
#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0, 0, _meshtastic_MeshPacket_TransportMechanism_MIN}
|
||||||
@@ -1489,6 +1544,16 @@ extern "C" {
|
|||||||
#define meshtastic_KeyVerification_nonce_tag 1
|
#define meshtastic_KeyVerification_nonce_tag 1
|
||||||
#define meshtastic_KeyVerification_hash1_tag 2
|
#define meshtastic_KeyVerification_hash1_tag 2
|
||||||
#define meshtastic_KeyVerification_hash2_tag 3
|
#define meshtastic_KeyVerification_hash2_tag 3
|
||||||
|
#define meshtastic_StoreForwardPlusPlus_sfpp_message_type_tag 1
|
||||||
|
#define meshtastic_StoreForwardPlusPlus_message_hash_tag 2
|
||||||
|
#define meshtastic_StoreForwardPlusPlus_commit_hash_tag 3
|
||||||
|
#define meshtastic_StoreForwardPlusPlus_root_hash_tag 4
|
||||||
|
#define meshtastic_StoreForwardPlusPlus_message_tag 5
|
||||||
|
#define meshtastic_StoreForwardPlusPlus_encapsulated_id_tag 6
|
||||||
|
#define meshtastic_StoreForwardPlusPlus_encapsulated_to_tag 7
|
||||||
|
#define meshtastic_StoreForwardPlusPlus_encapsulated_from_tag 8
|
||||||
|
#define meshtastic_StoreForwardPlusPlus_encapsulated_rxtime_tag 9
|
||||||
|
#define meshtastic_StoreForwardPlusPlus_chain_count_tag 10
|
||||||
#define meshtastic_Waypoint_id_tag 1
|
#define meshtastic_Waypoint_id_tag 1
|
||||||
#define meshtastic_Waypoint_latitude_i_tag 2
|
#define meshtastic_Waypoint_latitude_i_tag 2
|
||||||
#define meshtastic_Waypoint_longitude_i_tag 3
|
#define meshtastic_Waypoint_longitude_i_tag 3
|
||||||
@@ -1705,6 +1770,20 @@ X(a, STATIC, SINGULAR, BYTES, hash2, 3)
|
|||||||
#define meshtastic_KeyVerification_CALLBACK NULL
|
#define meshtastic_KeyVerification_CALLBACK NULL
|
||||||
#define meshtastic_KeyVerification_DEFAULT NULL
|
#define meshtastic_KeyVerification_DEFAULT NULL
|
||||||
|
|
||||||
|
#define meshtastic_StoreForwardPlusPlus_FIELDLIST(X, a) \
|
||||||
|
X(a, STATIC, SINGULAR, UENUM, sfpp_message_type, 1) \
|
||||||
|
X(a, STATIC, SINGULAR, BYTES, message_hash, 2) \
|
||||||
|
X(a, STATIC, SINGULAR, BYTES, commit_hash, 3) \
|
||||||
|
X(a, STATIC, SINGULAR, BYTES, root_hash, 4) \
|
||||||
|
X(a, STATIC, SINGULAR, BYTES, message, 5) \
|
||||||
|
X(a, STATIC, SINGULAR, UINT32, encapsulated_id, 6) \
|
||||||
|
X(a, STATIC, SINGULAR, UINT32, encapsulated_to, 7) \
|
||||||
|
X(a, STATIC, SINGULAR, UINT32, encapsulated_from, 8) \
|
||||||
|
X(a, STATIC, SINGULAR, UINT32, encapsulated_rxtime, 9) \
|
||||||
|
X(a, STATIC, SINGULAR, UINT32, chain_count, 10)
|
||||||
|
#define meshtastic_StoreForwardPlusPlus_CALLBACK NULL
|
||||||
|
#define meshtastic_StoreForwardPlusPlus_DEFAULT NULL
|
||||||
|
|
||||||
#define meshtastic_Waypoint_FIELDLIST(X, a) \
|
#define meshtastic_Waypoint_FIELDLIST(X, a) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, id, 1) \
|
X(a, STATIC, SINGULAR, UINT32, id, 1) \
|
||||||
X(a, STATIC, OPTIONAL, SFIXED32, latitude_i, 2) \
|
X(a, STATIC, OPTIONAL, SFIXED32, latitude_i, 2) \
|
||||||
@@ -1980,6 +2059,7 @@ extern const pb_msgdesc_t meshtastic_RouteDiscovery_msg;
|
|||||||
extern const pb_msgdesc_t meshtastic_Routing_msg;
|
extern const pb_msgdesc_t meshtastic_Routing_msg;
|
||||||
extern const pb_msgdesc_t meshtastic_Data_msg;
|
extern const pb_msgdesc_t meshtastic_Data_msg;
|
||||||
extern const pb_msgdesc_t meshtastic_KeyVerification_msg;
|
extern const pb_msgdesc_t meshtastic_KeyVerification_msg;
|
||||||
|
extern const pb_msgdesc_t meshtastic_StoreForwardPlusPlus_msg;
|
||||||
extern const pb_msgdesc_t meshtastic_Waypoint_msg;
|
extern const pb_msgdesc_t meshtastic_Waypoint_msg;
|
||||||
extern const pb_msgdesc_t meshtastic_MqttClientProxyMessage_msg;
|
extern const pb_msgdesc_t meshtastic_MqttClientProxyMessage_msg;
|
||||||
extern const pb_msgdesc_t meshtastic_MeshPacket_msg;
|
extern const pb_msgdesc_t meshtastic_MeshPacket_msg;
|
||||||
@@ -2013,6 +2093,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
|
|||||||
#define meshtastic_Routing_fields &meshtastic_Routing_msg
|
#define meshtastic_Routing_fields &meshtastic_Routing_msg
|
||||||
#define meshtastic_Data_fields &meshtastic_Data_msg
|
#define meshtastic_Data_fields &meshtastic_Data_msg
|
||||||
#define meshtastic_KeyVerification_fields &meshtastic_KeyVerification_msg
|
#define meshtastic_KeyVerification_fields &meshtastic_KeyVerification_msg
|
||||||
|
#define meshtastic_StoreForwardPlusPlus_fields &meshtastic_StoreForwardPlusPlus_msg
|
||||||
#define meshtastic_Waypoint_fields &meshtastic_Waypoint_msg
|
#define meshtastic_Waypoint_fields &meshtastic_Waypoint_msg
|
||||||
#define meshtastic_MqttClientProxyMessage_fields &meshtastic_MqttClientProxyMessage_msg
|
#define meshtastic_MqttClientProxyMessage_fields &meshtastic_MqttClientProxyMessage_msg
|
||||||
#define meshtastic_MeshPacket_fields &meshtastic_MeshPacket_msg
|
#define meshtastic_MeshPacket_fields &meshtastic_MeshPacket_msg
|
||||||
@@ -2069,6 +2150,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
|
|||||||
#define meshtastic_QueueStatus_size 23
|
#define meshtastic_QueueStatus_size 23
|
||||||
#define meshtastic_RouteDiscovery_size 256
|
#define meshtastic_RouteDiscovery_size 256
|
||||||
#define meshtastic_Routing_size 259
|
#define meshtastic_Routing_size 259
|
||||||
|
#define meshtastic_StoreForwardPlusPlus_size 377
|
||||||
#define meshtastic_ToRadio_size 504
|
#define meshtastic_ToRadio_size 504
|
||||||
#define meshtastic_User_size 115
|
#define meshtastic_User_size 115
|
||||||
#define meshtastic_Waypoint_size 165
|
#define meshtastic_Waypoint_size 165
|
||||||
|
|||||||
@@ -86,6 +86,11 @@ typedef enum _meshtastic_PortNum {
|
|||||||
/* Paxcounter lib included in the firmware
|
/* Paxcounter lib included in the firmware
|
||||||
ENCODING: protobuf */
|
ENCODING: protobuf */
|
||||||
meshtastic_PortNum_PAXCOUNTER_APP = 34,
|
meshtastic_PortNum_PAXCOUNTER_APP = 34,
|
||||||
|
/* Store and Forward++ module included in the firmware
|
||||||
|
ENCODING: protobuf
|
||||||
|
This module is specifically for Native Linux nodes, and provides a Git-style
|
||||||
|
chain of messages. */
|
||||||
|
meshtastic_PortNum_STORE_FORWARD_PLUSPLUS_APP = 35,
|
||||||
/* Provides a hardware serial interface to send and receive from the Meshtastic network.
|
/* Provides a hardware serial interface to send and receive from the Meshtastic network.
|
||||||
Connect to the RX/TX pins of a device with 38400 8N1. Packets received from the Meshtastic
|
Connect to the RX/TX pins of a device with 38400 8N1. Packets received from the Meshtastic
|
||||||
network is forwarded to the RX pin while sending a packet to TX will go out to the Mesh network.
|
network is forwarded to the RX pin while sending a packet to TX will go out to the Mesh network.
|
||||||
|
|||||||
@@ -417,6 +417,9 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
|||||||
}
|
}
|
||||||
case meshtastic_AdminMessage_enter_dfu_mode_request_tag: {
|
case meshtastic_AdminMessage_enter_dfu_mode_request_tag: {
|
||||||
LOG_INFO("Client requesting to enter DFU mode");
|
LOG_INFO("Client requesting to enter DFU mode");
|
||||||
|
#if HAS_SCREEN
|
||||||
|
IF_SCREEN(screen->showSimpleBanner("Device is rebooting\ninto DFU mode.", 0));
|
||||||
|
#endif
|
||||||
#if defined(ARCH_NRF52) || defined(ARCH_RP2040)
|
#if defined(ARCH_NRF52) || defined(ARCH_RP2040)
|
||||||
enterDfuMode();
|
enterDfuMode();
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1018,8 +1018,7 @@ int32_t CannedMessageModule::runOnce()
|
|||||||
// Clean up virtual keyboard if needed when going inactive
|
// Clean up virtual keyboard if needed when going inactive
|
||||||
if (graphics::NotificationRenderer::virtualKeyboard && graphics::NotificationRenderer::textInputCallback == nullptr) {
|
if (graphics::NotificationRenderer::virtualKeyboard && graphics::NotificationRenderer::textInputCallback == nullptr) {
|
||||||
LOG_INFO("Performing delayed virtual keyboard cleanup");
|
LOG_INFO("Performing delayed virtual keyboard cleanup");
|
||||||
delete graphics::NotificationRenderer::virtualKeyboard;
|
graphics::OnScreenKeyboardModule::instance().stop(false);
|
||||||
graphics::NotificationRenderer::virtualKeyboard = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
temporaryMessage = "";
|
temporaryMessage = "";
|
||||||
@@ -1036,9 +1035,7 @@ int32_t CannedMessageModule::runOnce()
|
|||||||
// Clean up virtual keyboard after sending
|
// Clean up virtual keyboard after sending
|
||||||
if (graphics::NotificationRenderer::virtualKeyboard) {
|
if (graphics::NotificationRenderer::virtualKeyboard) {
|
||||||
LOG_INFO("Cleaning up virtual keyboard after message send");
|
LOG_INFO("Cleaning up virtual keyboard after message send");
|
||||||
delete graphics::NotificationRenderer::virtualKeyboard;
|
graphics::OnScreenKeyboardModule::instance().stop(false);
|
||||||
graphics::NotificationRenderer::virtualKeyboard = nullptr;
|
|
||||||
graphics::NotificationRenderer::textInputCallback = nullptr;
|
|
||||||
graphics::NotificationRenderer::resetBanner();
|
graphics::NotificationRenderer::resetBanner();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1096,9 +1093,7 @@ int32_t CannedMessageModule::runOnce()
|
|||||||
// Clean up virtual keyboard if it exists during timeout
|
// Clean up virtual keyboard if it exists during timeout
|
||||||
if (graphics::NotificationRenderer::virtualKeyboard) {
|
if (graphics::NotificationRenderer::virtualKeyboard) {
|
||||||
LOG_INFO("Cleaning up virtual keyboard due to module timeout");
|
LOG_INFO("Cleaning up virtual keyboard due to module timeout");
|
||||||
delete graphics::NotificationRenderer::virtualKeyboard;
|
graphics::OnScreenKeyboardModule::instance().stop(false);
|
||||||
graphics::NotificationRenderer::virtualKeyboard = nullptr;
|
|
||||||
graphics::NotificationRenderer::textInputCallback = nullptr;
|
|
||||||
graphics::NotificationRenderer::resetBanner();
|
graphics::NotificationRenderer::resetBanner();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ int32_t ExternalNotificationModule::runOnce()
|
|||||||
delay = EXT_NOTIFICATION_FAST_THREAD_MS;
|
delay = EXT_NOTIFICATION_FAST_THREAD_MS;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef T_WATCH_S3
|
#ifdef HAS_DRV2605
|
||||||
drv.go();
|
drv.go();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -283,7 +283,7 @@ void ExternalNotificationModule::setExternalState(uint8_t index, bool on)
|
|||||||
#ifdef UNPHONE
|
#ifdef UNPHONE
|
||||||
unphone.rgb(red, green, blue);
|
unphone.rgb(red, green, blue);
|
||||||
#endif
|
#endif
|
||||||
#ifdef T_WATCH_S3
|
#ifdef HAS_DRV2605
|
||||||
if (on) {
|
if (on) {
|
||||||
drv.go();
|
drv.go();
|
||||||
} else {
|
} else {
|
||||||
@@ -310,8 +310,7 @@ void ExternalNotificationModule::stopNow()
|
|||||||
rtttl::stop();
|
rtttl::stop();
|
||||||
#ifdef HAS_I2S
|
#ifdef HAS_I2S
|
||||||
LOG_INFO("Stop audioThread playback");
|
LOG_INFO("Stop audioThread playback");
|
||||||
if (audioThread->isPlaying())
|
audioThread->stop();
|
||||||
audioThread->stop();
|
|
||||||
#endif
|
#endif
|
||||||
// Turn off all outputs
|
// Turn off all outputs
|
||||||
LOG_INFO("Turning off setExternalStates");
|
LOG_INFO("Turning off setExternalStates");
|
||||||
@@ -320,7 +319,7 @@ void ExternalNotificationModule::stopNow()
|
|||||||
externalTurnedOn[i] = 0;
|
externalTurnedOn[i] = 0;
|
||||||
}
|
}
|
||||||
setIntervalFromNow(0);
|
setIntervalFromNow(0);
|
||||||
#ifdef T_WATCH_S3
|
#ifdef HAS_DRV2605
|
||||||
drv.stop();
|
drv.stop();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -542,6 +541,19 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
|
|||||||
(!isBroadcast(mp.to) && isToUs(&mp))) {
|
(!isBroadcast(mp.to) && isToUs(&mp))) {
|
||||||
// Buzz if buzzer mode is not in DIRECT_MSG_ONLY or is DM to us
|
// Buzz if buzzer mode is not in DIRECT_MSG_ONLY or is DM to us
|
||||||
isNagging = true;
|
isNagging = true;
|
||||||
|
#ifdef T_LORA_PAGER
|
||||||
|
if (canBuzz()) {
|
||||||
|
drv.setWaveform(0, 16); // Long buzzer 100%
|
||||||
|
drv.setWaveform(1, 0); // Pause
|
||||||
|
drv.setWaveform(2, 16);
|
||||||
|
drv.setWaveform(3, 0);
|
||||||
|
drv.setWaveform(4, 16);
|
||||||
|
drv.setWaveform(5, 0);
|
||||||
|
drv.setWaveform(6, 16);
|
||||||
|
drv.setWaveform(7, 0);
|
||||||
|
drv.go();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (!moduleConfig.external_notification.use_pwm && !moduleConfig.external_notification.use_i2s_as_buzzer) {
|
if (!moduleConfig.external_notification.use_pwm && !moduleConfig.external_notification.use_i2s_as_buzzer) {
|
||||||
setExternalState(2, true);
|
setExternalState(2, true);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -181,25 +181,25 @@ void setupModules()
|
|||||||
// new ReplyModule();
|
// new ReplyModule();
|
||||||
#if (HAS_BUTTON || ARCH_PORTDUINO) && !MESHTASTIC_EXCLUDE_INPUTBROKER
|
#if (HAS_BUTTON || ARCH_PORTDUINO) && !MESHTASTIC_EXCLUDE_INPUTBROKER
|
||||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||||
#ifndef T_LORA_PAGER
|
#if defined(T_LORA_PAGER)
|
||||||
rotaryEncoderInterruptImpl1 = new RotaryEncoderInterruptImpl1();
|
|
||||||
if (!rotaryEncoderInterruptImpl1->init()) {
|
|
||||||
delete rotaryEncoderInterruptImpl1;
|
|
||||||
rotaryEncoderInterruptImpl1 = nullptr;
|
|
||||||
}
|
|
||||||
#elif defined(T_LORA_PAGER)
|
|
||||||
// use a special FSM based rotary encoder version for T-LoRa Pager
|
// use a special FSM based rotary encoder version for T-LoRa Pager
|
||||||
rotaryEncoderImpl = new RotaryEncoderImpl();
|
rotaryEncoderImpl = new RotaryEncoderImpl();
|
||||||
if (!rotaryEncoderImpl->init()) {
|
if (!rotaryEncoderImpl->init()) {
|
||||||
delete rotaryEncoderImpl;
|
delete rotaryEncoderImpl;
|
||||||
rotaryEncoderImpl = nullptr;
|
rotaryEncoderImpl = nullptr;
|
||||||
}
|
}
|
||||||
#else
|
#elif defined(INPUTDRIVER_ENCODER_TYPE) && (INPUTDRIVER_ENCODER_TYPE == 2)
|
||||||
upDownInterruptImpl1 = new UpDownInterruptImpl1();
|
upDownInterruptImpl1 = new UpDownInterruptImpl1();
|
||||||
if (!upDownInterruptImpl1->init()) {
|
if (!upDownInterruptImpl1->init()) {
|
||||||
delete upDownInterruptImpl1;
|
delete upDownInterruptImpl1;
|
||||||
upDownInterruptImpl1 = nullptr;
|
upDownInterruptImpl1 = nullptr;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
rotaryEncoderInterruptImpl1 = new RotaryEncoderInterruptImpl1();
|
||||||
|
if (!rotaryEncoderInterruptImpl1->init()) {
|
||||||
|
delete rotaryEncoderInterruptImpl1;
|
||||||
|
rotaryEncoderInterruptImpl1 = nullptr;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
cardKbI2cImpl = new CardKbI2cImpl();
|
cardKbI2cImpl = new CardKbI2cImpl();
|
||||||
cardKbI2cImpl->init();
|
cardKbI2cImpl->init();
|
||||||
@@ -217,7 +217,7 @@ void setupModules()
|
|||||||
}
|
}
|
||||||
#endif // HAS_BUTTON
|
#endif // HAS_BUTTON
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR && portduino_config.i2cdev != "") {
|
||||||
seesawRotary = new SeesawRotary("SeesawRotary");
|
seesawRotary = new SeesawRotary("SeesawRotary");
|
||||||
if (!seesawRotary->init()) {
|
if (!seesawRotary->init()) {
|
||||||
delete seesawRotary;
|
delete seesawRotary;
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ bool NeighborInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp,
|
|||||||
} else {
|
} else {
|
||||||
LOG_DEBUG(" Ignoring dummy neighbor info packet (single neighbor with nodeId 0, snr 0)");
|
LOG_DEBUG(" Ignoring dummy neighbor info packet (single neighbor with nodeId 0, snr 0)");
|
||||||
}
|
}
|
||||||
} else if (mp.hop_start != 0 && mp.hop_start == mp.hop_limit) {
|
} else if (getHopsAway(mp) == 0) {
|
||||||
LOG_DEBUG("Get or create neighbor: %u with snr %f", mp.from, mp.rx_snr);
|
LOG_DEBUG("Get or create neighbor: %u with snr %f", mp.from, mp.rx_snr);
|
||||||
// If the hopLimit is the same as hopStart, then it is a neighbor
|
// If the hopLimit is the same as hopStart, then it is a neighbor
|
||||||
getOrCreateNeighbor(mp.from, mp.from, 0,
|
getOrCreateNeighbor(mp.from, mp.from, 0,
|
||||||
|
|||||||
@@ -7,17 +7,41 @@
|
|||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include <Throttle.h>
|
#include <Throttle.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#ifndef USERPREFS_NODEINFO_REPLY_SUPPRESS_SECS
|
||||||
|
#define USERPREFS_NODEINFO_REPLY_SUPPRESS_SECS (12 * 60 * 60)
|
||||||
|
#endif
|
||||||
|
|
||||||
NodeInfoModule *nodeInfoModule;
|
NodeInfoModule *nodeInfoModule;
|
||||||
|
|
||||||
|
static constexpr uint32_t NodeInfoReplySuppressSeconds = USERPREFS_NODEINFO_REPLY_SUPPRESS_SECS;
|
||||||
|
|
||||||
bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_User *pptr)
|
bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_User *pptr)
|
||||||
{
|
{
|
||||||
|
suppressReplyForCurrentRequest = false;
|
||||||
|
|
||||||
if (mp.from == nodeDB->getNodeNum()) {
|
if (mp.from == nodeDB->getNodeNum()) {
|
||||||
LOG_WARN("Ignoring packet supposed to be from our own node: %08x", mp.from);
|
LOG_WARN("Ignoring packet supposed to be from our own node: %08x", mp.from);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto p = *pptr;
|
auto p = *pptr;
|
||||||
|
|
||||||
|
if (mp.decoded.want_response) {
|
||||||
|
const NodeNum sender = getFrom(&mp);
|
||||||
|
const uint32_t now = mp.rx_time ? mp.rx_time : getTime();
|
||||||
|
auto it = lastNodeInfoSeen.find(sender);
|
||||||
|
if (it != lastNodeInfoSeen.end()) {
|
||||||
|
uint32_t sinceLast = now >= it->second ? now - it->second : 0;
|
||||||
|
if (sinceLast < NodeInfoReplySuppressSeconds) {
|
||||||
|
suppressReplyForCurrentRequest = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastNodeInfoSeen[sender] = now;
|
||||||
|
pruneLastNodeInfoCache();
|
||||||
|
}
|
||||||
|
|
||||||
if (p.is_licensed != owner.is_licensed) {
|
if (p.is_licensed != owner.is_licensed) {
|
||||||
LOG_WARN("Invalid nodeInfo detected, is_licensed mismatch!");
|
LOG_WARN("Invalid nodeInfo detected, is_licensed mismatch!");
|
||||||
return true;
|
return true;
|
||||||
@@ -42,6 +66,8 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
|
|||||||
service->sendToPhone(packetCopy);
|
service->sendToPhone(packetCopy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pruneLastNodeInfoCache();
|
||||||
|
|
||||||
// LOG_DEBUG("did handleReceived");
|
// LOG_DEBUG("did handleReceived");
|
||||||
return false; // Let others look at this message also if they want
|
return false; // Let others look at this message also if they want
|
||||||
}
|
}
|
||||||
@@ -68,9 +94,11 @@ void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t cha
|
|||||||
|
|
||||||
if (p) { // Check whether we didn't ignore it
|
if (p) { // Check whether we didn't ignore it
|
||||||
p->to = dest;
|
p->to = dest;
|
||||||
p->decoded.want_response = (config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER &&
|
bool requestWantResponse = (config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER &&
|
||||||
config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
|
config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
|
||||||
wantReplies;
|
wantReplies;
|
||||||
|
|
||||||
|
p->decoded.want_response = requestWantResponse;
|
||||||
if (_shorterTimeout)
|
if (_shorterTimeout)
|
||||||
p->priority = meshtastic_MeshPacket_Priority_DEFAULT;
|
p->priority = meshtastic_MeshPacket_Priority_DEFAULT;
|
||||||
else
|
else
|
||||||
@@ -89,6 +117,13 @@ void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t cha
|
|||||||
|
|
||||||
meshtastic_MeshPacket *NodeInfoModule::allocReply()
|
meshtastic_MeshPacket *NodeInfoModule::allocReply()
|
||||||
{
|
{
|
||||||
|
if (suppressReplyForCurrentRequest) {
|
||||||
|
LOG_DEBUG("Skip send NodeInfo since we heard the requester <12h ago");
|
||||||
|
ignoreRequest = true;
|
||||||
|
suppressReplyForCurrentRequest = false;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (!airTime->isTxAllowedChannelUtil(false)) {
|
if (!airTime->isTxAllowedChannelUtil(false)) {
|
||||||
ignoreRequest = true; // Mark it as ignored for MeshModule
|
ignoreRequest = true; // Mark it as ignored for MeshModule
|
||||||
LOG_DEBUG("Skip send NodeInfo > 40%% ch. util");
|
LOG_DEBUG("Skip send NodeInfo > 40%% ch. util");
|
||||||
@@ -125,6 +160,29 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NodeInfoModule::pruneLastNodeInfoCache()
|
||||||
|
{
|
||||||
|
if (!nodeDB || !nodeDB->meshNodes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const size_t maxEntries = nodeDB->meshNodes->size();
|
||||||
|
|
||||||
|
for (auto it = lastNodeInfoSeen.begin(); it != lastNodeInfoSeen.end();) {
|
||||||
|
if (!nodeDB->getMeshNode(it->first)) {
|
||||||
|
it = lastNodeInfoSeen.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!lastNodeInfoSeen.empty() && lastNodeInfoSeen.size() > maxEntries) {
|
||||||
|
auto oldestIt = std::min_element(lastNodeInfoSeen.begin(), lastNodeInfoSeen.end(),
|
||||||
|
[](const std::pair<const NodeNum, uint32_t> &lhs,
|
||||||
|
const std::pair<const NodeNum, uint32_t> &rhs) { return lhs.second < rhs.second; });
|
||||||
|
lastNodeInfoSeen.erase(oldestIt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NodeInfoModule::NodeInfoModule()
|
NodeInfoModule::NodeInfoModule()
|
||||||
: ProtobufModule("nodeinfo", meshtastic_PortNum_NODEINFO_APP, &meshtastic_User_msg), concurrency::OSThread("NodeInfo")
|
: ProtobufModule("nodeinfo", meshtastic_PortNum_NODEINFO_APP, &meshtastic_User_msg), concurrency::OSThread("NodeInfo")
|
||||||
{
|
{
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user