mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-14 23:02:53 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c53c959cbd | ||
|
|
14a790cec5 | ||
|
|
51b83a2ca2 | ||
|
|
b0812cec27 | ||
|
|
05526df7c8 | ||
|
|
c5c634ee27 | ||
|
|
359338db32 | ||
|
|
1dfad22f5f | ||
|
|
15e04ef048 | ||
|
|
99c4096517 |
@@ -51,7 +51,7 @@ for f in .clusterfuzzlite/*_fuzzer.cpp; do
|
||||
fuzzer=$(basename "$f" .cpp)
|
||||
cp -f "$f" src/fuzzer.cpp
|
||||
pio run -vvv --environment "$PIO_ENV"
|
||||
program="$PLATFORMIO_WORKSPACE_DIR/build/$PIO_ENV/meshtasticd"
|
||||
program="$PLATFORMIO_WORKSPACE_DIR/build/$PIO_ENV/program"
|
||||
cp "$program" "$OUT/$fuzzer"
|
||||
|
||||
# Copy shared libraries used by the fuzzer.
|
||||
|
||||
1
.github/actionlint.yaml
vendored
1
.github/actionlint.yaml
vendored
@@ -2,5 +2,4 @@
|
||||
self-hosted-runner:
|
||||
# Labels of self-hosted runner in array of strings.
|
||||
labels:
|
||||
- arctastic
|
||||
- test-runner
|
||||
|
||||
2
.github/actions/build-variant/action.yml
vendored
2
.github/actions/build-variant/action.yml
vendored
@@ -102,7 +102,7 @@ runs:
|
||||
- name: Store binaries as an artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: firmware-${{ inputs.arch }}-${{ inputs.board }}-${{ steps.version.outputs.long }}
|
||||
name: firmware-${{ inputs.arch }}-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
|
||||
overwrite: true
|
||||
path: |
|
||||
${{ inputs.artifact-paths }}
|
||||
|
||||
21
.github/workflows/build_firmware.yml
vendored
21
.github/workflows/build_firmware.yml
vendored
@@ -18,8 +18,7 @@ permissions: read-all
|
||||
jobs:
|
||||
pio-build:
|
||||
name: build-${{ inputs.platform }}
|
||||
# Use 'arctastic' self-hosted runner pool when building in the main repo
|
||||
runs-on: ${{ github.repository_owner == 'meshtastic' && 'arctastic' || 'ubuntu-latest' }}
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
artifact-id: ${{ steps.upload.outputs.artifact-id }}
|
||||
steps:
|
||||
@@ -56,29 +55,15 @@ jobs:
|
||||
ota_firmware_source: ${{ steps.ota_dir.outputs.src || '' }}
|
||||
ota_firmware_target: ${{ steps.ota_dir.outputs.tgt || '' }}
|
||||
|
||||
- name: Echo manifest from release/firmware-*.mt.json to job summary
|
||||
if: ${{ always() }}
|
||||
env:
|
||||
PIO_ENV: ${{ inputs.pio_env }}
|
||||
run: |
|
||||
echo "## Manifest: \`$PIO_ENV\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```json' >> $GITHUB_STEP_SUMMARY
|
||||
cat release/firmware-*.mt.json >> $GITHUB_STEP_SUMMARY
|
||||
echo '' >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Store binaries as an artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
id: upload
|
||||
with:
|
||||
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}
|
||||
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}.zip
|
||||
overwrite: true
|
||||
path: |
|
||||
release/*.mt.json
|
||||
release/*.bin
|
||||
release/*.elf
|
||||
release/*.uf2
|
||||
release/*.hex
|
||||
release/*.zip
|
||||
release/device-*.sh
|
||||
release/device-*.bat
|
||||
release/*-ota.zip
|
||||
|
||||
6
.github/workflows/build_one_target.yml
vendored
6
.github/workflows/build_one_target.yml
vendored
@@ -119,7 +119,7 @@ jobs:
|
||||
./firmware-*.bin
|
||||
./firmware-*.uf2
|
||||
./firmware-*.hex
|
||||
./firmware-*.zip
|
||||
./firmware-*-ota.zip
|
||||
./device-*.sh
|
||||
./device-*.bat
|
||||
./littlefs-*.bin
|
||||
@@ -139,8 +139,8 @@ jobs:
|
||||
|
||||
- name: Device scripts permissions
|
||||
run: |
|
||||
chmod +x ./output/device-install.sh || true
|
||||
chmod +x ./output/device-update.sh || true
|
||||
chmod +x ./output/device-install.sh
|
||||
chmod +x ./output/device-update.sh
|
||||
|
||||
- name: Zip firmware
|
||||
run: zip -j -9 -r ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output
|
||||
|
||||
50
.github/workflows/main_matrix.yml
vendored
50
.github/workflows/main_matrix.yml
vendored
@@ -177,17 +177,19 @@ jobs:
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
|
||||
- name: Move files up
|
||||
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
|
||||
|
||||
- name: Repackage in single firmware zip
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||
overwrite: true
|
||||
path: |
|
||||
./firmware-*.mt.json
|
||||
./firmware-*.bin
|
||||
./firmware-*.uf2
|
||||
./firmware-*.hex
|
||||
./firmware-*.zip
|
||||
./firmware-*-ota.zip
|
||||
./device-*.sh
|
||||
./device-*.bat
|
||||
./littlefs-*.bin
|
||||
@@ -207,8 +209,8 @@ jobs:
|
||||
|
||||
- name: Device scripts permissions
|
||||
run: |
|
||||
chmod +x ./output/device-install.sh || true
|
||||
chmod +x ./output/device-update.sh || true
|
||||
chmod +x ./output/device-install.sh
|
||||
chmod +x ./output/device-update.sh
|
||||
|
||||
- name: Zip firmware
|
||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||
@@ -216,7 +218,7 @@ jobs:
|
||||
- name: Repackage in single elfs zip
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
||||
overwrite: true
|
||||
path: ./*.elf
|
||||
retention-days: 30
|
||||
@@ -234,7 +236,6 @@ jobs:
|
||||
outputs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
needs:
|
||||
- setup
|
||||
- version
|
||||
- gather-artifacts
|
||||
- build-debian-src
|
||||
@@ -243,6 +244,11 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
- name: Create release
|
||||
uses: softprops/action-gh-release@v2
|
||||
id: create_release
|
||||
@@ -278,25 +284,10 @@ jobs:
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -lR
|
||||
|
||||
- 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@v5
|
||||
with:
|
||||
name: manifest-${{ needs.version.outputs.long }}
|
||||
overwrite: true
|
||||
path: firmware-${{ needs.version.outputs.long }}.json
|
||||
|
||||
- name: Add sources to GitHub Release
|
||||
- name: Add Linux sources to GtiHub Release
|
||||
# Only run when targeting master branch with workflow_dispatch
|
||||
if: ${{ github.ref_name == 'master' }}
|
||||
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/platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip
|
||||
env:
|
||||
@@ -338,15 +329,15 @@ jobs:
|
||||
|
||||
- name: Device scripts permissions
|
||||
run: |
|
||||
chmod +x ./output/device-install.sh || true
|
||||
chmod +x ./output/device-update.sh || true
|
||||
chmod +x ./output/device-install.sh
|
||||
chmod +x ./output/device-update.sh
|
||||
|
||||
- name: Zip firmware
|
||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
||||
merge-multiple: true
|
||||
path: ./elfs
|
||||
|
||||
@@ -382,19 +373,12 @@ jobs:
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
- name: Get firmware artifacts
|
||||
uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v6
|
||||
with:
|
||||
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
|
||||
merge-multiple: true
|
||||
path: ./publish
|
||||
|
||||
- name: Get manifest artifact
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
pattern: manifest-${{ needs.version.outputs.long }}
|
||||
path: ./publish
|
||||
|
||||
- name: Publish firmware to meshtastic.github.io
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
env:
|
||||
|
||||
19
.github/workflows/merge_queue.yml
vendored
19
.github/workflows/merge_queue.yml
vendored
@@ -168,7 +168,7 @@ jobs:
|
||||
./firmware-*.bin
|
||||
./firmware-*.uf2
|
||||
./firmware-*.hex
|
||||
./firmware-*.zip
|
||||
./firmware-*-ota.zip
|
||||
./device-*.sh
|
||||
./device-*.bat
|
||||
./littlefs-*.bin
|
||||
@@ -188,8 +188,8 @@ jobs:
|
||||
|
||||
- name: Device scripts permissions
|
||||
run: |
|
||||
chmod +x ./output/device-install.sh || true
|
||||
chmod +x ./output/device-update.sh || true
|
||||
chmod +x ./output/device-install.sh
|
||||
chmod +x ./output/device-update.sh
|
||||
|
||||
- name: Zip firmware
|
||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||
@@ -197,7 +197,7 @@ jobs:
|
||||
- name: Repackage in single elfs zip
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
||||
overwrite: true
|
||||
path: ./*.elf
|
||||
retention-days: 30
|
||||
@@ -223,6 +223,11 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
- name: Create release
|
||||
uses: softprops/action-gh-release@v2
|
||||
id: create_release
|
||||
@@ -303,15 +308,15 @@ jobs:
|
||||
|
||||
- name: Device scripts permissions
|
||||
run: |
|
||||
chmod +x ./output/device-install.sh || true
|
||||
chmod +x ./output/device-update.sh || true
|
||||
chmod +x ./output/device-install.sh
|
||||
chmod +x ./output/device-update.sh
|
||||
|
||||
- name: Zip firmware
|
||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
|
||||
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
|
||||
merge-multiple: true
|
||||
path: ./elfs
|
||||
|
||||
|
||||
2
.github/workflows/pr_enforce_labels.yml
vendored
2
.github/workflows/pr_enforce_labels.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
with:
|
||||
script: |
|
||||
const labels = context.payload.pull_request.labels.map(label => label.name);
|
||||
const requiredLabels = ['bugfix', 'enhancement', 'hardware-support', 'dependencies', 'submodules', 'github_actions', 'trunk', 'cleanup'];
|
||||
const requiredLabels = ['bugfix', 'enhancement', 'hardware-support', 'dependencies', 'submodules', 'github_actions', 'trunk'];
|
||||
const hasRequiredLabel = labels.some(label => requiredLabels.includes(label));
|
||||
if (!hasRequiredLabel) {
|
||||
core.setFailed(`PR must have at least one of the following labels before it can be merged: ${requiredLabels.join(', ')}.`);
|
||||
|
||||
2
.github/workflows/pr_tests.yml
vendored
2
.github/workflows/pr_tests.yml
vendored
@@ -52,7 +52,7 @@ jobs:
|
||||
if: needs.native-tests.result != 'skipped'
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: platformio-test-report-${{ steps.version.outputs.long }}
|
||||
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
|
||||
merge-multiple: true
|
||||
|
||||
- name: Parse test results and create detailed summary
|
||||
|
||||
2
.github/workflows/stale_bot.yml
vendored
2
.github/workflows/stale_bot.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Stale PR+Issues
|
||||
uses: actions/stale@v10.1.1
|
||||
uses: actions/stale@v10.1.0
|
||||
with:
|
||||
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.
|
||||
|
||||
16
.github/workflows/test_native.yml
vendored
16
.github/workflows/test_native.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
- name: Integration test
|
||||
run: |
|
||||
.pio/build/coverage/meshtasticd -s &
|
||||
.pio/build/coverage/program -s &
|
||||
PID=$!
|
||||
timeout 20 bash -c "until ls -al /proc/$PID/fd | grep socket; do sleep 1; done"
|
||||
echo "Simulator started, launching python test..."
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v5
|
||||
if: always() # run this step even if previous step failed
|
||||
with:
|
||||
name: lcov-coverage-info-native-simulator-test-${{ steps.version.outputs.long }}
|
||||
name: lcov-coverage-info-native-simulator-test-${{ steps.version.outputs.long }}.zip
|
||||
overwrite: true
|
||||
path: ./coverage_*.info
|
||||
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
if: always() # run this step even if previous step failed
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: platformio-test-report-${{ steps.version.outputs.long }}
|
||||
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
|
||||
overwrite: true
|
||||
path: ./testreport.xml
|
||||
|
||||
@@ -111,7 +111,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v5
|
||||
if: always() # run this step even if previous step failed
|
||||
with:
|
||||
name: lcov-coverage-info-native-platformio-tests-${{ steps.version.outputs.long }}
|
||||
name: lcov-coverage-info-native-platformio-tests-${{ steps.version.outputs.long }}.zip
|
||||
overwrite: true
|
||||
path: ./coverage_*.info
|
||||
|
||||
@@ -139,11 +139,11 @@ jobs:
|
||||
- name: Download test artifacts
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: platformio-test-report-${{ steps.version.outputs.long }}
|
||||
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
|
||||
merge-multiple: true
|
||||
|
||||
- name: Test Report
|
||||
uses: dorny/test-reporter@v2.3.0
|
||||
uses: dorny/test-reporter@v2.2.0
|
||||
with:
|
||||
name: PlatformIO Tests
|
||||
path: testreport.xml
|
||||
@@ -152,7 +152,7 @@ jobs:
|
||||
- name: Download coverage artifacts
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }}
|
||||
pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }}.zip
|
||||
path: code-coverage-report
|
||||
merge-multiple: true
|
||||
|
||||
@@ -165,5 +165,5 @@ jobs:
|
||||
- name: Save Code Coverage Report
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: code-coverage-report-${{ steps.version.outputs.long }}
|
||||
name: code-coverage-report-${{ steps.version.outputs.long }}.zip
|
||||
path: code-coverage-report
|
||||
|
||||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
# - uses: actions/setup-python@v6
|
||||
# - uses: actions/setup-python@v5
|
||||
# with:
|
||||
# python-version: '3.10'
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@ plugins:
|
||||
lint:
|
||||
enabled:
|
||||
- checkov@3.2.495
|
||||
- renovate@42.30.4
|
||||
- prettier@3.7.4
|
||||
- trufflehog@3.91.2
|
||||
- renovate@42.24.1
|
||||
- prettier@3.6.2
|
||||
- trufflehog@3.91.1
|
||||
- yamllint@1.37.1
|
||||
- bandit@1.9.2
|
||||
- trivy@0.67.2
|
||||
- taplo@0.10.0
|
||||
- ruff@0.14.7
|
||||
- ruff@0.14.6
|
||||
- isort@7.0.0
|
||||
- markdownlint@0.46.0
|
||||
- oxipng@9.1.5
|
||||
|
||||
@@ -28,7 +28,7 @@ RUN bash ./bin/build-native.sh "$PIO_ENV" && \
|
||||
|
||||
# ##### PRODUCTION BUILD #############
|
||||
|
||||
FROM alpine:3.23
|
||||
FROM alpine:3.22
|
||||
LABEL org.opencontainers.image.title="Meshtastic" \
|
||||
org.opencontainers.image.description="Alpine Meshtastic daemon" \
|
||||
org.opencontainers.image.url="https://meshtastic.org" \
|
||||
|
||||
@@ -2,16 +2,10 @@
|
||||
[esp32_base]
|
||||
extends = arduino_base
|
||||
custom_esp32_kind = esp32
|
||||
custom_mtjson_part =
|
||||
platform =
|
||||
# renovate: datasource=custom.pio depName=platformio/espressif32 packageName=platformio/platform/espressif32
|
||||
platformio/espressif32@6.11.0
|
||||
|
||||
extra_scripts =
|
||||
${env.extra_scripts}
|
||||
pre:extra_scripts/esp32_pre.py
|
||||
extra_scripts/esp32_extra.py
|
||||
|
||||
build_src_filter =
|
||||
${arduino_base.build_src_filter} -<platform/nrf52/> -<platform/stm32wl> -<platform/rp2xx0> -<mesh/eth/> -<mesh/raspihttp>
|
||||
|
||||
@@ -63,11 +57,11 @@ lib_deps =
|
||||
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
|
||||
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
|
||||
# renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib
|
||||
https://github.com/lewisxhe/XPowersLib/archive/v0.3.2.zip
|
||||
https://github.com/lewisxhe/XPowersLib/archive/v0.3.1.zip
|
||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||
rweather/Crypto@0.4.0
|
||||
https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
|
||||
|
||||
lib_ignore =
|
||||
segger_rtt
|
||||
@@ -28,11 +28,11 @@ lib_deps =
|
||||
${environmental_extra.lib_deps}
|
||||
${radiolib_base.lib_deps}
|
||||
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
|
||||
lewisxhe/XPowersLib@0.3.2
|
||||
lewisxhe/XPowersLib@0.3.1
|
||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||
rweather/Crypto@0.4.0
|
||||
https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
|
||||
|
||||
build_src_filter =
|
||||
${esp32_base.build_src_filter} -<mesh/http>
|
||||
@@ -11,13 +11,9 @@ platform_packages =
|
||||
; Don't renovate toolchain-gccarmnoneeabi
|
||||
platformio/toolchain-gccarmnoneeabi@~1.90301.0
|
||||
|
||||
extra_scripts =
|
||||
${env.extra_scripts}
|
||||
extra_scripts/nrf52_extra.py
|
||||
|
||||
build_type = release
|
||||
build_type = debug
|
||||
build_flags =
|
||||
-include variants/nrf52840/cpp_overrides/lfs_util.h
|
||||
-include arch/nrf52/cpp_overrides/lfs_util.h
|
||||
${arduino_base.build_flags}
|
||||
-DSERIAL_BUFFER_SIZE=1024
|
||||
-Wno-unused-variable
|
||||
@@ -25,17 +21,6 @@ build_flags =
|
||||
-DLFS_NO_ASSERT ; Disable LFS assertions , see https://github.com/meshtastic/firmware/pull/3818
|
||||
-DMESHTASTIC_EXCLUDE_AUDIO=1
|
||||
-DMESHTASTIC_EXCLUDE_PAXCOUNTER=1
|
||||
-Os
|
||||
build_unflags =
|
||||
-Ofast
|
||||
-Og
|
||||
-ggdb3
|
||||
-ggdb2
|
||||
-g3
|
||||
-g2
|
||||
-g
|
||||
-g1
|
||||
-g0
|
||||
|
||||
build_src_filter =
|
||||
${arduino_base.build_src_filter} -<platform/esp32/> -<platform/stm32wl> -<nimble/> -<mesh/wifi/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<platform/rp2xx0> -<mesh/eth/> -<mesh/raspihttp> -<serialization/>
|
||||
@@ -44,7 +29,7 @@ lib_deps=
|
||||
${arduino_base.lib_deps}
|
||||
${radiolib_base.lib_deps}
|
||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||
rweather/Crypto@0.4.0
|
||||
https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
|
||||
|
||||
lib_ignore =
|
||||
BluetoothOTA
|
||||
@@ -24,7 +24,8 @@ lib_deps =
|
||||
${radiolib_base.lib_deps}
|
||||
${environmental_base.lib_deps}
|
||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||
rweather/Crypto@0.4.0
|
||||
#rweather/Crypto@0.4.0
|
||||
https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
|
||||
# renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX
|
||||
lovyan03/LovyanGFX@^1.2.0
|
||||
# renovate: datasource=git-refs depName=libch341-spi-userspace packageName=https://github.com/pine64/libch341-spi-userspace gitBranch=main
|
||||
@@ -31,4 +31,4 @@ lib_deps =
|
||||
${environmental_extra.lib_deps}
|
||||
${radiolib_base.lib_deps}
|
||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||
rweather/Crypto@0.4.0
|
||||
https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
|
||||
@@ -28,4 +28,4 @@ lib_deps =
|
||||
${environmental_extra.lib_deps}
|
||||
${radiolib_base.lib_deps}
|
||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||
rweather/Crypto@0.4.0
|
||||
https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
|
||||
@@ -8,7 +8,7 @@ platform_packages =
|
||||
platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32/archive/2.10.1.zip
|
||||
extra_scripts =
|
||||
${env.extra_scripts}
|
||||
extra_scripts/stm32_extra.py
|
||||
post:extra_scripts/extra_stm32.py
|
||||
|
||||
build_type = release
|
||||
|
||||
@@ -53,7 +53,7 @@ lib_deps =
|
||||
${radiolib_base.lib_deps}
|
||||
|
||||
# renovate: datasource=git-refs depName=caveman99-stm32-Crypto packageName=https://github.com/caveman99/Crypto gitBranch=main
|
||||
https://github.com/caveman99/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
|
||||
https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
|
||||
|
||||
lib_ignore =
|
||||
OneButton
|
||||
@@ -1,165 +0,0 @@
|
||||
#!/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,8 +5,7 @@ set -e
|
||||
VERSION=`bin/buildinfo.py long`
|
||||
SHORT_VERSION=`bin/buildinfo.py short`
|
||||
|
||||
BUILDDIR=.pio/build/$1
|
||||
OUTDIR=release
|
||||
OUTDIR=release/
|
||||
|
||||
rm -f $OUTDIR/firmware*
|
||||
rm -r $OUTDIR/* || true
|
||||
@@ -15,7 +14,7 @@ rm -r $OUTDIR/* || true
|
||||
platformio pkg install -e $1
|
||||
|
||||
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
|
||||
rm -f $BUILDDIR/firmware*
|
||||
rm -f .pio/build/$1/firmware.*
|
||||
|
||||
# The shell vars the build tool expects to find
|
||||
export APP_VERSION=$VERSION
|
||||
@@ -23,14 +22,16 @@ export APP_VERSION=$VERSION
|
||||
basename=firmware-$1-$VERSION
|
||||
|
||||
pio run --environment $1 # -v
|
||||
|
||||
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
|
||||
SRCELF=.pio/build/$1/firmware.elf
|
||||
cp $SRCELF $OUTDIR/$basename.elf
|
||||
|
||||
echo "Copying ESP32 bin file"
|
||||
cp $BUILDDIR/$basename.factory.bin $OUTDIR/$basename.factory.bin
|
||||
SRCBIN=.pio/build/$1/firmware.factory.bin
|
||||
cp $SRCBIN $OUTDIR/$basename.bin
|
||||
|
||||
echo "Copying ESP32 update bin file"
|
||||
cp $BUILDDIR/$basename.bin $OUTDIR/$basename.bin
|
||||
SRCBIN=.pio/build/$1/firmware.bin
|
||||
cp $SRCBIN $OUTDIR/$basename-update.bin
|
||||
|
||||
echo "Building Filesystem for ESP32 targets"
|
||||
# If you want to build the webui, uncomment the following lines
|
||||
@@ -39,13 +40,7 @@ echo "Building Filesystem for ESP32 targets"
|
||||
# # Remove webserver files from the filesystem and rebuild
|
||||
# ls -l data/static # Diagnostic list of files
|
||||
# rm -rf data/static
|
||||
pio run --environment $1 -t buildfs --disable-auto-clean
|
||||
cp $BUILDDIR/littlefs-$1-$VERSION.bin $OUTDIR/littlefs-$1-$VERSION.bin
|
||||
cp bin/device-install.* $OUTDIR/
|
||||
cp bin/device-update.* $OUTDIR/
|
||||
|
||||
# Generate the manifest file
|
||||
echo "Generating Meshtastic manifest"
|
||||
TIMEFORMAT="Generated manifest in %E seconds"
|
||||
time pio run --environment $1 -t mtjson --silent --disable-auto-clean
|
||||
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,19 +17,15 @@ VERSION=$(bin/buildinfo.py long)
|
||||
SHORT_VERSION=$(bin/buildinfo.py short)
|
||||
PIO_ENV=${1:-native}
|
||||
|
||||
BUILDDIR=.pio/build/$PIO_ENV
|
||||
OUTDIR=release
|
||||
OUTDIR=release/
|
||||
|
||||
rm -f $OUTDIR/meshtasticd*
|
||||
rm -f $OUTDIR/firmware*
|
||||
|
||||
mkdir -p $OUTDIR/
|
||||
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
|
||||
pio pkg install --environment "$PIO_ENV" || platformioFailed
|
||||
pio run --environment "$PIO_ENV" || platformioFailed
|
||||
|
||||
cp "$BUILDDIR/meshtasticd" "$OUTDIR/meshtasticd_linux_$(uname -m)"
|
||||
cp bin/native-install.* $OUTDIR/
|
||||
cp ".pio/build/$PIO_ENV/program" "$OUTDIR/meshtasticd_linux_$(uname -m)"
|
||||
cp bin/native-install.* $OUTDIR
|
||||
|
||||
@@ -5,8 +5,7 @@ set -e
|
||||
VERSION=$(bin/buildinfo.py long)
|
||||
SHORT_VERSION=$(bin/buildinfo.py short)
|
||||
|
||||
BUILDDIR=.pio/build/$1
|
||||
OUTDIR=release
|
||||
OUTDIR=release/
|
||||
|
||||
rm -f $OUTDIR/firmware*
|
||||
rm -r $OUTDIR/* || true
|
||||
@@ -15,7 +14,7 @@ rm -r $OUTDIR/* || true
|
||||
platformio pkg install -e $1
|
||||
|
||||
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
|
||||
rm -f $BUILDDIR/firmware*
|
||||
rm -f .pio/build/$1/firmware.*
|
||||
|
||||
# The shell vars the build tool expects to find
|
||||
export APP_VERSION=$VERSION
|
||||
@@ -23,32 +22,32 @@ export APP_VERSION=$VERSION
|
||||
basename=firmware-$1-$VERSION
|
||||
|
||||
pio run --environment $1 # -v
|
||||
SRCELF=.pio/build/$1/firmware.elf
|
||||
cp $SRCELF $OUTDIR/$basename.elf
|
||||
|
||||
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
|
||||
echo "Generating NRF52 dfu file"
|
||||
DFUPKG=.pio/build/$1/firmware.zip
|
||||
cp $DFUPKG $OUTDIR/$basename-ota.zip
|
||||
|
||||
echo "Copying NRF52 dfu (OTA) file"
|
||||
cp $BUILDDIR/$basename.zip $OUTDIR/$basename.zip
|
||||
echo "Generating NRF52 uf2 file"
|
||||
SRCHEX=.pio/build/$1/firmware.hex
|
||||
|
||||
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 WM1110 target, merge hex with softdevice 7.3.0
|
||||
if (echo $1 | grep -q "wio-sdk-wm1110"); then
|
||||
echo "Copying .merged.hex file"
|
||||
SRCHEX=$BUILDDIR/$basename.merged.hex
|
||||
cp $SRCHEX $OUTDIR/
|
||||
echo "Merging with softdevice"
|
||||
bin/mergehex -m bin/s140_nrf52_7.3.0_softdevice.hex $SRCHEX -o .pio/build/$1/$basename.hex
|
||||
SRCHEX=.pio/build/$1/$basename.hex
|
||||
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
|
||||
|
||||
if (echo $1 | grep -q "rak4631"); then
|
||||
echo "Copying .hex file"
|
||||
cp $SRCHEX $OUTDIR/
|
||||
fi
|
||||
|
||||
# Generate the manifest file
|
||||
echo "Generating Meshtastic manifest"
|
||||
TIMEFORMAT="Generated manifest in %E seconds"
|
||||
time pio run --environment $1 -t mtjson --silent --disable-auto-clean
|
||||
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json
|
||||
echo "Copying hex file"
|
||||
cp .pio/build/$1/firmware.hex $OUTDIR/$basename.hex
|
||||
fi
|
||||
@@ -5,8 +5,7 @@ set -e
|
||||
VERSION=`bin/buildinfo.py long`
|
||||
SHORT_VERSION=`bin/buildinfo.py short`
|
||||
|
||||
BUILDDIR=.pio/build/$1
|
||||
OUTDIR=release
|
||||
OUTDIR=release/
|
||||
|
||||
rm -f $OUTDIR/firmware*
|
||||
rm -r $OUTDIR/* || true
|
||||
@@ -15,7 +14,7 @@ rm -r $OUTDIR/* || true
|
||||
platformio pkg install -e $1
|
||||
|
||||
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
|
||||
rm -f $BUILDDIR/firmware*
|
||||
rm -f .pio/build/$1/firmware.*
|
||||
|
||||
# The shell vars the build tool expects to find
|
||||
export APP_VERSION=$VERSION
|
||||
@@ -23,14 +22,12 @@ export APP_VERSION=$VERSION
|
||||
basename=firmware-$1-$VERSION
|
||||
|
||||
pio run --environment $1 # -v
|
||||
|
||||
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
|
||||
SRCELF=.pio/build/$1/firmware.elf
|
||||
cp $SRCELF $OUTDIR/$basename.elf
|
||||
|
||||
echo "Copying uf2 file"
|
||||
cp $BUILDDIR/$basename.uf2 $OUTDIR/$basename.uf2
|
||||
SRCBIN=.pio/build/$1/firmware.uf2
|
||||
cp $SRCBIN $OUTDIR/$basename.uf2
|
||||
|
||||
# Generate the manifest file
|
||||
echo "Generating Meshtastic manifest"
|
||||
TIMEFORMAT="Generated manifest in %E seconds"
|
||||
time pio run --environment $1 -t mtjson --silent --disable-auto-clean
|
||||
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json
|
||||
cp bin/device-install.* $OUTDIR
|
||||
cp bin/device-update.* $OUTDIR
|
||||
|
||||
@@ -5,8 +5,7 @@ set -e
|
||||
VERSION=$(bin/buildinfo.py long)
|
||||
SHORT_VERSION=$(bin/buildinfo.py short)
|
||||
|
||||
BUILDDIR=.pio/build/$1
|
||||
OUTDIR=release
|
||||
OUTDIR=release/
|
||||
|
||||
rm -f $OUTDIR/firmware*
|
||||
rm -r $OUTDIR/* || true
|
||||
@@ -15,7 +14,7 @@ rm -r $OUTDIR/* || true
|
||||
platformio pkg install -e $1
|
||||
|
||||
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
|
||||
rm -f $BUILDDIR/firmware*
|
||||
rm -f .pio/build/$1/firmware.*
|
||||
|
||||
# The shell vars the build tool expects to find
|
||||
export APP_VERSION=$VERSION
|
||||
@@ -23,14 +22,8 @@ export APP_VERSION=$VERSION
|
||||
basename=firmware-$1-$VERSION
|
||||
|
||||
pio run --environment $1 # -v
|
||||
SRCELF=.pio/build/$1/firmware.elf
|
||||
cp $SRCELF $OUTDIR/$basename.elf
|
||||
|
||||
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
|
||||
|
||||
echo "Copying STM32 bin file"
|
||||
cp $BUILDDIR/$basename.bin $OUTDIR/$basename.bin
|
||||
|
||||
# Generate the manifest file
|
||||
echo "Generating Meshtastic manifest"
|
||||
TIMEFORMAT="Generated manifest in %E seconds"
|
||||
time pio run --environment $1 -t mtjson --silent --disable-auto-clean
|
||||
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json
|
||||
SRCBIN=.pio/build/$1/firmware.bin
|
||||
cp $SRCBIN $OUTDIR/$basename.bin
|
||||
|
||||
@@ -5,14 +5,22 @@ TITLE Meshtastic device-install
|
||||
SET "SCRIPT_NAME=%~nx0"
|
||||
SET "DEBUG=0"
|
||||
SET "PYTHON="
|
||||
SET "TFT_BUILD=0"
|
||||
SET "BIGDB8=0"
|
||||
SET "MUIDB8=0"
|
||||
SET "BIGDB16=0"
|
||||
SET "ESPTOOL_BAUD=115200"
|
||||
SET "ESPTOOL_CMD="
|
||||
SET "LOGCOUNTER=0"
|
||||
SET "BPS_RESET=0"
|
||||
@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 FIXME: Determine mcu from PlatformIO variant, this is unmaintainable.
|
||||
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 "C3=esp32c3"
|
||||
@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
|
||||
:help
|
||||
@@ -21,7 +29,7 @@ ECHO.
|
||||
ECHO Usage: %SCRIPT_NAME% -f filename [-p PORT] [-P python] [--1200bps-reset]
|
||||
ECHO.
|
||||
ECHO Options:
|
||||
ECHO -f filename The firmware .factory.bin file to flash. Custom to your device type and region. (required)
|
||||
ECHO -f filename The firmware .bin file to flash. Custom to your device type and region. (required)
|
||||
ECHO The file must be located in this current directory.
|
||||
ECHO -p PORT Set the environment variable for ESPTOOL_PORT.
|
||||
ECHO If not set, ESPTOOL iterates all ports (Dangerous).
|
||||
@@ -32,12 +40,12 @@ ECHO --1200bps-reset Attempt to place the device in correct mode. (1200bps
|
||||
ECHO Some hardware requires this twice.
|
||||
ECHO.
|
||||
ECHO Example: %SCRIPT_NAME% -p COM17 --1200bps-reset
|
||||
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.factory.bin -p COM11
|
||||
ECHO Example: %SCRIPT_NAME% -f firmware-t-deck-tft-2.6.0.0b106d4.bin -p COM11
|
||||
ECHO Example: %SCRIPT_NAME% -f firmware-unphone-2.6.0.0b106d4.bin -p COM11
|
||||
GOTO eof
|
||||
|
||||
:version
|
||||
ECHO %SCRIPT_NAME% [Version 2.7.0]
|
||||
ECHO %SCRIPT_NAME% [Version 2.6.2]
|
||||
ECHO Meshtastic
|
||||
GOTO eof
|
||||
|
||||
@@ -70,8 +78,8 @@ IF "__!FILENAME!__"=="____" (
|
||||
CALL :LOG_MESSAGE ERROR "Filename containing spaces are not supported."
|
||||
GOTO help
|
||||
)
|
||||
IF NOT "__!FILENAME:.factory.bin=!__"=="__!FILENAME!__" (
|
||||
CALL :LOG_MESSAGE ERROR "Filename must be a firmware-*.factory.bin file."
|
||||
IF "__!FILENAME:firmware-=!__"=="__!FILENAME!__" (
|
||||
CALL :LOG_MESSAGE ERROR "Filename must be a firmware-* file."
|
||||
GOTO help
|
||||
)
|
||||
@REM Remove ".\" or "./" file prefix if present.
|
||||
@@ -85,26 +93,12 @@ IF NOT EXIST !FILENAME! (
|
||||
GOTO eof
|
||||
)
|
||||
|
||||
CALL :LOG_MESSAGE DEBUG "Checking for metadata..."
|
||||
@REM Derive metadata filename from firmware filename.
|
||||
SET "METAFILE=!FILENAME:.factory.bin=!.mt.json"
|
||||
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 (
|
||||
CALL :LOG_MESSAGE ERROR "No metadata file found: !METAFILE!"
|
||||
IF NOT "!FILENAME:update=!"=="!FILENAME!" (
|
||||
CALL :LOG_MESSAGE DEBUG "We are working with a *update* file. !FILENAME!"
|
||||
CALL :LOG_MESSAGE INFO "Use script device-update.bat to flash update !FILENAME!."
|
||||
GOTO eof
|
||||
) ELSE (
|
||||
CALL :LOG_MESSAGE DEBUG "We are NOT working with a *update* file. !FILENAME!"
|
||||
)
|
||||
|
||||
:skip-filename
|
||||
@@ -114,7 +108,7 @@ IF NOT "__%PYTHON%__"=="____" (
|
||||
SET "ESPTOOL_CMD=!PYTHON! -m esptool"
|
||||
CALL :LOG_MESSAGE DEBUG "Python interpreter supplied."
|
||||
) 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
|
||||
IF %ERRORLEVEL% EQU 0 (
|
||||
@REM WHERE exits with code 0 if esptool is found.
|
||||
@@ -152,26 +146,100 @@ IF %BPS_RESET% EQU 1 (
|
||||
GOTO eof
|
||||
)
|
||||
|
||||
@REM Extract PROGNAME from %FILENAME% for later use.
|
||||
SET "PROGNAME=!FILENAME:.factory.bin=!"
|
||||
CALL :LOG_MESSAGE DEBUG "Computed PROGNAME: !PROGNAME!"
|
||||
|
||||
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"
|
||||
@REM Check if FILENAME contains "-tft-" and set target partitionScheme accordingly.
|
||||
@REM https://github.com/meshtastic/web-flasher/blob/main/types/resources.ts#L3
|
||||
IF NOT "!FILENAME:-tft-=!"=="!FILENAME!" (
|
||||
CALL :LOG_MESSAGE DEBUG "We are working with a *-tft-* file. !FILENAME!"
|
||||
SET "TFT_BUILD=1"
|
||||
) ELSE (
|
||||
@REM Everything else
|
||||
SET "OTA_FILENAME=bleota.bin"
|
||||
CALL :LOG_MESSAGE DEBUG "We are NOT working with a *-tft-* file. !FILENAME!"
|
||||
)
|
||||
|
||||
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!"
|
||||
|
||||
@REM Set SPIFFS filename with "littlefs-" prefix.
|
||||
SET "SPIFFS_FILENAME=littlefs-!PROGNAME:firmware-=!.bin"
|
||||
SET "SPIFFS_FILENAME=littlefs-%BASENAME%"
|
||||
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 SPIFFS_OFFSET to: !SPIFFS_OFFSET!"
|
||||
|
||||
|
||||
@@ -2,15 +2,69 @@
|
||||
|
||||
PYTHON=${PYTHON:-$(which python3 python | head -n 1)}
|
||||
BPS_RESET=false
|
||||
TFT_BUILD=false
|
||||
MCU=""
|
||||
|
||||
# Constants
|
||||
RESET_BAUD=1200
|
||||
FIRMWARE_OFFSET=0x00
|
||||
# Default littlefs* offset.
|
||||
OFFSET=0x300000
|
||||
# Default OTA Offset
|
||||
OTA_OFFSET=0x260000
|
||||
|
||||
# Variant groups
|
||||
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"
|
||||
)
|
||||
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
|
||||
if "$PYTHON" -m esptool version >/dev/null 2>&1; then
|
||||
@@ -24,14 +78,6 @@ else
|
||||
exit 1
|
||||
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
|
||||
|
||||
# Usage info
|
||||
@@ -43,7 +89,7 @@ Flash image file to device, but first erasing and writing system information.
|
||||
-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 PYTHON Specify alternate python interpreter to use to invoke esptool. (Default: "$PYTHON")
|
||||
-f FILENAME The firmware *.factory.bin file to flash. Custom to your device type and region.
|
||||
-f FILENAME The firmware .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)
|
||||
|
||||
EOF
|
||||
@@ -92,43 +138,69 @@ fi
|
||||
shift
|
||||
}
|
||||
|
||||
if [[ $(basename "$FILENAME") != firmware-*.factory.bin ]]; then
|
||||
echo "Filename must be a firmware-*.factory.bin file."
|
||||
if [[ "$FILENAME" != firmware-* ]]; then
|
||||
echo "Filename must be a firmware-* file."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract PROGNAME from %FILENAME% for later use.
|
||||
PROGNAME="${FILENAME/.factory.bin/}"
|
||||
# Derive metadata filename from %PROGNAME%.
|
||||
METAFILE="${PROGNAME}.mt.json"
|
||||
# Check if FILENAME contains "-tft-" and set target partitionScheme accordingly.
|
||||
if [[ "${FILENAME//-tft-/}" != "$FILENAME" ]]; then
|
||||
TFT_BUILD=true
|
||||
fi
|
||||
|
||||
if [[ -f "$FILENAME" && "$FILENAME" == *.factory.bin ]]; then
|
||||
# Display metadata if it exists
|
||||
if [[ -f "$METAFILE" ]]; then
|
||||
echo "Firmware metadata: ${METAFILE}"
|
||||
jq . "$METAFILE"
|
||||
# Extract relevant fields from metadata
|
||||
if [[ $(jq -r '.part' "$METAFILE") != "null" ]]; then
|
||||
OTA_OFFSET=$(jq -r '.part[] | select(.subtype == "ota_1") | .offset' "$METAFILE")
|
||||
SPIFFS_OFFSET=$(jq -r '.part[] | select(.subtype == "spiffs") | .offset' "$METAFILE")
|
||||
# Extract BASENAME from %FILENAME% for later use.
|
||||
BASENAME="${FILENAME/firmware-/}"
|
||||
|
||||
if [ -f "${FILENAME}" ] && [ -n "${FILENAME##*"update"*}" ]; then
|
||||
# Default littlefs* offset.
|
||||
OFFSET=0x300000
|
||||
|
||||
# Default OTA Offset
|
||||
OTA_OFFSET=0x260000
|
||||
|
||||
# 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
|
||||
MCU=$(jq -r '.mcu' "$METAFILE")
|
||||
else
|
||||
echo "ERROR: No metadata file found at ${METAFILE}"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Determine OTA filename based on MCU type
|
||||
if [ "$MCU" == "esp32s3" ]; then
|
||||
OTAFILE=bleota-s3.bin
|
||||
elif [ "$MCU" == "esp32c3" ]; then
|
||||
OTAFILE=bleota-c3.bin
|
||||
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
|
||||
else
|
||||
OTAFILE=bleota.bin
|
||||
OTAFILE=bleota-s3.bin
|
||||
fi
|
||||
|
||||
# Set SPIFFS filename with "littlefs-" prefix.
|
||||
SPIFFSFILE="littlefs-${PROGNAME/firmware-/}.bin"
|
||||
SPIFFSFILE=littlefs-${BASENAME}
|
||||
|
||||
if [[ ! -f "$FILENAME" ]]; then
|
||||
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.
|
||||
ECHO Example: %SCRIPT_NAME% -p COM17 --change-mode
|
||||
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-update.bin -p COM11
|
||||
GOTO eof
|
||||
|
||||
:version
|
||||
ECHO %SCRIPT_NAME% [Version 2.7.0]
|
||||
ECHO %SCRIPT_NAME% [Version 2.6.2]
|
||||
ECHO Meshtastic
|
||||
GOTO eof
|
||||
|
||||
@@ -78,12 +78,12 @@ IF NOT EXIST !FILENAME! (
|
||||
GOTO eof
|
||||
)
|
||||
|
||||
IF NOT "__!FILENAME:.factory.bin=!__"=="__!FILENAME!__" (
|
||||
CALL :LOG_MESSAGE DEBUG "We are working with a *.factory.bin* file. !FILENAME!"
|
||||
IF "!FILENAME:update=!"=="!FILENAME!" (
|
||||
CALL :LOG_MESSAGE DEBUG "We are NOT working with a *update* file. !FILENAME!"
|
||||
CALL :LOG_MESSAGE INFO "Use script device-install.bat to flash !FILENAME!."
|
||||
GOTO eof
|
||||
) ELSE (
|
||||
CALL :LOG_MESSAGE DEBUG "We are not working with a *.factory.bin* file. !FILENAME!"
|
||||
CALL :LOG_MESSAGE DEBUG "We are working with a *update* file. !FILENAME!"
|
||||
)
|
||||
|
||||
:skip-filename
|
||||
|
||||
@@ -29,7 +29,7 @@ Flash image file to device, leave existing system intact."
|
||||
-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 PYTHON Specify alternate python interpreter to use to invoke esptool. (Default: "$PYTHON")
|
||||
-f FILENAME The *.bin file to flash. Custom to your device type.
|
||||
-f FILENAME The *update.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)
|
||||
|
||||
EOF
|
||||
@@ -78,7 +78,7 @@ fi
|
||||
shift
|
||||
}
|
||||
|
||||
if [[ -f "$FILENAME" && "$FILENAME" != *.factory.bin ]]; then
|
||||
if [ -f "${FILENAME}" ] && [ -z "${FILENAME##*"update"*}" ]; then
|
||||
echo "Trying to flash update ${FILENAME}"
|
||||
$ESPTOOL_CMD --baud $FLASH_BAUD write-flash $UPDATE_OFFSET "${FILENAME}"
|
||||
else
|
||||
|
||||
@@ -75,7 +75,7 @@ TOOLS = {
|
||||
}
|
||||
|
||||
BACKTRACE_REGEX = re.compile(
|
||||
r"\b(0x4[0-9a-fA-F]{7,8}):0x[0-9a-fA-F]{8}\b"
|
||||
r"(?:\s+(0x40[0-2](?:\d|[a-f]|[A-F]){5}):0x(?:\d|[a-f]|[A-F]){8})\b"
|
||||
)
|
||||
EXCEPTION_REGEX = re.compile("^Exception \\((?P<exc>[0-9]*)\\):$")
|
||||
COUNTER_REGEX = re.compile(
|
||||
@@ -89,7 +89,7 @@ POINTER_REGEX = re.compile(
|
||||
STACK_BEGIN = ">>>stack>>>"
|
||||
STACK_END = "<<<stack<<<"
|
||||
STACK_REGEX = re.compile(
|
||||
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.*)?$"
|
||||
"^(?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"])
|
||||
@@ -223,7 +223,7 @@ class AddressResolver(object):
|
||||
if match is None:
|
||||
if last is not None and line.startswith("(inlined by)"):
|
||||
line = line[12:].strip()
|
||||
self._address_map[last] += "\n \\-> inlined by: " + line
|
||||
self._address_map[last] += "\n \-> inlined by: " + line
|
||||
continue
|
||||
|
||||
if match.group("result") == "?? ??:0":
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
set -e
|
||||
pio run --environment native
|
||||
gdbserver --once localhost:2345 .pio/build/native/meshtasticd "$@"
|
||||
gdbserver --once localhost:2345 .pio/build/native/program "$@"
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
set -e
|
||||
pio run --environment native
|
||||
.pio/build/native/meshtasticd "$@"
|
||||
.pio/build/native/program "$@"
|
||||
|
||||
@@ -87,9 +87,6 @@
|
||||
</screenshots>
|
||||
|
||||
<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">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.16</url>
|
||||
</release>
|
||||
|
||||
@@ -2,77 +2,98 @@
|
||||
# trunk-ignore-all(ruff/F821)
|
||||
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
||||
import sys
|
||||
from os.path import join, basename, isfile
|
||||
from os.path import join
|
||||
import subprocess
|
||||
import json
|
||||
import re
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
from readprops import readProps
|
||||
|
||||
Import("env")
|
||||
platform = env.PioPlatform()
|
||||
progname = env.get("PROGNAME")
|
||||
lfsbin = f"{progname.replace('firmware-', 'littlefs-')}.bin"
|
||||
|
||||
def manifest_gather(source, target, env):
|
||||
out = []
|
||||
check_paths = [
|
||||
progname,
|
||||
f"{progname}.elf",
|
||||
f"{progname}.bin",
|
||||
f"{progname}.factory.bin",
|
||||
f"{progname}.hex",
|
||||
f"{progname}.merged.hex",
|
||||
f"{progname}.uf2",
|
||||
f"{progname}.factory.uf2",
|
||||
f"{progname}.zip",
|
||||
lfsbin
|
||||
|
||||
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")
|
||||
flash_size = env.BoardConfig().get("upload.flash_size")
|
||||
flash_freq = env.BoardConfig().get("build.f_flash", "40m")
|
||||
flash_freq = flash_freq.replace("000000L", "m")
|
||||
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():
|
||||
d = {
|
||||
"name": p,
|
||||
"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)
|
||||
|
||||
def manifest_write(files, env):
|
||||
manifest = {
|
||||
"version": verObj["long"],
|
||||
"build_epoch": build_epoch,
|
||||
"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(" Offset | File")
|
||||
for section in sections:
|
||||
sect_adr, sect_file = section.split(" ", 1)
|
||||
print(f" - {sect_adr} | {sect_file}")
|
||||
cmd += [sect_adr, sect_file]
|
||||
|
||||
# Write the manifest to the build directory
|
||||
with open(env.subst("$BUILD_DIR/${PROGNAME}.mt.json"), "w") as f:
|
||||
json.dump(manifest, f, indent=2)
|
||||
print(f" - {hex(app_offset)} | {firmware_name}")
|
||||
cmd += [hex(app_offset), firmware_name]
|
||||
|
||||
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")
|
||||
|
||||
prefsLoc = projenv["PROJECT_DIR"] + "/version.properties"
|
||||
verObj = readProps(prefsLoc)
|
||||
print(f"Using meshtastic platformio-custom.py, firmware version {verObj['long']} on {env.get('PIOENV')}")
|
||||
print("Using meshtastic platformio-custom.py, firmware version " + verObj["long"] + " on " + env.get("PIOENV"))
|
||||
|
||||
# get repository owner if git is installed
|
||||
try:
|
||||
@@ -118,10 +139,10 @@ flags = [
|
||||
"-DBUILD_EPOCH=" + str(build_epoch),
|
||||
] + pref_flags
|
||||
|
||||
print("Using flags:")
|
||||
print ("Using flags:")
|
||||
for flag in flags:
|
||||
print(flag)
|
||||
|
||||
|
||||
projenv.Append(
|
||||
CCFLAGS=flags,
|
||||
)
|
||||
@@ -160,19 +181,3 @@ def load_boot_logo(source, target, env):
|
||||
# Load the boot logo on TFT builds
|
||||
if ("HAS_TFT", 1) in env.get("CPPDEFINES", []):
|
||||
env.AddPreAction('$BUILD_DIR/littlefs.bin', load_boot_logo)
|
||||
|
||||
# Rename (mv) littlefs.bin to include the PROGNAME
|
||||
# This ensures the littlefs.bin is named consistently with the firmware
|
||||
env.AddPostAction('$BUILD_DIR/littlefs.bin', env.VerboseAction(
|
||||
f'mv $BUILD_DIR/littlefs.bin $BUILD_DIR/{lfsbin}',
|
||||
f'Renaming littlefs.bin to {lfsbin}'
|
||||
))
|
||||
|
||||
env.AddCustomTarget(
|
||||
name="mtjson",
|
||||
dependencies=None,
|
||||
actions=[manifest_gather],
|
||||
title="Meshtastic Manifest",
|
||||
description="Generating Meshtastic manifest JSON + Checksums",
|
||||
always_build=True,
|
||||
)
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/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']}")
|
||||
|
||||
# Print the new program name for verification
|
||||
print(f"PROGNAME: {env.get('PROGNAME')}")
|
||||
@@ -3,7 +3,7 @@
|
||||
set -e
|
||||
|
||||
echo "Starting simulator"
|
||||
.pio/build/native/meshtasticd -s &
|
||||
.pio/build/native/program &
|
||||
sleep 20 # 5 seconds was not enough
|
||||
|
||||
echo "Simulator started, launching python test..."
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
6
debian/changelog
vendored
6
debian/changelog
vendored
@@ -1,9 +1,3 @@
|
||||
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
|
||||
|
||||
* Version 2.7.16
|
||||
|
||||
1
debian/rules
vendored
1
debian/rules
vendored
@@ -28,4 +28,5 @@ override_dh_auto_build:
|
||||
# Build with platformio
|
||||
$(PIO_ENV) platformio run -e native-tft
|
||||
# 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
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#!/usr/bin/env python3
|
||||
# trunk-ignore-all(flake8/F821)
|
||||
# trunk-ignore-all(ruff/F821)
|
||||
|
||||
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 Build targets", BUILD_TARGETS)
|
||||
# print("CPP defs", env.get("CPPDEFINES"))
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
#!/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")))
|
||||
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"])
|
||||
@@ -1,73 +0,0 @@
|
||||
#!/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)
|
||||
@@ -1,9 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
# trunk-ignore-all(ruff/F821)
|
||||
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
||||
|
||||
Import("env")
|
||||
|
||||
# Custom HEX from ELF
|
||||
env.AddPostAction(
|
||||
"$BUILD_DIR/${PROGNAME}.elf",
|
||||
@@ -1,50 +0,0 @@
|
||||
#!/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)
|
||||
@@ -33,6 +33,7 @@ BuildRequires: python3dist(grpcio[protobuf])
|
||||
BuildRequires: python3dist(grpcio-tools)
|
||||
BuildRequires: git-core
|
||||
BuildRequires: gcc-c++
|
||||
BuildRequires: (glibc-devel >= 2.38) or pkgconfig(libbsd-overlay)
|
||||
BuildRequires: pkgconfig(yaml-cpp)
|
||||
BuildRequires: pkgconfig(libgpiod)
|
||||
BuildRequires: pkgconfig(bluez)
|
||||
@@ -76,7 +77,7 @@ platformio run -e native-tft
|
||||
%install
|
||||
# Install meshtasticd binary
|
||||
mkdir -p %{buildroot}%{_bindir}
|
||||
install -m 0755 .pio/build/native-tft/meshtasticd %{buildroot}%{_bindir}/meshtasticd
|
||||
install -m 0755 .pio/build/native-tft/program %{buildroot}%{_bindir}/meshtasticd
|
||||
|
||||
# Install portduino VFS dir
|
||||
install -p -d -m 0770 %{buildroot}%{_localstatedir}/lib/meshtasticd
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
default_envs = tbeam
|
||||
|
||||
extra_configs =
|
||||
variants/*/*.ini
|
||||
arch/*/*.ini
|
||||
variants/*/*/platformio.ini
|
||||
variants/*/diy/*/platformio.ini
|
||||
src/graphics/niche/InkHUD/PlatformioConfig.ini
|
||||
@@ -14,9 +14,7 @@ description = Meshtastic
|
||||
|
||||
[env]
|
||||
test_build_src = true
|
||||
extra_scripts =
|
||||
pre:bin/platformio-pre.py
|
||||
bin/platformio-custom.py
|
||||
extra_scripts = bin/platformio-custom.py
|
||||
; 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
|
||||
; of code is a heap corruption bug!
|
||||
@@ -123,7 +121,7 @@ lib_deps =
|
||||
[device-ui_base]
|
||||
lib_deps =
|
||||
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
||||
https://github.com/meshtastic/device-ui/archive/4fb5f24787caa841b58dbf623a52c4c5861d6722.zip
|
||||
https://github.com/meshtastic/device-ui/archive/28167c67dfd13015a0b5eef1828f95fe8e3ab7c3.zip
|
||||
|
||||
; Common libs for environmental measurements in telemetry module
|
||||
[environmental_base]
|
||||
@@ -184,8 +182,8 @@ lib_deps =
|
||||
dfrobot/DFRobot_BMM150@1.0.0
|
||||
# renovate: datasource=custom.pio depName=Adafruit_TSL2561 packageName=adafruit/library/Adafruit TSL2561
|
||||
adafruit/Adafruit TSL2561@1.1.2
|
||||
# renovate: datasource=custom.pio depName=BH1750_WE packageName=wollewald/library/BH1750_WE
|
||||
wollewald/BH1750_WE@1.1.10
|
||||
# renovate: datasource=custom.pio depName=BH1750_WE packageName=wollewald/BH1750_WE@^1.1.10
|
||||
wollewald/BH1750_WE@^1.1.10
|
||||
|
||||
; (not included in native / portduino)
|
||||
[environmental_extra]
|
||||
@@ -207,7 +205,7 @@ lib_deps =
|
||||
# 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
|
||||
# renovate: datasource=custom.pio depName=ClosedCube OPT3001 packageName=closedcube/library/ClosedCube OPT3001
|
||||
closedcube/ClosedCube OPT3001@1.1.2
|
||||
ClosedCube OPT3001@1.1.2
|
||||
# renovate: datasource=custom.pio depName=Bosch BSEC2 packageName=boschsensortec/library/bsec2
|
||||
boschsensortec/bsec2@1.10.2610
|
||||
# renovate: datasource=custom.pio depName=Bosch BME68x packageName=boschsensortec/library/BME68x Sensor Library
|
||||
|
||||
Submodule protobufs updated: 4095e59890...52fa252f1e
@@ -50,11 +50,8 @@ class AudioThread : public concurrency::OSThread
|
||||
delete i2sRtttl;
|
||||
i2sRtttl = nullptr;
|
||||
}
|
||||
|
||||
if (rtttlFile != nullptr) {
|
||||
delete rtttlFile;
|
||||
rtttlFile = nullptr;
|
||||
}
|
||||
delete rtttlFile;
|
||||
rtttlFile = nullptr;
|
||||
|
||||
setCPUFast(false);
|
||||
#ifdef T_LORA_PAGER
|
||||
@@ -102,9 +99,9 @@ class AudioThread : public concurrency::OSThread
|
||||
};
|
||||
|
||||
AudioGeneratorRTTTL *i2sRtttl = nullptr;
|
||||
AudioOutputI2S *audioOut = nullptr;
|
||||
AudioOutputI2S *audioOut;
|
||||
|
||||
AudioFileSourcePROGMEM *rtttlFile = nullptr;
|
||||
AudioFileSourcePROGMEM *rtttlFile;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -16,7 +16,6 @@ struct ToneDuration {
|
||||
};
|
||||
|
||||
// Some common frequencies.
|
||||
#define NOTE_SILENT 1
|
||||
#define NOTE_C3 131
|
||||
#define NOTE_CS3 139
|
||||
#define NOTE_D3 147
|
||||
@@ -30,16 +29,11 @@ struct ToneDuration {
|
||||
#define NOTE_AS3 233
|
||||
#define NOTE_B3 247
|
||||
#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_4 = 250; // 1/4 note
|
||||
const int DURATION_1_2 = 500; // 1/2 note
|
||||
const int DURATION_3_4 = 750; // 3/4 note
|
||||
const int DURATION_3_4 = 750; // 1/4 note
|
||||
const int DURATION_1_1 = 1000; // 1/1 note
|
||||
|
||||
void playTones(const ToneDuration *tone_durations, int size)
|
||||
@@ -77,24 +71,13 @@ void playLongBeep()
|
||||
|
||||
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}};
|
||||
#endif
|
||||
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
||||
}
|
||||
|
||||
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}};
|
||||
#endif
|
||||
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
||||
}
|
||||
|
||||
|
||||
@@ -36,29 +36,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
/* Offer chance for variant-specific defines */
|
||||
#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
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -394,9 +371,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#ifndef HAS_BLUETOOTH
|
||||
#define HAS_BLUETOOTH 0
|
||||
#endif
|
||||
#ifndef USE_TFTDISPLAY
|
||||
#define USE_TFTDISPLAY 0
|
||||
#endif
|
||||
|
||||
#ifndef HW_VENDOR
|
||||
#error HW_VENDOR must be defined
|
||||
|
||||
@@ -69,11 +69,7 @@ using graphics::Emote;
|
||||
using graphics::emotes;
|
||||
using graphics::numEmotes;
|
||||
|
||||
#if USE_TFTDISPLAY
|
||||
extern uint16_t TFT_MESH;
|
||||
#else
|
||||
uint16_t TFT_MESH = COLOR565(0x67, 0xEA, 0x94);
|
||||
#endif
|
||||
|
||||
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
|
||||
#include "mesh/wifi/WiFiAPClient.h"
|
||||
@@ -230,9 +226,24 @@ void Screen::showTextInput(const char *header, const char *initialText, uint32_t
|
||||
{
|
||||
LOG_INFO("showTextInput called with header='%s', durationMs=%d", header ? header : "NULL", durationMs);
|
||||
|
||||
// Start OnScreenKeyboardModule session (non-touch variant)
|
||||
OnScreenKeyboardModule::instance().start(header, initialText, durationMs, textCallback);
|
||||
if (NotificationRenderer::virtualKeyboard) {
|
||||
delete NotificationRenderer::virtualKeyboard;
|
||||
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::virtualKeyboard->setCallback([textCallback](const std::string &text) { textCallback(text); });
|
||||
|
||||
// Store the message and set the expiration timestamp (use same pattern as other notifications)
|
||||
strncpy(NotificationRenderer::alertBannerMessage, header ? header : "Text Input", 255);
|
||||
@@ -1498,14 +1509,14 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
||||
// Incoming message
|
||||
devicestate.has_rx_text_message = true; // Needed to include the message frame
|
||||
hasUnreadMessage = true; // Enables mail icon in the header
|
||||
setFrames(FOCUS_PRESERVE); // Refresh frame list without switching view (no-op during text_input)
|
||||
setFrames(FOCUS_PRESERVE); // Refresh frame list without switching view
|
||||
|
||||
// Only wake/force display if the configuration allows it
|
||||
if (shouldWakeOnReceivedMessage()) {
|
||||
setOn(true); // Wake up the screen first
|
||||
forceDisplay(); // Forces screen redraw
|
||||
}
|
||||
// === Prepare banner/popup content ===
|
||||
// === Prepare banner content ===
|
||||
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
|
||||
const meshtastic_Channel channel =
|
||||
channels.getByIndex(packet->channel ? packet->channel : channels.getPrimaryIndex());
|
||||
@@ -1529,84 +1540,38 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
||||
|
||||
// Unlike generic messages, alerts (when enabled via the ext notif module) ignore any
|
||||
// 'mute' preferences set to any specific node or channel.
|
||||
// If on-screen keyboard is active, show a transient popup over keyboard instead of interrupting it
|
||||
if (NotificationRenderer::current_notification_type == notificationTypeEnum::text_input) {
|
||||
// Wake and force redraw so popup is visible immediately
|
||||
if (shouldWakeOnReceivedMessage()) {
|
||||
setOn(true);
|
||||
forceDisplay();
|
||||
}
|
||||
|
||||
// Build popup: title = message source name, content = message text (sanitized)
|
||||
// Title
|
||||
char titleBuf[64] = {0};
|
||||
if (isAlert) {
|
||||
if (longName && longName[0]) {
|
||||
// Sanitize sender name
|
||||
std::string t = sanitizeString(longName);
|
||||
strncpy(titleBuf, t.c_str(), sizeof(titleBuf) - 1);
|
||||
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
|
||||
} else {
|
||||
strncpy(titleBuf, "Message", sizeof(titleBuf) - 1);
|
||||
strcpy(banner, "Alert Received");
|
||||
}
|
||||
|
||||
// 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
|
||||
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();
|
||||
}
|
||||
#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);
|
||||
screen->showSimpleBanner(banner, 3000);
|
||||
#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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1689,12 +1654,6 @@ int Screen::handleInputEvent(const InputEvent *event)
|
||||
showPrevFrame();
|
||||
} else if (event->inputEvent == INPUT_BROKER_RIGHT || event->inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||
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) {
|
||||
if (this->ui->getUiState()->currentFrame == framesetInfo.positions.home) {
|
||||
menuHandler::homeBaseMenu();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#if USE_TFTDISPLAY
|
||||
|
||||
#if ARCH_PORTDUINO
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
@@ -1139,6 +1138,9 @@ static LGFX *tft = nullptr;
|
||||
|
||||
#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 "TFTDisplay.h"
|
||||
#include <SPI.h>
|
||||
@@ -1516,4 +1518,4 @@ bool TFTDisplay::connect()
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // USE_TFTDISPLAY
|
||||
#endif
|
||||
|
||||
@@ -506,9 +506,6 @@ void VirtualKeyboard::drawKey(OLEDDisplay *display, const VirtualKey &key, bool
|
||||
centeredTextY -= 1;
|
||||
}
|
||||
}
|
||||
#ifdef MUZI_BASE // Correct issue with character vertical position on MUZI_BASE
|
||||
centeredTextY -= 2;
|
||||
#endif
|
||||
display->drawString(textX, centeredTextY, keyText.c_str());
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "input/RotaryEncoderInterruptImpl1.h"
|
||||
#include "input/UpDownInterruptImpl1.h"
|
||||
#include "main.h"
|
||||
#include "mesh/Default.h"
|
||||
#include "mesh/MeshTypes.h"
|
||||
#include "modules/AdminModule.h"
|
||||
#include "modules/CannedMessageModule.h"
|
||||
@@ -120,28 +119,8 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
|
||||
auto changes = SEGMENT_CONFIG;
|
||||
|
||||
// This is needed as we wait til picking the LoRa region to generate keys for the first time.
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI)
|
||||
if (!owner.is_licensed) {
|
||||
bool keygenSuccess = false;
|
||||
if (config.security.private_key.size == 32) {
|
||||
// public key is derived from private, so this will always have the same result.
|
||||
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
|
||||
keygenSuccess = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
LOG_INFO("Generate new PKI keys");
|
||||
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
|
||||
keygenSuccess = true;
|
||||
}
|
||||
if (keygenSuccess) {
|
||||
config.security.public_key.size = 32;
|
||||
config.security.private_key.size = 32;
|
||||
owner.public_key.size = 32;
|
||||
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Use consolidated key generation function
|
||||
nodeDB->generateCryptoKeyPair();
|
||||
config.lora.tx_enabled = true;
|
||||
initRegion();
|
||||
if (myRegion->dutyCycle < 100) {
|
||||
@@ -1041,13 +1020,12 @@ void menuHandler::switchToMUIMenu()
|
||||
|
||||
void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
|
||||
{
|
||||
static const char *optionsArray[] = {
|
||||
"Back", "Default", "Meshtastic Green", "Yellow", "Red", "Orange", "Purple", "Blue", "Teal", "Cyan", "Ice", "Pink",
|
||||
"White", "Gray"};
|
||||
static const char *optionsArray[] = {"Back", "Default", "Meshtastic Green", "Yellow", "Red", "Orange", "Purple", "Teal",
|
||||
"Pink", "White"};
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = "Select Screen Color";
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsCount = 14;
|
||||
bannerOptions.optionsCount = 10;
|
||||
bannerOptions.bannerCallback = [display](int selected) -> void {
|
||||
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || \
|
||||
HAS_TFT || defined(HACKADAY_COMMUNICATOR)
|
||||
@@ -1083,40 +1061,20 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
|
||||
TFT_MESH_g = 153;
|
||||
TFT_MESH_b = 255;
|
||||
} else if (selected == 7) {
|
||||
LOG_INFO("Setting color to Blue");
|
||||
TFT_MESH_r = 0;
|
||||
TFT_MESH_g = 0;
|
||||
TFT_MESH_b = 255;
|
||||
} 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) {
|
||||
TFT_MESH_r = 64;
|
||||
TFT_MESH_g = 224;
|
||||
TFT_MESH_b = 208;
|
||||
} else if (selected == 8) {
|
||||
LOG_INFO("Setting color to Pink");
|
||||
TFT_MESH_r = 255;
|
||||
TFT_MESH_g = 105;
|
||||
TFT_MESH_b = 180;
|
||||
} else if (selected == 12) {
|
||||
} else if (selected == 9) {
|
||||
LOG_INFO("Setting color to White");
|
||||
TFT_MESH_r = 255;
|
||||
TFT_MESH_g = 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 {
|
||||
menuQueue = system_base_menu;
|
||||
screen->runNow();
|
||||
|
||||
@@ -85,13 +85,9 @@ void NotificationRenderer::drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiStat
|
||||
|
||||
void NotificationRenderer::resetBanner()
|
||||
{
|
||||
notificationTypeEnum previousType = current_notification_type;
|
||||
|
||||
alertBannerMessage[0] = '\0';
|
||||
current_notification_type = notificationTypeEnum::none;
|
||||
|
||||
OnScreenKeyboardModule::instance().clearPopup();
|
||||
|
||||
inEvent.inputEvent = INPUT_BROKER_NONE;
|
||||
inEvent.kbchar = 0;
|
||||
curSelected = 0;
|
||||
@@ -104,13 +100,6 @@ void NotificationRenderer::resetBanner()
|
||||
currentNumber = 0;
|
||||
|
||||
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)
|
||||
@@ -174,15 +163,13 @@ void NotificationRenderer::drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiS
|
||||
// modulo to extract
|
||||
uint8_t this_digit = (currentNumber % (pow_of_10(numDigits - curSelected))) / (pow_of_10(numDigits - curSelected - 1));
|
||||
// Handle input
|
||||
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS ||
|
||||
inEvent.inputEvent == INPUT_BROKER_UP_LONG) {
|
||||
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
||||
if (this_digit == 9) {
|
||||
currentNumber -= 9 * (pow_of_10(numDigits - curSelected - 1));
|
||||
} else {
|
||||
currentNumber += (pow_of_10(numDigits - curSelected - 1));
|
||||
}
|
||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS ||
|
||||
inEvent.inputEvent == INPUT_BROKER_DOWN_LONG) {
|
||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||
if (this_digit == 0) {
|
||||
currentNumber += 9 * (pow_of_10(numDigits - curSelected - 1));
|
||||
} else {
|
||||
@@ -264,10 +251,10 @@ void NotificationRenderer::drawNodePicker(OLEDDisplay *display, OLEDDisplayUiSta
|
||||
|
||||
// Handle input
|
||||
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_LEFT ||
|
||||
inEvent.inputEvent == INPUT_BROKER_ALT_PRESS || inEvent.inputEvent == INPUT_BROKER_UP_LONG) {
|
||||
inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
||||
curSelected--;
|
||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_RIGHT ||
|
||||
inEvent.inputEvent == INPUT_BROKER_USER_PRESS || inEvent.inputEvent == INPUT_BROKER_DOWN_LONG) {
|
||||
inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||
curSelected++;
|
||||
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
||||
alertBannerCallback(selectedNodenum);
|
||||
@@ -381,10 +368,10 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
|
||||
// Handle input
|
||||
if (alertBannerOptions > 0) {
|
||||
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_LEFT ||
|
||||
inEvent.inputEvent == INPUT_BROKER_ALT_PRESS || inEvent.inputEvent == INPUT_BROKER_UP_LONG) {
|
||||
inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
||||
curSelected--;
|
||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_RIGHT ||
|
||||
inEvent.inputEvent == INPUT_BROKER_USER_PRESS || inEvent.inputEvent == INPUT_BROKER_DOWN_LONG) {
|
||||
inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||
curSelected++;
|
||||
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
||||
if (optionsEnumPtr != nullptr) {
|
||||
@@ -782,8 +769,40 @@ void NotificationRenderer::drawTextInput(OLEDDisplay *display, OLEDDisplayUiStat
|
||||
}
|
||||
|
||||
if (inEvent.inputEvent != INPUT_BROKER_NONE) {
|
||||
bool handled = OnScreenKeyboardModule::processVirtualKeyboardInput(inEvent, virtualKeyboard);
|
||||
if (!handled && inEvent.inputEvent == INPUT_BROKER_CANCEL) {
|
||||
if (inEvent.inputEvent == INPUT_BROKER_UP) {
|
||||
// high frequency for move cursor left/right than up/down with encoders
|
||||
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;
|
||||
delete virtualKeyboard;
|
||||
virtualKeyboard = nullptr;
|
||||
@@ -802,28 +821,12 @@ void NotificationRenderer::drawTextInput(OLEDDisplay *display, OLEDDisplayUiStat
|
||||
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
|
||||
display->setColor(BLACK);
|
||||
display->fillRect(0, 0, display->getWidth(), display->getHeight());
|
||||
display->setColor(WHITE);
|
||||
// Draw the virtual keyboard
|
||||
virtualKeyboard->draw(display, 0, 0);
|
||||
|
||||
// Draw transient popup overlay (if any) managed by OnScreenKeyboardModule
|
||||
OnScreenKeyboardModule::instance().drawPopupOverlay(display);
|
||||
} else {
|
||||
// If virtualKeyboard is null, reset the banner to avoid getting stuck
|
||||
LOG_INFO("Virtual keyboard is null - resetting banner");
|
||||
@@ -836,12 +839,5 @@ bool NotificationRenderer::isOverlayBannerShowing()
|
||||
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
|
||||
#endif
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "OLEDDisplayUi.h"
|
||||
#include "graphics/Screen.h"
|
||||
#include "graphics/VirtualKeyboard.h"
|
||||
#include "modules/OnScreenKeyboardModule.h"
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#define MAX_LINES 5
|
||||
@@ -32,7 +31,6 @@ class NotificationRenderer
|
||||
static bool pauseBanner;
|
||||
|
||||
static void resetBanner();
|
||||
static void showKeyboardMessagePopupWithTitle(const char *title, const char *content, uint32_t durationMs);
|
||||
static void drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state);
|
||||
static void drawAlertBannerOverlay(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);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return utf32;
|
||||
|
||||
@@ -88,50 +88,6 @@ 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 (this->action == TB_ACTION_PRESSED && !pressDetected) {
|
||||
// Start long press detection
|
||||
@@ -157,22 +113,17 @@ int32_t TrackballInterruptBase::runOnce()
|
||||
pressDetected = true;
|
||||
pressStartTime = millis();
|
||||
// Don't send event yet, wait to see if it's a long press
|
||||
} else if (this->action == TB_ACTION_UP && !digitalRead(_pinUp) && !directionDetected) {
|
||||
directionDetected = true;
|
||||
directionStartTime = millis();
|
||||
} else if (this->action == TB_ACTION_UP && !digitalRead(_pinUp)) {
|
||||
// LOG_DEBUG("Trackball event UP");
|
||||
e.inputEvent = this->_eventUp;
|
||||
// send event first,will automatically trigger every 50ms * 3 after 500ms
|
||||
} else if (this->action == TB_ACTION_DOWN && !digitalRead(_pinDown) && !directionDetected) {
|
||||
directionDetected = true;
|
||||
directionStartTime = millis();
|
||||
} else if (this->action == TB_ACTION_DOWN && !digitalRead(_pinDown)) {
|
||||
// LOG_DEBUG("Trackball event DOWN");
|
||||
e.inputEvent = this->_eventDown;
|
||||
} else if (this->action == TB_ACTION_LEFT && !digitalRead(_pinLeft) && !directionDetected) {
|
||||
directionDetected = true;
|
||||
directionStartTime = millis();
|
||||
} else if (this->action == TB_ACTION_LEFT && !digitalRead(_pinLeft)) {
|
||||
// LOG_DEBUG("Trackball event LEFT");
|
||||
e.inputEvent = this->_eventLeft;
|
||||
} else if (this->action == TB_ACTION_RIGHT && !digitalRead(_pinRight) && !directionDetected) {
|
||||
directionDetected = true;
|
||||
directionStartTime = millis();
|
||||
} else if (this->action == TB_ACTION_RIGHT && !digitalRead(_pinRight)) {
|
||||
// LOG_DEBUG("Trackball event RIGHT");
|
||||
e.inputEvent = this->_eventRight;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -49,14 +49,10 @@ class TrackballInterruptBase : public Observable<const InputEvent *>, public con
|
||||
|
||||
// Long press detection for press button
|
||||
uint32_t pressStartTime = 0;
|
||||
uint32_t directionStartTime = 0;
|
||||
uint8_t directionInterval = 0;
|
||||
bool pressDetected = false;
|
||||
bool directionDetected = false;
|
||||
uint32_t lastLongPressEventTime = 0;
|
||||
uint32_t lastDirectionPressEventTime = 0;
|
||||
static const uint32_t LONG_PRESS_DURATION = 500; // ms
|
||||
static const uint32_t LONG_PRESS_REPEAT_INTERVAL = 300; // ms - interval between repeated long press events
|
||||
static const uint32_t LONG_PRESS_REPEAT_INTERVAL = 500; // ms - interval between repeated long press events
|
||||
|
||||
private:
|
||||
input_broker_event _eventDown = INPUT_BROKER_NONE;
|
||||
|
||||
@@ -3,14 +3,6 @@
|
||||
#include "InputBroker.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
|
||||
{
|
||||
public:
|
||||
@@ -48,8 +40,8 @@ class UpDownInterruptBase : public Observable<const InputEvent *>, public concur
|
||||
uint32_t lastPressLongEventTime = 0;
|
||||
uint32_t lastUpLongEventTime = 0;
|
||||
uint32_t lastDownLongEventTime = 0;
|
||||
static const uint32_t LONG_PRESS_DURATION = UPDOWN_LONG_PRESS_DURATION;
|
||||
static const uint32_t LONG_PRESS_REPEAT_INTERVAL = UPDOWN_LONG_PRESS_REPEAT_INTERVAL;
|
||||
static const uint32_t LONG_PRESS_DURATION = 300;
|
||||
static const uint32_t LONG_PRESS_REPEAT_INTERVAL = 300;
|
||||
|
||||
private:
|
||||
uint8_t _pinDown = 0;
|
||||
|
||||
@@ -439,11 +439,6 @@ void setup()
|
||||
|
||||
LOG_INFO("\n\n//\\ E S H T /\\ S T / C\n");
|
||||
|
||||
#if defined(ARCH_ESP32) && defined(BOARD_HAS_PSRAM)
|
||||
// use PSRAM for malloc calls > 256 bytes
|
||||
heap_caps_malloc_extmem_enable(256);
|
||||
#endif
|
||||
|
||||
#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("Version %s for %s from %s\r\n", optstr(APP_VERSION), optstr(APP_ENV), optstr(APP_REPO));
|
||||
|
||||
@@ -4,17 +4,22 @@
|
||||
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI)
|
||||
#include "NodeDB.h"
|
||||
#include "XEdDSA.h"
|
||||
#include "aes-ccm.h"
|
||||
#include "meshUtils.h"
|
||||
#include <Crypto.h>
|
||||
#include <Curve25519.h>
|
||||
#include <Ed25519.h>
|
||||
#include <RNG.h>
|
||||
#include <SHA256.h>
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN)
|
||||
#if !defined(ARCH_STM32WL)
|
||||
#define CryptRNG RNG
|
||||
|
||||
#ifndef NUM_LIMBS_256BIT
|
||||
#define NUM_LIMBS_BITS(n) (((n) + sizeof(limb_t) * 8 - 1) / (8 * sizeof(limb_t)))
|
||||
#define NUM_LIMBS_256BIT NUM_LIMBS_BITS(256)
|
||||
#endif
|
||||
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN)
|
||||
|
||||
/**
|
||||
* Create a public/private key pair with Curve25519.
|
||||
*
|
||||
@@ -35,6 +40,7 @@ void CryptoEngine::generateKeyPair(uint8_t *pubKey, uint8_t *privKey)
|
||||
Curve25519::dh1(public_key, private_key);
|
||||
memcpy(pubKey, public_key, sizeof(public_key));
|
||||
memcpy(privKey, private_key, sizeof(private_key));
|
||||
XEdDSA::priv_curve_to_ed_keys(private_key, xeddsa_private_key, xeddsa_public_key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,12 +60,66 @@ bool CryptoEngine::regeneratePublicKey(uint8_t *pubKey, uint8_t *privKey)
|
||||
}
|
||||
memcpy(private_key, privKey, sizeof(private_key));
|
||||
memcpy(public_key, pubKey, sizeof(public_key));
|
||||
XEdDSA::priv_curve_to_ed_keys(private_key, xeddsa_private_key, xeddsa_public_key);
|
||||
} else {
|
||||
LOG_WARN("X25519 key generation failed due to blank private key");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoEngine::xeddsa_sign(uint8_t *message, size_t len, uint8_t *signature)
|
||||
{
|
||||
XEdDSA::sign(signature, xeddsa_private_key, xeddsa_public_key, message,
|
||||
len); // sign will need modified to use the raw secret scalar, and not hash it first.
|
||||
return true;
|
||||
}
|
||||
bool CryptoEngine::xeddsa_verify(uint8_t *pubKey, uint8_t *message, size_t len, uint8_t *signature)
|
||||
{
|
||||
uint8_t publicKey[32] = {0};
|
||||
curve_to_ed_pub(pubKey, publicKey);
|
||||
|
||||
return XEdDSA::verify(signature, publicKey, message, len);
|
||||
}
|
||||
|
||||
void CryptoEngine::curve_to_ed_pub(uint8_t *curve_pubkey, uint8_t *ed_pubkey)
|
||||
{
|
||||
|
||||
// Apply the birational map defined in RFC 7748, section 4.1 "Curve25519" to calculate an Ed25519 public
|
||||
// key from a Curve25519 public key. Because the serialization format of Curve25519 public keys only
|
||||
// contains the u coordinate, the x coordinate of the corresponding Ed25519 public key can't be uniquely
|
||||
// calculated as defined by the birational map. The x coordinate is represented in the serialization
|
||||
// format of Ed25519 public keys only in a single sign bit. This function assumes that the sign bit is
|
||||
// known to the user and is passed accordingly.
|
||||
fe u, y;
|
||||
fe one;
|
||||
fe u_minus_one, u_plus_one, u_plus_one_inv;
|
||||
|
||||
// Parse the Curve25519 public key input as a field element containing the u coordinate. RFC 7748,
|
||||
// section 5 "The X25519 and X448 Functions", mandates that the most significant bit of the Curve25519
|
||||
// public key has to be zeroized. This is handled by fe_frombytes internally.
|
||||
fe_frombytes(u, curve_pubkey);
|
||||
|
||||
// Calculate the parameters (u - 1) and (u + 1)
|
||||
fe_1(one);
|
||||
fe_sub(u_minus_one, u, one);
|
||||
fe_add(u_plus_one, u, one);
|
||||
|
||||
// Invert u + 1
|
||||
fe_invert(u_plus_one_inv, u_plus_one);
|
||||
|
||||
// Calculate y = (u - 1) * inv(u + 1) (mod p)
|
||||
fe_mul(y, u_minus_one, u_plus_one_inv);
|
||||
|
||||
// Serialize the field element containing the y coordinate to the Ed25519 public key output
|
||||
fe_tobytes(ed_pubkey, y);
|
||||
|
||||
// Set the sign bit to zero
|
||||
ed_pubkey[31] &= 0x7f;
|
||||
|
||||
// need to convert the pubkey y = ( u - 1) * inv( u + 1) (mod p).
|
||||
}
|
||||
|
||||
#endif
|
||||
void CryptoEngine::clearKeys()
|
||||
{
|
||||
|
||||
@@ -35,6 +35,8 @@ class CryptoEngine
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN)
|
||||
virtual void generateKeyPair(uint8_t *pubKey, uint8_t *privKey);
|
||||
virtual bool regeneratePublicKey(uint8_t *pubKey, uint8_t *privKey);
|
||||
bool xeddsa_sign(uint8_t *message, size_t len, uint8_t *signature);
|
||||
bool xeddsa_verify(uint8_t *pubKey, uint8_t *message, size_t len, uint8_t *signature);
|
||||
|
||||
#endif
|
||||
void clearKeys();
|
||||
@@ -82,6 +84,9 @@ class CryptoEngine
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI)
|
||||
uint8_t shared_key[32] = {0};
|
||||
uint8_t private_key[32] = {0};
|
||||
uint8_t xeddsa_public_key[32] = {0};
|
||||
uint8_t xeddsa_private_key[32] = {0};
|
||||
void curve_to_ed_pub(uint8_t *curve_pubkey, uint8_t *ed_pubkey);
|
||||
#endif
|
||||
/**
|
||||
* Init our 128 bit nonce for a new packet
|
||||
|
||||
@@ -57,7 +57,14 @@ class Default
|
||||
// Note: Kept as uint32_t to match the public API parameter type
|
||||
static float congestionScalingCoefficient(uint32_t numOnlineNodes)
|
||||
{
|
||||
if (numOnlineNodes <= 40) {
|
||||
// Increase frequency of broadcasts for small networks regardless of preset
|
||||
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;
|
||||
} else {
|
||||
float throttlingFactor = 0.075;
|
||||
|
||||
@@ -246,8 +246,6 @@ NodeDB::NodeDB()
|
||||
|
||||
// likewise - we always want the app requirements to come from the running appload
|
||||
myNodeInfo.min_app_version = 30200; // format is Mmmss (where M is 1+the numeric major number. i.e. 30200 means 2.2.00
|
||||
// Note! We do this after loading saved settings, so that if somehow an invalid nodenum was stored in preferences we won't
|
||||
// keep using that nodenum forever. Crummy guess at our nodenum (but we will check against the nodedb to avoid conflicts)
|
||||
pickNewNodeNum();
|
||||
|
||||
// Set our board type so we can share it with others
|
||||
@@ -267,31 +265,18 @@ NodeDB::NodeDB()
|
||||
}
|
||||
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI)
|
||||
|
||||
if (!owner.is_licensed && config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||
bool keygenSuccess = false;
|
||||
keyIsLowEntropy = checkLowEntropyPublicKey(config.security.public_key);
|
||||
if (config.security.private_key.size == 32 && !keyIsLowEntropy) {
|
||||
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
|
||||
keygenSuccess = true;
|
||||
}
|
||||
} else {
|
||||
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
|
||||
keygenSuccess = true;
|
||||
}
|
||||
if (keygenSuccess) {
|
||||
config.security.public_key.size = 32;
|
||||
config.security.private_key.size = 32;
|
||||
owner.public_key.size = 32;
|
||||
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
|
||||
}
|
||||
}
|
||||
// Generate crypto keys if needed using consolidated function
|
||||
// Set my node num uint32 value to bytes from the public key (if we have one)
|
||||
// Generate identity and crypto keys if needed; this will create a new identity if one does not exist
|
||||
generateCryptoKeyPair(nullptr);
|
||||
#elif !(MESHTASTIC_EXCLUDE_PKI)
|
||||
// Calculate Curve25519 public and private keys
|
||||
if (config.security.private_key.size == 32 && config.security.public_key.size == 32) {
|
||||
owner.public_key.size = config.security.public_key.size;
|
||||
memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size);
|
||||
crypto->setDHPrivateKey(config.security.private_key.bytes);
|
||||
// Set my node num uint32 value to bytes from the new public key
|
||||
myNodeInfo.my_node_num = crc32Buffer(config.security.public_key.bytes, config.security.public_key.size);
|
||||
}
|
||||
#endif
|
||||
// Include our owner in the node db under our nodenum
|
||||
@@ -2029,6 +2014,94 @@ bool NodeDB::checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_pub
|
||||
}
|
||||
#endif
|
||||
|
||||
bool NodeDB::generateCryptoKeyPair(const uint8_t *privateKey)
|
||||
{
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI)
|
||||
// Only generate keys for non-licensed users and if LoRa region is set
|
||||
if (owner.is_licensed || config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool keygenSuccess = false;
|
||||
bool lowEntropy = checkLowEntropyPublicKey(config.security.public_key);
|
||||
|
||||
// If a specific private key was provided, use it
|
||||
if (privateKey != nullptr) {
|
||||
LOG_INFO("Using provided private key for PKI");
|
||||
memcpy(config.security.private_key.bytes, privateKey, 32);
|
||||
config.security.private_key.size = 32;
|
||||
config.security.public_key.size = 32;
|
||||
|
||||
// Generate public key from the provided private key
|
||||
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
|
||||
keygenSuccess = true;
|
||||
} else {
|
||||
LOG_ERROR("Failed to generate public key from provided private key");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Try to regenerate public key from existing private key if it's valid and not low entropy
|
||||
else if (config.security.private_key.size == 32 && !lowEntropy) {
|
||||
config.security.public_key.size = 32;
|
||||
LOG_DEBUG("Regenerate PKI public key from existing private key");
|
||||
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
|
||||
keygenSuccess = true;
|
||||
}
|
||||
} else {
|
||||
// Generate a new key pair
|
||||
LOG_INFO("Generate new PKI keys");
|
||||
config.security.public_key.size = 32;
|
||||
config.security.private_key.size = 32;
|
||||
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
|
||||
keygenSuccess = true;
|
||||
}
|
||||
|
||||
// Update sizes and copy to owner if successful
|
||||
if (keygenSuccess) {
|
||||
owner.public_key.size = 32;
|
||||
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
|
||||
|
||||
// Update global entropy flag for UI display
|
||||
keyIsLowEntropy = false;
|
||||
|
||||
// Set the DH private key for crypto operations
|
||||
LOG_DEBUG("Set DH private key for crypto operations");
|
||||
crypto->setDHPrivateKey(config.security.private_key.bytes);
|
||||
|
||||
// Conditionally create new identity based on parameter
|
||||
createNewIdentity();
|
||||
}
|
||||
return keygenSuccess;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool NodeDB::createNewIdentity()
|
||||
{
|
||||
// Remove the old node from the NodeDB
|
||||
uint32_t oldNodeNum = getNodeNum();
|
||||
meshtastic_NodeInfoLite *node = getMeshNode(oldNodeNum);
|
||||
|
||||
// Set my node num uint32 value to bytes from the new public key
|
||||
myNodeInfo.my_node_num = crc32Buffer(config.security.public_key.bytes, config.security.public_key.size);
|
||||
|
||||
if (node != NULL && myNodeInfo.my_node_num != oldNodeNum) {
|
||||
LOG_DEBUG("Old node num %u is now %u", oldNodeNum, myNodeInfo.my_node_num);
|
||||
node->is_ignored = true;
|
||||
node->has_device_metrics = false;
|
||||
node->has_position = false;
|
||||
node->user.public_key.size = 0;
|
||||
node->user.public_key.bytes[0] = 0;
|
||||
}
|
||||
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum());
|
||||
info->user = TypeConversions::ConvertToUserLite(owner);
|
||||
info->has_user = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NodeDB::backupPreferences(meshtastic_AdminMessage_BackupLocation location)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
@@ -287,6 +287,12 @@ class NodeDB
|
||||
bool checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest);
|
||||
#endif
|
||||
|
||||
/// Consolidate crypto key generation logic used across multiple modules
|
||||
/// @param privateKey Optional 32-byte private key to use. If nullptr, generates new random keys.
|
||||
bool generateCryptoKeyPair(const uint8_t *privateKey = nullptr);
|
||||
|
||||
bool createNewIdentity();
|
||||
|
||||
bool backupPreferences(meshtastic_AdminMessage_BackupLocation location);
|
||||
bool restorePreferences(meshtastic_AdminMessage_BackupLocation location,
|
||||
int restoreWhat = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS);
|
||||
@@ -370,6 +376,9 @@ extern uint32_t error_address;
|
||||
#define NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_SHIFT 0
|
||||
#define NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK (1 << NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_SHIFT)
|
||||
|
||||
#define NODEINFO_BITFIELD_HAS_XEDDSA_SIGNED_SHIFT 1
|
||||
#define NODEINFO_BITFIELD_HAS_XEDDSA_SIGNED_MASK (1 << NODEINFO_BITFIELD_HAS_XEDDSA_SIGNED_SHIFT)
|
||||
|
||||
#define Module_Config_size \
|
||||
(ModuleConfig_CannedMessageConfig_size + ModuleConfig_ExternalNotificationConfig_size + ModuleConfig_MQTTConfig_size + \
|
||||
ModuleConfig_RangeTestConfig_size + ModuleConfig_SerialConfig_size + ModuleConfig_StoreForwardConfig_size + \
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
|
||||
static MemoryDynamic<meshtastic_MeshPacket> dynamicPool;
|
||||
Allocator<meshtastic_MeshPacket> &packetPool = dynamicPool;
|
||||
#elif defined(ARCH_STM32WL) || defined(BOARD_HAS_PSRAM)
|
||||
// On STM32 and boards with PSRAM, there isn't enough heap left over for the rest of the firmware if we allocate this statically.
|
||||
#elif defined(ARCH_STM32WL)
|
||||
// On STM32 there isn't enough heap left over for the rest of the firmware if we allocate this statically.
|
||||
// For now, make it dynamic again.
|
||||
#define MAX_PACKETS \
|
||||
(MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + \
|
||||
@@ -500,6 +500,25 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p)
|
||||
if (p->decoded.has_bitfield)
|
||||
p->decoded.want_response |= p->decoded.bitfield & BITFIELD_WANT_RESPONSE_MASK;
|
||||
|
||||
if (p->decoded.has_xeddsa_signature) {
|
||||
LOG_WARN("packet shows XEdDSA");
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->from);
|
||||
if (node && node->user.public_key.size == 32) {
|
||||
LOG_WARN("attempting to verify");
|
||||
p->xeddsa_signed = crypto->xeddsa_verify(node->user.public_key.bytes, p->decoded.payload.bytes,
|
||||
p->decoded.payload.size, p->decoded.xeddsa_signature.bytes);
|
||||
} else {
|
||||
LOG_WARN("Don't have key to verify");
|
||||
}
|
||||
}
|
||||
if (p->xeddsa_signed) {
|
||||
LOG_WARN("Received XEdDSA Signed Packet!");
|
||||
} else if (p->decoded.has_xeddsa_signature) {
|
||||
LOG_ERROR("Node sent signed packet, but cannot verify!");
|
||||
} else {
|
||||
LOG_WARN("Received Unsigned Packet!");
|
||||
}
|
||||
|
||||
/* Not actually ever used.
|
||||
// Decompress if needed. jm
|
||||
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP) {
|
||||
@@ -549,6 +568,12 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
|
||||
p->decoded.has_bitfield = true;
|
||||
p->decoded.bitfield |= (config.lora.config_ok_to_mqtt << BITFIELD_OK_TO_MQTT_SHIFT);
|
||||
p->decoded.bitfield |= (p->decoded.want_response << BITFIELD_WANT_RESPONSE_SHIFT);
|
||||
if (p->pki_encrypted == false && isBroadcast(p->to) && p->decoded.payload.size < 120) {
|
||||
crypto->xeddsa_sign(p->decoded.payload.bytes, p->decoded.payload.size, p->decoded.xeddsa_signature.bytes);
|
||||
p->decoded.xeddsa_signature.size = 64;
|
||||
p->decoded.has_xeddsa_signature = true;
|
||||
LOG_WARN("XEDDSA Signed!");
|
||||
}
|
||||
}
|
||||
|
||||
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_Data_msg, &p->decoded);
|
||||
|
||||
@@ -14,6 +14,7 @@ meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfo
|
||||
info.is_favorite = lite->is_favorite;
|
||||
info.is_ignored = lite->is_ignored;
|
||||
info.is_key_manually_verified = lite->bitfield & NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
|
||||
info.has_xeddsa_signed = lite->bitfield & NODEINFO_BITFIELD_HAS_XEDDSA_SIGNED_MASK;
|
||||
|
||||
if (lite->has_hops_away) {
|
||||
info.has_hops_away = true;
|
||||
|
||||
@@ -362,7 +362,7 @@ extern const pb_msgdesc_t meshtastic_BackupPreferences_msg;
|
||||
#define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_BackupPreferences_size
|
||||
#define meshtastic_BackupPreferences_size 2277
|
||||
#define meshtastic_ChannelFile_size 718
|
||||
#define meshtastic_DeviceState_size 1737
|
||||
#define meshtastic_DeviceState_size 1944
|
||||
#define meshtastic_NodeInfoLite_size 196
|
||||
#define meshtastic_PositionLite_size 28
|
||||
#define meshtastic_UserLite_size 98
|
||||
|
||||
@@ -237,8 +237,8 @@ typedef enum _meshtastic_HardwareModel {
|
||||
meshtastic_HardwareModel_T_ETH_ELITE = 91,
|
||||
/* Heltec HRI-3621 industrial probe */
|
||||
meshtastic_HardwareModel_HELTEC_SENSOR_HUB = 92,
|
||||
/* Muzi Works Muzi-Base device */
|
||||
meshtastic_HardwareModel_MUZI_BASE = 93,
|
||||
/* Reserved Fried Chicken ID for future use */
|
||||
meshtastic_HardwareModel_RESERVED_FRIED_CHICKEN = 93,
|
||||
/* Heltec Magnetic Power Bank with Meshtastic compatible */
|
||||
meshtastic_HardwareModel_HELTEC_MESH_POCKET = 94,
|
||||
/* Seeed Solar Node */
|
||||
@@ -734,6 +734,7 @@ typedef struct _meshtastic_Routing {
|
||||
} meshtastic_Routing;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(233) meshtastic_Data_payload_t;
|
||||
typedef PB_BYTES_ARRAY_T(64) meshtastic_Data_xeddsa_signature_t;
|
||||
/* (Formerly called SubPacket)
|
||||
The payload portion fo a packet, this is the actual bytes that are sent
|
||||
inside a radio packet (because from/to are broken out by the comms library) */
|
||||
@@ -767,6 +768,9 @@ typedef struct _meshtastic_Data {
|
||||
/* Bitfield for extra flags. First use is to indicate that user approves the packet being uploaded to MQTT. */
|
||||
bool has_bitfield;
|
||||
uint8_t bitfield;
|
||||
/* XEdDSA signature for the payload */
|
||||
bool has_xeddsa_signature;
|
||||
meshtastic_Data_xeddsa_signature_t xeddsa_signature;
|
||||
} meshtastic_Data;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(32) meshtastic_KeyVerification_hash1_t;
|
||||
@@ -913,6 +917,8 @@ typedef struct _meshtastic_MeshPacket {
|
||||
uint32_t tx_after;
|
||||
/* Indicates which transport mechanism this packet arrived over */
|
||||
meshtastic_MeshPacket_TransportMechanism transport_mechanism;
|
||||
/* Indicates whether the packet has a valid signature */
|
||||
bool xeddsa_signed;
|
||||
} meshtastic_MeshPacket;
|
||||
|
||||
/* The bluetooth to device link:
|
||||
@@ -966,6 +972,10 @@ typedef struct _meshtastic_NodeInfo {
|
||||
Persists between NodeDB internal clean ups
|
||||
LSB 0 of the bitfield */
|
||||
bool is_key_manually_verified;
|
||||
/* True if node is signing its packets via XEdDSA
|
||||
Persists between NodeDB internal clean ups
|
||||
LSB 1 of the bitfield */
|
||||
bool has_xeddsa_signed;
|
||||
} meshtastic_NodeInfo;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(16) meshtastic_MyNodeInfo_device_id_t;
|
||||
@@ -1378,12 +1388,12 @@ extern "C" {
|
||||
#define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}, false, 0}
|
||||
#define meshtastic_RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}}
|
||||
#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, false, {0, {0}}}
|
||||
#define meshtastic_KeyVerification_init_default {0, {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_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_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, false, 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, 0}
|
||||
#define meshtastic_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, false, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_MyNodeInfo_init_default {0, 0, 0, {0, {0}}, "", _meshtastic_FirmwareEdition_MIN, 0}
|
||||
#define meshtastic_LogRecord_init_default {"", 0, "", _meshtastic_LogRecord_Level_MIN}
|
||||
#define meshtastic_QueueStatus_init_default {0, 0, 0, 0}
|
||||
@@ -1409,12 +1419,12 @@ extern "C" {
|
||||
#define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}, false, 0}
|
||||
#define meshtastic_RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}}
|
||||
#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, false, {0, {0}}}
|
||||
#define meshtastic_KeyVerification_init_zero {0, {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_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_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, false, 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, 0}
|
||||
#define meshtastic_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, false, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_MyNodeInfo_init_zero {0, 0, 0, {0, {0}}, "", _meshtastic_FirmwareEdition_MIN, 0}
|
||||
#define meshtastic_LogRecord_init_zero {"", 0, "", _meshtastic_LogRecord_Level_MIN}
|
||||
#define meshtastic_QueueStatus_init_zero {0, 0, 0, 0}
|
||||
@@ -1486,6 +1496,7 @@ extern "C" {
|
||||
#define meshtastic_Data_reply_id_tag 7
|
||||
#define meshtastic_Data_emoji_tag 8
|
||||
#define meshtastic_Data_bitfield_tag 9
|
||||
#define meshtastic_Data_xeddsa_signature_tag 10
|
||||
#define meshtastic_KeyVerification_nonce_tag 1
|
||||
#define meshtastic_KeyVerification_hash1_tag 2
|
||||
#define meshtastic_KeyVerification_hash2_tag 3
|
||||
@@ -1522,6 +1533,7 @@ extern "C" {
|
||||
#define meshtastic_MeshPacket_relay_node_tag 19
|
||||
#define meshtastic_MeshPacket_tx_after_tag 20
|
||||
#define meshtastic_MeshPacket_transport_mechanism_tag 21
|
||||
#define meshtastic_MeshPacket_xeddsa_signed_tag 22
|
||||
#define meshtastic_NodeInfo_num_tag 1
|
||||
#define meshtastic_NodeInfo_user_tag 2
|
||||
#define meshtastic_NodeInfo_position_tag 3
|
||||
@@ -1534,6 +1546,7 @@ extern "C" {
|
||||
#define meshtastic_NodeInfo_is_favorite_tag 10
|
||||
#define meshtastic_NodeInfo_is_ignored_tag 11
|
||||
#define meshtastic_NodeInfo_is_key_manually_verified_tag 12
|
||||
#define meshtastic_NodeInfo_has_xeddsa_signed_tag 13
|
||||
#define meshtastic_MyNodeInfo_my_node_num_tag 1
|
||||
#define meshtastic_MyNodeInfo_reboot_count_tag 8
|
||||
#define meshtastic_MyNodeInfo_min_app_version_tag 11
|
||||
@@ -1694,7 +1707,8 @@ X(a, STATIC, SINGULAR, FIXED32, source, 5) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, request_id, 6) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, reply_id, 7) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, emoji, 8) \
|
||||
X(a, STATIC, OPTIONAL, UINT32, bitfield, 9)
|
||||
X(a, STATIC, OPTIONAL, UINT32, bitfield, 9) \
|
||||
X(a, STATIC, OPTIONAL, BYTES, xeddsa_signature, 10)
|
||||
#define meshtastic_Data_CALLBACK NULL
|
||||
#define meshtastic_Data_DEFAULT NULL
|
||||
|
||||
@@ -1746,7 +1760,8 @@ X(a, STATIC, SINGULAR, BOOL, pki_encrypted, 17) \
|
||||
X(a, STATIC, SINGULAR, UINT32, next_hop, 18) \
|
||||
X(a, STATIC, SINGULAR, UINT32, relay_node, 19) \
|
||||
X(a, STATIC, SINGULAR, UINT32, tx_after, 20) \
|
||||
X(a, STATIC, SINGULAR, UENUM, transport_mechanism, 21)
|
||||
X(a, STATIC, SINGULAR, UENUM, transport_mechanism, 21) \
|
||||
X(a, STATIC, SINGULAR, BOOL, xeddsa_signed, 22)
|
||||
#define meshtastic_MeshPacket_CALLBACK NULL
|
||||
#define meshtastic_MeshPacket_DEFAULT NULL
|
||||
#define meshtastic_MeshPacket_payload_variant_decoded_MSGTYPE meshtastic_Data
|
||||
@@ -1763,7 +1778,8 @@ X(a, STATIC, SINGULAR, BOOL, via_mqtt, 8) \
|
||||
X(a, STATIC, OPTIONAL, UINT32, hops_away, 9) \
|
||||
X(a, STATIC, SINGULAR, BOOL, is_favorite, 10) \
|
||||
X(a, STATIC, SINGULAR, BOOL, is_ignored, 11) \
|
||||
X(a, STATIC, SINGULAR, BOOL, is_key_manually_verified, 12)
|
||||
X(a, STATIC, SINGULAR, BOOL, is_key_manually_verified, 12) \
|
||||
X(a, STATIC, SINGULAR, BOOL, has_xeddsa_signed, 13)
|
||||
#define meshtastic_NodeInfo_CALLBACK NULL
|
||||
#define meshtastic_NodeInfo_DEFAULT NULL
|
||||
#define meshtastic_NodeInfo_user_MSGTYPE meshtastic_User
|
||||
@@ -2046,7 +2062,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
|
||||
#define meshtastic_ChunkedPayload_size 245
|
||||
#define meshtastic_ClientNotification_size 482
|
||||
#define meshtastic_Compressed_size 239
|
||||
#define meshtastic_Data_size 269
|
||||
#define meshtastic_Data_size 335
|
||||
#define meshtastic_DeviceMetadata_size 54
|
||||
#define meshtastic_DuplicatedPublicKey_size 0
|
||||
#define meshtastic_FileInfo_size 236
|
||||
@@ -2058,12 +2074,12 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
|
||||
#define meshtastic_KeyVerification_size 79
|
||||
#define meshtastic_LogRecord_size 426
|
||||
#define meshtastic_LowEntropyKey_size 0
|
||||
#define meshtastic_MeshPacket_size 381
|
||||
#define meshtastic_MeshPacket_size 450
|
||||
#define meshtastic_MqttClientProxyMessage_size 501
|
||||
#define meshtastic_MyNodeInfo_size 83
|
||||
#define meshtastic_NeighborInfo_size 258
|
||||
#define meshtastic_Neighbor_size 22
|
||||
#define meshtastic_NodeInfo_size 323
|
||||
#define meshtastic_NodeInfo_size 325
|
||||
#define meshtastic_NodeRemoteHardwarePin_size 29
|
||||
#define meshtastic_Position_size 144
|
||||
#define meshtastic_QueueStatus_size 23
|
||||
|
||||
@@ -773,26 +773,8 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
|
||||
config.lora = validatedLora;
|
||||
// If we're setting region for the first time, init the region and regenerate the keys
|
||||
if (isRegionUnset && config.lora.region > meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI)
|
||||
if (!owner.is_licensed) {
|
||||
bool keygenSuccess = false;
|
||||
if (config.security.private_key.size == 32) {
|
||||
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
|
||||
keygenSuccess = true;
|
||||
}
|
||||
} else {
|
||||
LOG_INFO("Generate new PKI keys");
|
||||
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
|
||||
keygenSuccess = true;
|
||||
}
|
||||
if (keygenSuccess) {
|
||||
config.security.public_key.size = 32;
|
||||
config.security.private_key.size = 32;
|
||||
owner.public_key.size = 32;
|
||||
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Use consolidated key generation function
|
||||
nodeDB->generateCryptoKeyPair();
|
||||
config.lora.tx_enabled = true;
|
||||
initRegion();
|
||||
if (myRegion->dutyCycle < 100) {
|
||||
@@ -801,7 +783,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
|
||||
// Compare the entire string, we are sure of the length as a topic has never been set
|
||||
if (strcmp(moduleConfig.mqtt.root, default_mqtt_root) == 0) {
|
||||
sprintf(moduleConfig.mqtt.root, "%s/%s", default_mqtt_root, myRegion->name);
|
||||
changes = SEGMENT_CONFIG | SEGMENT_MODULECONFIG;
|
||||
changes = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_NODEDATABASE | SEGMENT_DEVICESTATE;
|
||||
}
|
||||
}
|
||||
if (config.lora.region != myRegion->code) {
|
||||
@@ -827,22 +809,14 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
|
||||
LOG_INFO("Set config: Security");
|
||||
config.security = c.payload_variant.security;
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) && !(MESHTASTIC_EXCLUDE_PKI)
|
||||
// If the client set the key to blank, go ahead and regenerate so long as we're not in ham mode
|
||||
if (!owner.is_licensed && config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||
if (config.security.private_key.size != 32) {
|
||||
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
|
||||
|
||||
} else {
|
||||
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
|
||||
config.security.public_key.size = 32;
|
||||
}
|
||||
}
|
||||
// Only regenerate keys if the private key is not 32 bytes
|
||||
if (config.security.private_key.size != 32) {
|
||||
nodeDB->generateCryptoKeyPair();
|
||||
}
|
||||
// If user provided a private key of correct size but no public key, generate the public key from private key
|
||||
else if (config.security.private_key.size == 32 && config.security.public_key.size == 0) {
|
||||
nodeDB->generateCryptoKeyPair(config.security.private_key.bytes);
|
||||
}
|
||||
#endif
|
||||
owner.public_key.size = config.security.public_key.size;
|
||||
memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size);
|
||||
#if !MESHTASTIC_EXCLUDE_PKI
|
||||
crypto->setDHPrivateKey(config.security.private_key.bytes);
|
||||
#endif
|
||||
if (config.security.is_managed && !(config.security.admin_key[0].size == 32 || config.security.admin_key[1].size == 32 ||
|
||||
config.security.admin_key[2].size == 32)) {
|
||||
@@ -852,9 +826,9 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
|
||||
sendWarning(warning);
|
||||
}
|
||||
|
||||
if (config.security.debug_log_api_enabled == c.payload_variant.security.debug_log_api_enabled &&
|
||||
config.security.serial_enabled == c.payload_variant.security.serial_enabled)
|
||||
requiresReboot = false;
|
||||
changes = SEGMENT_CONFIG | SEGMENT_DEVICESTATE | SEGMENT_NODEDATABASE;
|
||||
|
||||
requiresReboot = true;
|
||||
|
||||
break;
|
||||
case meshtastic_Config_device_ui_tag:
|
||||
|
||||
@@ -1018,7 +1018,8 @@ int32_t CannedMessageModule::runOnce()
|
||||
// Clean up virtual keyboard if needed when going inactive
|
||||
if (graphics::NotificationRenderer::virtualKeyboard && graphics::NotificationRenderer::textInputCallback == nullptr) {
|
||||
LOG_INFO("Performing delayed virtual keyboard cleanup");
|
||||
graphics::OnScreenKeyboardModule::instance().stop(false);
|
||||
delete graphics::NotificationRenderer::virtualKeyboard;
|
||||
graphics::NotificationRenderer::virtualKeyboard = nullptr;
|
||||
}
|
||||
|
||||
temporaryMessage = "";
|
||||
@@ -1035,7 +1036,9 @@ int32_t CannedMessageModule::runOnce()
|
||||
// Clean up virtual keyboard after sending
|
||||
if (graphics::NotificationRenderer::virtualKeyboard) {
|
||||
LOG_INFO("Cleaning up virtual keyboard after message send");
|
||||
graphics::OnScreenKeyboardModule::instance().stop(false);
|
||||
delete graphics::NotificationRenderer::virtualKeyboard;
|
||||
graphics::NotificationRenderer::virtualKeyboard = nullptr;
|
||||
graphics::NotificationRenderer::textInputCallback = nullptr;
|
||||
graphics::NotificationRenderer::resetBanner();
|
||||
}
|
||||
|
||||
@@ -1093,7 +1096,9 @@ int32_t CannedMessageModule::runOnce()
|
||||
// Clean up virtual keyboard if it exists during timeout
|
||||
if (graphics::NotificationRenderer::virtualKeyboard) {
|
||||
LOG_INFO("Cleaning up virtual keyboard due to module timeout");
|
||||
graphics::OnScreenKeyboardModule::instance().stop(false);
|
||||
delete graphics::NotificationRenderer::virtualKeyboard;
|
||||
graphics::NotificationRenderer::virtualKeyboard = nullptr;
|
||||
graphics::NotificationRenderer::textInputCallback = nullptr;
|
||||
graphics::NotificationRenderer::resetBanner();
|
||||
}
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ int32_t ExternalNotificationModule::runOnce()
|
||||
delay = EXT_NOTIFICATION_FAST_THREAD_MS;
|
||||
#endif
|
||||
|
||||
#if defined(T_WATCH_S3) || defined(T_LORA_PAGER)
|
||||
#ifdef T_WATCH_S3
|
||||
drv.go();
|
||||
#endif
|
||||
}
|
||||
@@ -283,7 +283,7 @@ void ExternalNotificationModule::setExternalState(uint8_t index, bool on)
|
||||
#ifdef UNPHONE
|
||||
unphone.rgb(red, green, blue);
|
||||
#endif
|
||||
#if defined(T_WATCH_S3) || defined(T_LORA_PAGER)
|
||||
#ifdef T_WATCH_S3
|
||||
if (on) {
|
||||
drv.go();
|
||||
} else {
|
||||
@@ -310,7 +310,8 @@ void ExternalNotificationModule::stopNow()
|
||||
rtttl::stop();
|
||||
#ifdef HAS_I2S
|
||||
LOG_INFO("Stop audioThread playback");
|
||||
audioThread->stop();
|
||||
if (audioThread->isPlaying())
|
||||
audioThread->stop();
|
||||
#endif
|
||||
// Turn off all outputs
|
||||
LOG_INFO("Turning off setExternalStates");
|
||||
@@ -319,7 +320,7 @@ void ExternalNotificationModule::stopNow()
|
||||
externalTurnedOn[i] = 0;
|
||||
}
|
||||
setIntervalFromNow(0);
|
||||
#if defined(T_WATCH_S3) || defined(T_LORA_PAGER)
|
||||
#ifdef T_WATCH_S3
|
||||
drv.stop();
|
||||
#endif
|
||||
|
||||
@@ -541,19 +542,6 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
|
||||
(!isBroadcast(mp.to) && isToUs(&mp))) {
|
||||
// Buzz if buzzer mode is not in DIRECT_MSG_ONLY or is DM to us
|
||||
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) {
|
||||
setExternalState(2, true);
|
||||
} else {
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
#include "configuration.h"
|
||||
#include "input/InputBroker.h"
|
||||
|
||||
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(CONFIG_IDF_TARGET_ESP32C6) && \
|
||||
!defined(CONFIG_IDF_TARGET_ESP32H2)
|
||||
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||
#include <NonBlockingRtttl.h>
|
||||
#else
|
||||
// Noop class for portduino.
|
||||
|
||||
@@ -181,25 +181,25 @@ void setupModules()
|
||||
// new ReplyModule();
|
||||
#if (HAS_BUTTON || ARCH_PORTDUINO) && !MESHTASTIC_EXCLUDE_INPUTBROKER
|
||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||
#if defined(T_LORA_PAGER)
|
||||
#ifndef 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
|
||||
rotaryEncoderImpl = new RotaryEncoderImpl();
|
||||
if (!rotaryEncoderImpl->init()) {
|
||||
delete rotaryEncoderImpl;
|
||||
rotaryEncoderImpl = nullptr;
|
||||
}
|
||||
#elif defined(INPUTDRIVER_ENCODER_TYPE) && (INPUTDRIVER_ENCODER_TYPE == 2)
|
||||
#else
|
||||
upDownInterruptImpl1 = new UpDownInterruptImpl1();
|
||||
if (!upDownInterruptImpl1->init()) {
|
||||
delete upDownInterruptImpl1;
|
||||
upDownInterruptImpl1 = nullptr;
|
||||
}
|
||||
#else
|
||||
rotaryEncoderInterruptImpl1 = new RotaryEncoderInterruptImpl1();
|
||||
if (!rotaryEncoderInterruptImpl1->init()) {
|
||||
delete rotaryEncoderInterruptImpl1;
|
||||
rotaryEncoderInterruptImpl1 = nullptr;
|
||||
}
|
||||
#endif
|
||||
cardKbI2cImpl = new CardKbI2cImpl();
|
||||
cardKbI2cImpl->init();
|
||||
|
||||
@@ -22,6 +22,10 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
|
||||
LOG_WARN("Invalid nodeInfo detected, is_licensed mismatch!");
|
||||
return true;
|
||||
}
|
||||
NodeNum sourceNum = getFrom(&mp);
|
||||
auto node = nodeDB->getMeshNode(sourceNum);
|
||||
if ((node->bitfield & NODEINFO_BITFIELD_HAS_XEDDSA_SIGNED_MASK) && !mp.xeddsa_signed)
|
||||
return true;
|
||||
|
||||
// Coerce user.id to be derived from the node number
|
||||
snprintf(p.id, sizeof(p.id), "!%08x", getFrom(&mp));
|
||||
|
||||
@@ -1,272 +0,0 @@
|
||||
#include "configuration.h"
|
||||
#if HAS_SCREEN
|
||||
|
||||
#include "graphics/SharedUIDisplay.h"
|
||||
#include "graphics/draw/NotificationRenderer.h"
|
||||
#include "input/RotaryEncoderInterruptImpl1.h"
|
||||
#include "input/UpDownInterruptImpl1.h"
|
||||
#include "modules/OnScreenKeyboardModule.h"
|
||||
#include <Arduino.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
OnScreenKeyboardModule &OnScreenKeyboardModule::instance()
|
||||
{
|
||||
static OnScreenKeyboardModule inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
OnScreenKeyboardModule::~OnScreenKeyboardModule()
|
||||
{
|
||||
if (keyboard) {
|
||||
delete keyboard;
|
||||
keyboard = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void OnScreenKeyboardModule::start(const char *header, const char *initialText, uint32_t durationMs,
|
||||
std::function<void(const std::string &)> cb)
|
||||
{
|
||||
if (keyboard) {
|
||||
delete keyboard;
|
||||
keyboard = nullptr;
|
||||
}
|
||||
keyboard = new VirtualKeyboard();
|
||||
callback = cb;
|
||||
if (header)
|
||||
keyboard->setHeader(header);
|
||||
if (initialText)
|
||||
keyboard->setInputText(initialText);
|
||||
|
||||
// Route VK submission/cancel events back into the module
|
||||
keyboard->setCallback([this](const std::string &text) {
|
||||
if (text.empty()) {
|
||||
this->onCancel();
|
||||
} else {
|
||||
this->onSubmit(text);
|
||||
}
|
||||
});
|
||||
|
||||
// Maintain legacy compatibility hooks
|
||||
NotificationRenderer::virtualKeyboard = keyboard;
|
||||
NotificationRenderer::textInputCallback = callback;
|
||||
}
|
||||
|
||||
void OnScreenKeyboardModule::stop(bool callEmptyCallback)
|
||||
{
|
||||
auto cb = callback;
|
||||
callback = nullptr;
|
||||
if (keyboard) {
|
||||
delete keyboard;
|
||||
keyboard = nullptr;
|
||||
}
|
||||
// Keep NotificationRenderer legacy pointers in sync
|
||||
NotificationRenderer::virtualKeyboard = nullptr;
|
||||
NotificationRenderer::textInputCallback = nullptr;
|
||||
clearPopup();
|
||||
if (callEmptyCallback && cb)
|
||||
cb("");
|
||||
}
|
||||
|
||||
void OnScreenKeyboardModule::handleInput(const InputEvent &event)
|
||||
{
|
||||
if (!keyboard)
|
||||
return;
|
||||
|
||||
if (processVirtualKeyboardInput(event, keyboard))
|
||||
return;
|
||||
|
||||
if (event.inputEvent == INPUT_BROKER_CANCEL)
|
||||
onCancel();
|
||||
}
|
||||
|
||||
bool OnScreenKeyboardModule::processVirtualKeyboardInput(const InputEvent &event, VirtualKeyboard *targetKeyboard)
|
||||
{
|
||||
if (!targetKeyboard)
|
||||
return false;
|
||||
|
||||
switch (event.inputEvent) {
|
||||
case INPUT_BROKER_UP:
|
||||
case INPUT_BROKER_UP_LONG:
|
||||
targetKeyboard->moveCursorUp();
|
||||
return true;
|
||||
case INPUT_BROKER_DOWN:
|
||||
case INPUT_BROKER_DOWN_LONG:
|
||||
targetKeyboard->moveCursorDown();
|
||||
return true;
|
||||
case INPUT_BROKER_LEFT:
|
||||
case INPUT_BROKER_ALT_PRESS:
|
||||
targetKeyboard->moveCursorLeft();
|
||||
return true;
|
||||
case INPUT_BROKER_RIGHT:
|
||||
case INPUT_BROKER_USER_PRESS:
|
||||
targetKeyboard->moveCursorRight();
|
||||
return true;
|
||||
case INPUT_BROKER_SELECT:
|
||||
targetKeyboard->handlePress();
|
||||
return true;
|
||||
case INPUT_BROKER_SELECT_LONG:
|
||||
targetKeyboard->handleLongPress();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool OnScreenKeyboardModule::draw(OLEDDisplay *display)
|
||||
{
|
||||
if (!keyboard)
|
||||
return false;
|
||||
|
||||
// Timeout
|
||||
if (keyboard->isTimedOut()) {
|
||||
onCancel();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clear full screen behind keyboard
|
||||
display->setColor(BLACK);
|
||||
display->fillRect(0, 0, display->getWidth(), display->getHeight());
|
||||
display->setColor(WHITE);
|
||||
keyboard->draw(display, 0, 0);
|
||||
|
||||
// Draw popup overlay if needed
|
||||
drawPopup(display);
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnScreenKeyboardModule::onSubmit(const std::string &text)
|
||||
{
|
||||
auto cb = callback;
|
||||
stop(false);
|
||||
if (cb)
|
||||
cb(text);
|
||||
}
|
||||
|
||||
void OnScreenKeyboardModule::onCancel()
|
||||
{
|
||||
stop(true);
|
||||
}
|
||||
|
||||
void OnScreenKeyboardModule::showPopup(const char *title, const char *content, uint32_t durationMs)
|
||||
{
|
||||
if (!title || !content)
|
||||
return;
|
||||
strncpy(popupTitle, title, sizeof(popupTitle) - 1);
|
||||
popupTitle[sizeof(popupTitle) - 1] = '\0';
|
||||
strncpy(popupMessage, content, sizeof(popupMessage) - 1);
|
||||
popupMessage[sizeof(popupMessage) - 1] = '\0';
|
||||
popupUntil = millis() + durationMs;
|
||||
popupVisible = true;
|
||||
}
|
||||
|
||||
void OnScreenKeyboardModule::clearPopup()
|
||||
{
|
||||
popupTitle[0] = '\0';
|
||||
popupMessage[0] = '\0';
|
||||
popupUntil = 0;
|
||||
popupVisible = false;
|
||||
}
|
||||
|
||||
void OnScreenKeyboardModule::drawPopupOverlay(OLEDDisplay *display)
|
||||
{
|
||||
// Only render the popup overlay (without drawing the keyboard)
|
||||
drawPopup(display);
|
||||
}
|
||||
|
||||
void OnScreenKeyboardModule::drawPopup(OLEDDisplay *display)
|
||||
{
|
||||
if (!popupVisible)
|
||||
return;
|
||||
if (millis() > popupUntil || popupMessage[0] == '\0') {
|
||||
popupVisible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Build lines and leverage NotificationRenderer inverted box drawing for consistent style
|
||||
constexpr uint16_t maxContentLines = 3;
|
||||
const bool hasTitle = popupTitle[0] != '\0';
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
const uint16_t maxWrapWidth = display->width() - 40;
|
||||
|
||||
auto wrapText = [&](const char *text, uint16_t availableWidth) -> std::vector<std::string> {
|
||||
std::vector<std::string> wrapped;
|
||||
std::string current;
|
||||
std::string word;
|
||||
const char *p = text;
|
||||
while (*p && wrapped.size() < maxContentLines) {
|
||||
while (*p && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) {
|
||||
if (*p == '\n') {
|
||||
if (!current.empty()) {
|
||||
wrapped.push_back(current);
|
||||
current.clear();
|
||||
if (wrapped.size() >= maxContentLines)
|
||||
break;
|
||||
}
|
||||
}
|
||||
++p;
|
||||
}
|
||||
if (!*p || wrapped.size() >= maxContentLines)
|
||||
break;
|
||||
word.clear();
|
||||
while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r')
|
||||
word += *p++;
|
||||
if (word.empty())
|
||||
continue;
|
||||
std::string test = current.empty() ? word : (current + " " + word);
|
||||
uint16_t w = display->getStringWidth(test.c_str(), test.length(), true);
|
||||
if (w <= availableWidth)
|
||||
current = test;
|
||||
else {
|
||||
if (!current.empty()) {
|
||||
wrapped.push_back(current);
|
||||
current = word;
|
||||
if (wrapped.size() >= maxContentLines)
|
||||
break;
|
||||
} else {
|
||||
current = word;
|
||||
while (current.size() > 1 &&
|
||||
display->getStringWidth(current.c_str(), current.length(), true) > availableWidth)
|
||||
current.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!current.empty() && wrapped.size() < maxContentLines)
|
||||
wrapped.push_back(current);
|
||||
return wrapped;
|
||||
};
|
||||
|
||||
std::vector<std::string> allLines;
|
||||
if (hasTitle)
|
||||
allLines.emplace_back(popupTitle);
|
||||
|
||||
char buf[sizeof(popupMessage)];
|
||||
strncpy(buf, popupMessage, sizeof(buf) - 1);
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
char *paragraph = strtok(buf, "\n");
|
||||
while (paragraph && allLines.size() < maxContentLines + (hasTitle ? 1 : 0)) {
|
||||
auto wrapped = wrapText(paragraph, maxWrapWidth);
|
||||
for (const auto &ln : wrapped) {
|
||||
if (allLines.size() >= maxContentLines + (hasTitle ? 1 : 0))
|
||||
break;
|
||||
allLines.push_back(ln);
|
||||
}
|
||||
paragraph = strtok(nullptr, "\n");
|
||||
}
|
||||
|
||||
std::vector<const char *> ptrs;
|
||||
for (const auto &ln : allLines)
|
||||
ptrs.push_back(ln.c_str());
|
||||
ptrs.push_back(nullptr);
|
||||
|
||||
// Use the standard notification box drawing from NotificationRenderer
|
||||
NotificationRenderer::drawNotificationBox(display, nullptr, ptrs.data(), allLines.size(), 0, 0);
|
||||
}
|
||||
|
||||
} // namespace graphics
|
||||
|
||||
#endif // HAS_SCREEN
|
||||
@@ -1,55 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "configuration.h"
|
||||
#if HAS_SCREEN
|
||||
|
||||
#include "graphics/Screen.h" // InputEvent
|
||||
#include "graphics/VirtualKeyboard.h"
|
||||
#include <OLEDDisplay.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace graphics
|
||||
{
|
||||
class OnScreenKeyboardModule
|
||||
{
|
||||
public:
|
||||
static OnScreenKeyboardModule &instance();
|
||||
|
||||
void start(const char *header, const char *initialText, uint32_t durationMs,
|
||||
std::function<void(const std::string &)> callback);
|
||||
|
||||
void stop(bool callEmptyCallback);
|
||||
|
||||
void handleInput(const InputEvent &event);
|
||||
static bool processVirtualKeyboardInput(const InputEvent &event, VirtualKeyboard *keyboard);
|
||||
bool draw(OLEDDisplay *display);
|
||||
|
||||
void showPopup(const char *title, const char *content, uint32_t durationMs);
|
||||
void clearPopup();
|
||||
// Draw only the popup overlay (used when legacy virtualKeyboard draws the keyboard)
|
||||
void drawPopupOverlay(OLEDDisplay *display);
|
||||
|
||||
private:
|
||||
OnScreenKeyboardModule() = default;
|
||||
~OnScreenKeyboardModule();
|
||||
OnScreenKeyboardModule(const OnScreenKeyboardModule &) = delete;
|
||||
OnScreenKeyboardModule &operator=(const OnScreenKeyboardModule &) = delete;
|
||||
|
||||
void onSubmit(const std::string &text);
|
||||
void onCancel();
|
||||
|
||||
void drawPopup(OLEDDisplay *display);
|
||||
|
||||
VirtualKeyboard *keyboard = nullptr;
|
||||
std::function<void(const std::string &)> callback;
|
||||
|
||||
char popupTitle[64] = {0};
|
||||
char popupMessage[256] = {0};
|
||||
uint32_t popupUntil = 0;
|
||||
bool popupVisible = false;
|
||||
};
|
||||
|
||||
} // namespace graphics
|
||||
|
||||
#endif // HAS_SCREEN
|
||||
@@ -71,7 +71,7 @@ SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("Serial
|
||||
api_type = TYPE_SERIAL;
|
||||
}
|
||||
static Print *serialPrint = &Serial;
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C6) || defined(RAK3172) || defined(EBYTE_E77_MBL) || defined(CONFIG_IDF_TARGET_ESP32H2)
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C6) || defined(RAK3172) || defined(EBYTE_E77_MBL)
|
||||
SerialModule::SerialModule() : StreamAPI(&Serial1), concurrency::OSThread("Serial")
|
||||
{
|
||||
api_type = TYPE_SERIAL;
|
||||
@@ -175,7 +175,7 @@ int32_t SerialModule::runOnce()
|
||||
// Give it a chance to flush out 💩
|
||||
delay(10);
|
||||
}
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2)
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||
if (moduleConfig.serial.rxd && moduleConfig.serial.txd) {
|
||||
Serial1.setRxBufferSize(RX_BUFFER);
|
||||
Serial1.begin(baud, SERIAL_8N1, moduleConfig.serial.rxd, moduleConfig.serial.txd);
|
||||
@@ -277,7 +277,7 @@ int32_t SerialModule::runOnce()
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2)
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||
while (Serial1.available()) {
|
||||
serialPayloadSize = Serial1.readBytes(serialBytes, meshtastic_Constants_DATA_PAYLOAD_LEN);
|
||||
#else
|
||||
@@ -538,7 +538,7 @@ void SerialModule::processWXSerial()
|
||||
{
|
||||
#if !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(CONFIG_IDF_TARGET_ESP32C6) && \
|
||||
!defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M3) && !defined(ELECROW_ThinkNode_M5) && \
|
||||
!defined(ARCH_STM32WL) && !defined(MUZI_BASE) && !defined(CONFIG_IDF_TARGET_ESP32H2)
|
||||
!defined(ARCH_STM32WL) && !defined(MUZI_BASE)
|
||||
static unsigned int lastAveraged = 0;
|
||||
static unsigned int averageIntervalMillis = 300000; // 5 minutes hard coded.
|
||||
static double dir_sum_sin = 0;
|
||||
|
||||
@@ -651,8 +651,8 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
{
|
||||
LOG_INFO("BLE incoming connection %s", connInfo.getAddress().toString().c_str());
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||
const uint16_t connHandle = connInfo.getConnHandle();
|
||||
#if NIMBLE_ENABLE_2M_PHY && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6))
|
||||
int phyResult =
|
||||
ble_gap_set_prefered_le_phy(connHandle, BLE_GAP_LE_PHY_2M_MASK, BLE_GAP_LE_PHY_2M_MASK, BLE_GAP_LE_PHY_CODED_ANY);
|
||||
if (phyResult == 0) {
|
||||
@@ -660,7 +660,6 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
} else {
|
||||
LOG_WARN("Failed to prefer 2M PHY for conn %u, rc=%d", connHandle, phyResult);
|
||||
}
|
||||
#endif
|
||||
|
||||
int dataLenResult = ble_gap_set_data_len(connHandle, kPreferredBleTxOctets, kPreferredBleTxTimeUs);
|
||||
if (dataLenResult == 0) {
|
||||
@@ -671,10 +670,9 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
|
||||
LOG_INFO("BLE conn %u initial MTU %u (target %u)", connHandle, connInfo.getMTU(), kPreferredBleMtu);
|
||||
pServer->updateConnParams(connHandle, 6, 12, 0, 200);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason)
|
||||
{
|
||||
LOG_INFO("BLE disconnect reason: %d", reason);
|
||||
@@ -820,7 +818,7 @@ void NimbleBluetooth::setup()
|
||||
NimBLEDevice::init(getDeviceName());
|
||||
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
|
||||
|
||||
#if NIMBLE_ENABLE_2M_PHY && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6))
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||
int mtuResult = NimBLEDevice::setMTU(kPreferredBleMtu);
|
||||
if (mtuResult == 0) {
|
||||
LOG_INFO("BLE MTU request set to %u", kPreferredBleMtu);
|
||||
|
||||
@@ -169,8 +169,6 @@ void esp32Setup()
|
||||
// #define APP_WATCHDOG_SECS 45
|
||||
#define APP_WATCHDOG_SECS 90
|
||||
|
||||
// esp_task_wdt_init returns an unknown error, so skip it on ESP32H2
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32C6
|
||||
esp_task_wdt_config_t *wdt_config = (esp_task_wdt_config_t *)malloc(sizeof(esp_task_wdt_config_t));
|
||||
wdt_config->timeout_ms = APP_WATCHDOG_SECS * 1000;
|
||||
@@ -183,7 +181,7 @@ void esp32Setup()
|
||||
#endif
|
||||
res = esp_task_wdt_add(NULL);
|
||||
assert(res == ESP_OK);
|
||||
#endif
|
||||
|
||||
#if HAS_32768HZ
|
||||
enableSlowCLK();
|
||||
#endif
|
||||
@@ -225,10 +223,9 @@ void cpuDeepSleep(uint32_t msecToWake)
|
||||
13,
|
||||
#endif
|
||||
34, 35, 37};
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
||||
|
||||
for (int i = 0; i < sizeof(rtcGpios); i++)
|
||||
rtc_gpio_isolate((gpio_num_t)rtcGpios[i]);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// FIXME, disable internal rtc pullups/pulldowns on the non isolated pins. for inputs that we aren't using
|
||||
@@ -261,10 +258,10 @@ void cpuDeepSleep(uint32_t msecToWake)
|
||||
|
||||
#endif // #end ESP32S3_WAKE_TYPE
|
||||
#endif
|
||||
#ifdef ESP_PD_DOMAIN_RTC_PERIPH
|
||||
|
||||
// We want RTC peripherals to stay on
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
||||
#endif
|
||||
|
||||
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
|
||||
esp_deep_sleep_start(); // TBD mA sleep current (battery)
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
#elif defined(HELTEC_MESH_SOLAR)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_MESH_SOLAR
|
||||
#elif defined(MUZI_BASE)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_MUZI_BASE
|
||||
#define HW_VENDOR meshtastic_HardwareModel_RESERVED_FRIED_CHICKEN
|
||||
#else
|
||||
#define HW_VENDOR meshtastic_HardwareModel_NRF52_UNKNOWN
|
||||
#endif
|
||||
|
||||
@@ -335,16 +335,6 @@ void cpuDeepSleep(uint32_t msecToWake)
|
||||
if (Serial1) // A straightforward solution to the wake from deepsleep problem
|
||||
Serial1.end();
|
||||
#endif
|
||||
|
||||
#ifdef TTGO_T_ECHO
|
||||
// To power off the T-Echo, the display must be set
|
||||
// as an input pin; otherwise, there will be leakage current.
|
||||
pinMode(PIN_EINK_CS, INPUT);
|
||||
pinMode(PIN_EINK_DC, INPUT);
|
||||
pinMode(PIN_EINK_RES, INPUT);
|
||||
pinMode(PIN_EINK_BUSY, INPUT);
|
||||
#endif
|
||||
|
||||
setBluetoothEnable(false);
|
||||
|
||||
#ifdef RAK4630
|
||||
|
||||
13
src/power.h
13
src/power.h
@@ -13,7 +13,6 @@
|
||||
#define NUM_OCV_POINTS 11
|
||||
#endif
|
||||
|
||||
// Device specific curves go in variant.h
|
||||
#ifndef OCV_ARRAY
|
||||
#ifdef CELL_TYPE_LIFEPO4
|
||||
#define OCV_ARRAY 3400, 3350, 3320, 3290, 3270, 3260, 3250, 3230, 3200, 3120, 3000
|
||||
@@ -25,6 +24,18 @@
|
||||
#define OCV_ARRAY 1400, 1300, 1280, 1270, 1260, 1250, 1240, 1230, 1210, 1150, 1000
|
||||
#elif defined(CELL_TYPE_LTO)
|
||||
#define OCV_ARRAY 2700, 2560, 2540, 2520, 2500, 2460, 2420, 2400, 2380, 2320, 1500
|
||||
#elif defined(TRACKER_T1000_E)
|
||||
#define OCV_ARRAY 4190, 4042, 3957, 3885, 3820, 3776, 3746, 3725, 3696, 3644, 3100
|
||||
#elif defined(HELTEC_MESH_POCKET_BATTERY_5000)
|
||||
#define OCV_ARRAY 4300, 4240, 4120, 4000, 3888, 3800, 3740, 3698, 3655, 3580, 3400
|
||||
#elif defined(HELTEC_MESH_POCKET_BATTERY_10000)
|
||||
#define OCV_ARRAY 4100, 4060, 3960, 3840, 3729, 3625, 3550, 3500, 3420, 3345, 3100
|
||||
#elif defined(SEEED_WIO_TRACKER_L1)
|
||||
#define OCV_ARRAY 4200, 3876, 3826, 3763, 3713, 3660, 3573, 3485, 3422, 3359, 3300
|
||||
#elif defined(SEEED_SOLAR_NODE)
|
||||
#define OCV_ARRAY 4200, 3986, 3922, 3812, 3734, 3645, 3527, 3420, 3281, 3087, 2786
|
||||
#elif defined(WISMESH_TAG)
|
||||
#define OCV_ARRAY 4240, 4112, 4029, 3970, 3906, 3846, 3824, 3802, 3776, 3650, 3072
|
||||
#else // LiIon
|
||||
#define OCV_ARRAY 4190, 4050, 3990, 3890, 3800, 3720, 3630, 3530, 3420, 3300, 3100
|
||||
#endif
|
||||
|
||||
@@ -388,10 +388,10 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
|
||||
uint64_t sleepUsec = sleepMsec * 1000LL;
|
||||
|
||||
// NOTE! ESP docs say we must disable bluetooth and wifi before light sleep
|
||||
#ifdef ESP_PD_DOMAIN_RTC_PERIPH
|
||||
|
||||
// We want RTC peripherals to stay on
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
||||
#endif
|
||||
|
||||
#if defined(BUTTON_PIN) && defined(BUTTON_NEED_PULLUP)
|
||||
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
|
||||
#endif
|
||||
@@ -523,8 +523,6 @@ void enableModemSleep()
|
||||
esp32_config.max_freq_mhz = CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ;
|
||||
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||
esp32_config.max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ;
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
esp32_config.max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ;
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
esp32_config.max_freq_mhz = CONFIG_ESP32C3_DEFAULT_CPU_FREQ_MHZ;
|
||||
#else
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "CryptoEngine.h"
|
||||
|
||||
#include "TestUtil.h"
|
||||
#include <XEdDSA.h>
|
||||
#include <unity.h>
|
||||
|
||||
void HexToBytes(uint8_t *result, const std::string hex, size_t len = 0)
|
||||
@@ -152,6 +153,31 @@ void test_PKC(void)
|
||||
TEST_ASSERT_EQUAL_MEMORY(expected_decrypted, decrypted, 10);
|
||||
}
|
||||
|
||||
void test_XEdDSA(void)
|
||||
{
|
||||
uint8_t private_key[32];
|
||||
uint8_t x_public_key[32];
|
||||
uint8_t ed_private_key[32];
|
||||
uint8_t ed_public_key[32];
|
||||
uint8_t ed_public_key2[32];
|
||||
meshtastic_UserLite_public_key_t public_key;
|
||||
uint8_t message[] = "This is a test!";
|
||||
uint8_t message2[] = "This is a test.";
|
||||
uint8_t signature[64];
|
||||
for (int times = 0; times < 10; times++) {
|
||||
printf("Start of time %u\n", times);
|
||||
crypto->generateKeyPair(x_public_key, private_key);
|
||||
// crypto->setDHPrivateKey(private_key);
|
||||
XEdDSA::priv_curve_to_ed_keys(private_key, ed_private_key, ed_public_key);
|
||||
crypto->curve_to_ed_pub(x_public_key, ed_public_key2);
|
||||
TEST_ASSERT_EQUAL_MEMORY(ed_public_key, ed_public_key2, 32);
|
||||
|
||||
crypto->xeddsa_sign(message, sizeof(message), signature);
|
||||
TEST_ASSERT(crypto->xeddsa_verify(x_public_key, message, sizeof(message), signature));
|
||||
TEST_ASSERT_FALSE(crypto->xeddsa_verify(x_public_key, message2, sizeof(message), signature));
|
||||
}
|
||||
}
|
||||
|
||||
void test_AES_CTR(void)
|
||||
{
|
||||
uint8_t expected[32];
|
||||
@@ -192,6 +218,7 @@ void setup()
|
||||
RUN_TEST(test_DH25519);
|
||||
RUN_TEST(test_AES_CTR);
|
||||
RUN_TEST(test_PKC);
|
||||
RUN_TEST(test_XEdDSA);
|
||||
exit(UNITY_END()); // stop unit testing
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,6 @@
|
||||
#define SCREEN_TRANSITION_FRAMERATE 5 // fps
|
||||
#define DISPLAY_FORCE_SMALL_FONTS
|
||||
#define TFT_BACKLIGHT_ON LOW
|
||||
#define USE_TFTDISPLAY 1
|
||||
|
||||
// Battery
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ build_src_filter =
|
||||
build_flags =
|
||||
${esp32_base.build_flags}
|
||||
-I variants/esp32/m5stack_core
|
||||
-DILI9341_DRIVER
|
||||
-DM5STACK
|
||||
-DUSER_SETUP_LOADED
|
||||
-DTFT_SDA_READ
|
||||
|
||||
@@ -34,13 +34,11 @@
|
||||
#define GPS_RX_PIN 16
|
||||
#define GPS_TX_PIN 17
|
||||
|
||||
#define ILI9341_DRIVER
|
||||
#define TFT_HEIGHT 240
|
||||
#define TFT_WIDTH 320
|
||||
#define TFT_OFFSET_X 0
|
||||
#define TFT_OFFSET_Y 0
|
||||
#define TFT_BUSY -1
|
||||
#define USE_TFTDISPLAY 1
|
||||
|
||||
// LCD screens are slow, so slowdown the wipe so it looks better
|
||||
#define SCREEN_TRANSITION_FRAMERATE 1 // fps
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#define ST7789_SCK 18
|
||||
#define ST7789_CS 5
|
||||
#define ST7789_RS 26
|
||||
#define USE_TFTDISPLAY 1
|
||||
// I don't have a 'wiphone' but this I think should not be defined this way (don't set TFT_BL if we don't have a hw way to control
|
||||
// it)
|
||||
// #define ST7789_BL -1 // EXTENDER_PIN(9)
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
[esp32h2_base]
|
||||
extends = esp32_base
|
||||
platform =
|
||||
# Do not renovate until we have switched to pioarduino tagged builds
|
||||
https://github.com/Jason2866/platform-espressif32/archive/39475a7213fa3a52b0c2048d1a31c215fc85fcf8.zip
|
||||
build_flags =
|
||||
${arduino_base.build_flags}
|
||||
-Wall
|
||||
-Wextra
|
||||
-Isrc/platform/esp32
|
||||
-std=c++11
|
||||
-DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
|
||||
-DSERIAL_BUFFER_SIZE=4096
|
||||
-DLIBPAX_ARDUINO
|
||||
-DLIBPAX_WIFI
|
||||
-DLIBPAX_BLE
|
||||
-DMESHTASTIC_EXCLUDE_WEBSERVER
|
||||
;-DDEBUG_HEAP
|
||||
; TEMP
|
||||
-DHAS_BLUETOOTH=0
|
||||
-DMESHTASTIC_EXCLUDE_PAXCOUNTER
|
||||
-DMESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
${networking_base.lib_deps}
|
||||
${environmental_base.lib_deps}
|
||||
${environmental_extra.lib_deps}
|
||||
${radiolib_base.lib_deps}
|
||||
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
|
||||
lewisxhe/XPowersLib@0.3.1
|
||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||
rweather/Crypto@0.4.0
|
||||
|
||||
build_src_filter =
|
||||
${esp32_base.build_src_filter} -<mesh/http>
|
||||
|
||||
monitor_speed = 460800
|
||||
monitor_filters = esp32_h2_exception_decoder
|
||||
|
||||
lib_ignore =
|
||||
NonBlockingRTTTL
|
||||
NimBLE-Arduino
|
||||
libpax
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
[env:waveshare-esp32h2]
|
||||
extends = esp32h2_base
|
||||
board = esp32-h2-devkitm-1
|
||||
board_build.f_flash = 16000000L
|
||||
board_level = pr
|
||||
build_flags =
|
||||
${esp32h2_base.build_flags}
|
||||
-I variants/esp32h2/waveshare-esp32-h2
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
-DARDUINO_USB_MODE=1
|
||||
-DHAS_WIFI=0
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user