Compare commits

..

10 Commits

Author SHA1 Message Date
Jonathan Bennett
90e2083d2c clean up merge 2025-12-10 15:27:09 -06:00
Jonathan Bennett
af1d4e08ff Merge branch 'develop' into refactor-nodedb 2025-12-10 11:15:18 -06:00
Jonathan Bennett
d772cd4431 Retain getMeshNode virtual marker for tests 2025-10-17 12:16:59 -05:00
Jonathan Bennett
b7b8071056 Merge branch 'develop' into refactor-nodedb 2025-10-17 12:15:36 -05:00
Jonathan Bennett
4a3d28f06b Add FUNCTION_START and FUNCTION_END to public NodeDB functions 2025-10-17 00:36:58 -05:00
Jonathan Bennett
0f4210d2e8 Merge branch 'develop' into refactor-nodedb 2025-10-16 14:40:23 -05:00
Jonathan Bennett
dc6e109329 Finish adding NodeDB wrapper classes 2025-10-16 14:37:33 -05:00
Jonathan Bennett
10141b2562 InkHUD: don't try to access meshNodes directly 2025-10-16 12:08:00 -05:00
Jonathan Bennett
770085ddf7 Begin refactoring nodedb.cpp with public wrappers 2025-10-16 09:44:35 -05:00
Jonathan Bennett
833f950edc Move loadProto and saveProto into FSCommon 2025-10-16 09:36:54 -05:00
72 changed files with 687 additions and 809 deletions

View File

@@ -76,7 +76,7 @@ runs:
done done
- name: PlatformIO ${{ inputs.arch }} download cache - name: PlatformIO ${{ inputs.arch }} download cache
uses: actions/cache@v5 uses: actions/cache@v4
with: with:
path: ~/.platformio/.cache path: ~/.platformio/.cache
key: pio-cache-${{ inputs.arch }}-${{ hashFiles('.github/actions/**', '**.ini') }} key: pio-cache-${{ inputs.arch }}-${{ hashFiles('.github/actions/**', '**.ini') }}
@@ -100,7 +100,7 @@ runs:
id: version id: version
- name: Store binaries as an artifact - name: Store binaries as an artifact
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v5
with: with:
name: firmware-${{ inputs.arch }}-${{ inputs.board }}-${{ steps.version.outputs.long }} name: firmware-${{ inputs.arch }}-${{ inputs.board }}-${{ steps.version.outputs.long }}
overwrite: true overwrite: true

View File

@@ -64,7 +64,7 @@ jobs:
PKG_VERSION: ${{ steps.version.outputs.deb }} PKG_VERSION: ${{ steps.version.outputs.deb }}
- name: Store binaries as an artifact - name: Store binaries as an artifact
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v5
with: with:
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
overwrite: true overwrite: true

View File

@@ -68,7 +68,7 @@ jobs:
echo '```' >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY
- name: Store binaries as an artifact - name: Store binaries as an artifact
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v5
id: upload id: upload
with: with:
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }} name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}

View File

@@ -98,7 +98,7 @@ jobs:
ref: ${{github.event.pull_request.head.ref}} ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}} repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: actions/download-artifact@v7 - uses: actions/download-artifact@v6
with: with:
path: ./ path: ./
pattern: firmware-*-* pattern: firmware-*-*
@@ -111,7 +111,7 @@ jobs:
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
- name: Repackage in single firmware zip - name: Repackage in single firmware zip
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v5
with: with:
name: firmware-${{inputs.target}}-${{ needs.version.outputs.long }} name: firmware-${{inputs.target}}-${{ needs.version.outputs.long }}
overwrite: true overwrite: true
@@ -127,7 +127,7 @@ jobs:
./Meshtastic_nRF52_factory_erase*.uf2 ./Meshtastic_nRF52_factory_erase*.uf2
retention-days: 30 retention-days: 30
- uses: actions/download-artifact@v7 - uses: actions/download-artifact@v6
with: with:
pattern: firmware-*-${{ needs.version.outputs.long }} pattern: firmware-*-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true
@@ -146,7 +146,7 @@ jobs:
run: zip -j -9 -r ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output run: zip -j -9 -r ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output
- name: Repackage in single elfs zip - name: Repackage in single elfs zip
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v5
with: with:
name: debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip name: debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip
overwrite: true overwrite: true

View File

@@ -77,21 +77,16 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
check: ${{ fromJson(needs.setup.outputs.check) }} check: ${{ fromJson(needs.setup.outputs.check) }}
# Use 'arctastic' self-hosted runner pool when checking in the main repo
runs-on: ${{ github.repository_owner == 'meshtastic' && 'arctastic' || 'ubuntu-latest' }} runs-on: ubuntu-latest
if: ${{ github.event_name != 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }} if: ${{ github.event_name != 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
with: - name: Build base
submodules: recursive id: base
ref: ${{github.event.pull_request.head.ref}} uses: ./.github/actions/setup-base
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Check ${{ matrix.check.board }} - name: Check ${{ matrix.check.board }}
uses: meshtastic/gh-action-firmware@main run: bin/check-all.sh ${{ matrix.check.board }}
with:
pio_platform: ${{ matrix.check.platform }}
pio_env: ${{ matrix.check.board }}
pio_target: check
build: build:
needs: [setup, version] needs: [setup, version]
@@ -173,7 +168,7 @@ jobs:
ref: ${{github.event.pull_request.head.ref}} ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}} repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: actions/download-artifact@v7 - uses: actions/download-artifact@v6
with: with:
path: ./ path: ./
pattern: firmware-${{matrix.arch}}-* pattern: firmware-${{matrix.arch}}-*
@@ -183,7 +178,7 @@ jobs:
run: ls -R run: ls -R
- name: Repackage in single firmware zip - name: Repackage in single firmware zip
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v5
with: with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }} name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
overwrite: true overwrite: true
@@ -200,7 +195,7 @@ jobs:
./Meshtastic_nRF52_factory_erase*.uf2 ./Meshtastic_nRF52_factory_erase*.uf2
retention-days: 30 retention-days: 30
- uses: actions/download-artifact@v7 - uses: actions/download-artifact@v6
with: with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }} name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true
@@ -219,7 +214,7 @@ jobs:
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
- name: Repackage in single elfs zip - name: Repackage in single elfs zip
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v5
with: with:
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }} name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
overwrite: true overwrite: true
@@ -260,14 +255,14 @@ jobs:
Autogenerated by github action, developer should edit as required before publishing... Autogenerated by github action, developer should edit as required before publishing...
- name: Download source deb - name: Download source deb
uses: actions/download-artifact@v7 uses: actions/download-artifact@v6
with: with:
pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src
merge-multiple: true merge-multiple: true
path: ./output/debian-src path: ./output/debian-src
- name: Download `native-tft` pio deps - name: Download `native-tft` pio deps
uses: actions/download-artifact@v7 uses: actions/download-artifact@v6
with: with:
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }} pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true
@@ -291,7 +286,7 @@ jobs:
}' > firmware-${{ needs.version.outputs.long }}.json }' > firmware-${{ needs.version.outputs.long }}.json
- name: Save Release manifest artifact - name: Save Release manifest artifact
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v5
with: with:
name: manifest-${{ needs.version.outputs.long }} name: manifest-${{ needs.version.outputs.long }}
overwrite: true overwrite: true
@@ -332,7 +327,7 @@ jobs:
with: with:
python-version: 3.x python-version: 3.x
- uses: actions/download-artifact@v7 - uses: actions/download-artifact@v6
with: with:
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }} pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true
@@ -349,7 +344,7 @@ jobs:
- name: Zip firmware - name: Zip firmware
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
- uses: actions/download-artifact@v7 - uses: actions/download-artifact@v6
with: with:
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }} name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true
@@ -388,14 +383,14 @@ jobs:
python-version: 3.x python-version: 3.x
- name: Get firmware artifacts - name: Get firmware artifacts
uses: actions/download-artifact@v7 uses: actions/download-artifact@v6
with: with:
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }} pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true
path: ./publish path: ./publish
- name: Get manifest artifact - name: Get manifest artifact
uses: actions/download-artifact@v7 uses: actions/download-artifact@v6
with: with:
pattern: manifest-${{ needs.version.outputs.long }} pattern: manifest-${{ needs.version.outputs.long }}
path: ./publish path: ./publish

View File

@@ -147,7 +147,7 @@ jobs:
ref: ${{github.event.pull_request.head.ref}} ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}} repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: actions/download-artifact@v7 - uses: actions/download-artifact@v6
with: with:
path: ./ path: ./
pattern: firmware-${{matrix.arch}}-* pattern: firmware-${{matrix.arch}}-*
@@ -160,7 +160,7 @@ jobs:
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
- name: Repackage in single firmware zip - name: Repackage in single firmware zip
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v5
with: with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }} name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
overwrite: true overwrite: true
@@ -176,7 +176,7 @@ jobs:
./Meshtastic_nRF52_factory_erase*.uf2 ./Meshtastic_nRF52_factory_erase*.uf2
retention-days: 30 retention-days: 30
- uses: actions/download-artifact@v7 - uses: actions/download-artifact@v6
with: with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }} name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true
@@ -195,7 +195,7 @@ jobs:
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
- name: Repackage in single elfs zip - name: Repackage in single elfs zip
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v5
with: with:
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }} name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
overwrite: true overwrite: true
@@ -235,14 +235,14 @@ jobs:
Autogenerated by github action, developer should edit as required before publishing... Autogenerated by github action, developer should edit as required before publishing...
- name: Download source deb - name: Download source deb
uses: actions/download-artifact@v7 uses: actions/download-artifact@v6
with: with:
pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src
merge-multiple: true merge-multiple: true
path: ./output/debian-src path: ./output/debian-src
- name: Download `native-tft` pio deps - name: Download `native-tft` pio deps
uses: actions/download-artifact@v7 uses: actions/download-artifact@v6
with: with:
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }} pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true
@@ -292,7 +292,7 @@ jobs:
with: with:
python-version: 3.x python-version: 3.x
- uses: actions/download-artifact@v7 - uses: actions/download-artifact@v6
with: with:
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }} pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true
@@ -309,7 +309,7 @@ jobs:
- name: Zip firmware - name: Zip firmware
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
- uses: actions/download-artifact@v7 - uses: actions/download-artifact@v6
with: with:
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }} name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true
@@ -347,7 +347,7 @@ jobs:
with: with:
python-version: 3.x python-version: 3.x
- uses: actions/download-artifact@v7 - uses: actions/download-artifact@v6
with: with:
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }} pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true

View File

@@ -58,7 +58,7 @@ jobs:
id: version id: version
- name: Download artifacts - name: Download artifacts
uses: actions/download-artifact@v7 uses: actions/download-artifact@v6
with: with:
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
merge-multiple: true merge-multiple: true

View File

@@ -56,7 +56,7 @@ jobs:
PLATFORMIO_CORE_DIR: pio/core PLATFORMIO_CORE_DIR: pio/core
- name: Store binaries as an artifact - name: Store binaries as an artifact
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v5
with: with:
name: platformio-deps-${{ inputs.pio_env }}-${{ steps.version.outputs.long }} name: platformio-deps-${{ inputs.pio_env }}-${{ steps.version.outputs.long }}
overwrite: true overwrite: true

View File

@@ -60,7 +60,7 @@ jobs:
id: version id: version
- name: Download artifacts - name: Download artifacts
uses: actions/download-artifact@v7 uses: actions/download-artifact@v6
with: with:
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
merge-multiple: true merge-multiple: true

View File

@@ -50,7 +50,7 @@ jobs:
- name: Download test artifacts - name: Download test artifacts
if: needs.native-tests.result != 'skipped' if: needs.native-tests.result != 'skipped'
uses: actions/download-artifact@v7 uses: actions/download-artifact@v6
with: with:
name: platformio-test-report-${{ steps.version.outputs.long }} name: platformio-test-report-${{ steps.version.outputs.long }}
merge-multiple: true merge-multiple: true

View File

@@ -102,7 +102,7 @@ jobs:
PIP_DISABLE_PIP_VERSION_CHECK: 1 PIP_DISABLE_PIP_VERSION_CHECK: 1
- name: Create Bumps pull request - name: Create Bumps pull request
uses: peter-evans/create-pull-request@v8 uses: peter-evans/create-pull-request@v7
with: with:
base: ${{ github.event.repository.default_branch }} base: ${{ github.event.repository.default_branch }}
branch: create-pull-request/bump-version branch: create-pull-request/bump-version

View File

@@ -33,7 +33,7 @@ jobs:
# step 3 # step 3
- name: save report as pipeline artifact - name: save report as pipeline artifact
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v5
with: with:
name: report.sarif name: report.sarif
overwrite: true overwrite: true

View File

@@ -59,7 +59,7 @@ jobs:
id: version id: version
- name: Save coverage information - name: Save coverage information
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v5
if: always() # run this step even if previous step failed if: always() # run this step even if previous step failed
with: with:
name: lcov-coverage-info-native-simulator-test-${{ steps.version.outputs.long }} name: lcov-coverage-info-native-simulator-test-${{ steps.version.outputs.long }}
@@ -94,7 +94,7 @@ jobs:
- name: Save test results - name: Save test results
if: always() # run this step even if previous step failed if: always() # run this step even if previous step failed
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v5
with: with:
name: platformio-test-report-${{ steps.version.outputs.long }} name: platformio-test-report-${{ steps.version.outputs.long }}
overwrite: true overwrite: true
@@ -108,7 +108,7 @@ jobs:
sed -i -e "s#${PWD}#.#" coverage_tests.info # Make paths relative. sed -i -e "s#${PWD}#.#" coverage_tests.info # Make paths relative.
- name: Save coverage information - name: Save coverage information
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v5
if: always() # run this step even if previous step failed if: always() # run this step even if previous step failed
with: with:
name: lcov-coverage-info-native-platformio-tests-${{ steps.version.outputs.long }} name: lcov-coverage-info-native-platformio-tests-${{ steps.version.outputs.long }}
@@ -137,7 +137,7 @@ jobs:
id: version id: version
- name: Download test artifacts - name: Download test artifacts
uses: actions/download-artifact@v7 uses: actions/download-artifact@v6
with: with:
name: platformio-test-report-${{ steps.version.outputs.long }} name: platformio-test-report-${{ steps.version.outputs.long }}
merge-multiple: true merge-multiple: true
@@ -150,7 +150,7 @@ jobs:
reporter: java-junit reporter: java-junit
- name: Download coverage artifacts - name: Download coverage artifacts
uses: actions/download-artifact@v7 uses: actions/download-artifact@v6
with: with:
pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }} pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }}
path: code-coverage-report path: code-coverage-report
@@ -163,7 +163,7 @@ jobs:
genhtml --quiet --legend --prefix "${PWD}" code-coverage-report/coverage_src.info --output-directory code-coverage-report genhtml --quiet --legend --prefix "${PWD}" code-coverage-report/coverage_src.info --output-directory code-coverage-report
- name: Save Code Coverage Report - name: Save Code Coverage Report
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v5
with: with:
name: code-coverage-report-${{ steps.version.outputs.long }} name: code-coverage-report-${{ steps.version.outputs.long }}
path: code-coverage-report path: code-coverage-report

View File

@@ -31,7 +31,7 @@ jobs:
./bin/regen-protos.sh ./bin/regen-protos.sh
- name: Create pull request - name: Create pull request
uses: peter-evans/create-pull-request@v8 uses: peter-evans/create-pull-request@v7
with: with:
branch: create-pull-request/update-protobufs branch: create-pull-request/update-protobufs
labels: submodules labels: submodules

View File

@@ -9,24 +9,24 @@ plugins:
lint: lint:
enabled: enabled:
- checkov@3.2.495 - checkov@3.2.495
- renovate@42.64.1 - renovate@42.30.4
- prettier@3.7.4 - prettier@3.7.4
- trufflehog@3.92.3 - trufflehog@3.91.2
- yamllint@1.37.1 - yamllint@1.37.1
- bandit@1.9.2 - bandit@1.9.2
- trivy@0.68.2 - trivy@0.67.2
- taplo@0.10.0 - taplo@0.10.0
- ruff@0.14.10 - ruff@0.14.7
- isort@7.0.0 - isort@7.0.0
- markdownlint@0.47.0 - markdownlint@0.46.0
- oxipng@10.0.0 - oxipng@9.1.5
- svgo@4.0.0 - svgo@4.0.0
- actionlint@1.7.9 - actionlint@1.7.9
- flake8@7.3.0 - flake8@7.3.0
- hadolint@2.14.0 - hadolint@2.14.0
- shfmt@3.6.0 - shfmt@3.6.0
- shellcheck@0.11.0 - shellcheck@0.11.0
- black@25.12.0 - black@25.11.0
- git-diff-check - git-diff-check
- gitleaks@8.30.0 - gitleaks@8.30.0
- clang-format@16.0.3 - clang-format@16.0.3

View File

@@ -22,7 +22,7 @@ export APP_VERSION=$VERSION
basename=firmware-$1-$VERSION basename=firmware-$1-$VERSION
pio run --environment $1 -t mtjson # -v pio run --environment $1 # -v
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
@@ -32,10 +32,20 @@ cp $BUILDDIR/$basename.factory.bin $OUTDIR/$basename.factory.bin
echo "Copying ESP32 update bin file" echo "Copying ESP32 update bin file"
cp $BUILDDIR/$basename.bin $OUTDIR/$basename.bin cp $BUILDDIR/$basename.bin $OUTDIR/$basename.bin
echo "Copying Filesystem for ESP32 targets" echo "Building Filesystem for ESP32 targets"
# If you want to build the webui, uncomment the following lines
# pio run --environment $1 -t buildfs
# cp .pio/build/$1/littlefs.bin $OUTDIR/littlefswebui-$1-$VERSION.bin
# # 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 $BUILDDIR/littlefs-$1-$VERSION.bin $OUTDIR/littlefs-$1-$VERSION.bin
cp bin/device-install.* $OUTDIR/ cp bin/device-install.* $OUTDIR/
cp bin/device-update.* $OUTDIR/ cp bin/device-update.* $OUTDIR/
echo "Copying manifest" # 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 $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json

View File

@@ -22,7 +22,7 @@ export APP_VERSION=$VERSION
basename=firmware-$1-$VERSION basename=firmware-$1-$VERSION
pio run --environment $1 -t mtjson # -v pio run --environment $1 # -v
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
@@ -47,5 +47,8 @@ if (echo $1 | grep -q "rak4631"); then
cp $SRCHEX $OUTDIR/ cp $SRCHEX $OUTDIR/
fi fi
echo "Copying manifest" # 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 $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json

View File

@@ -22,12 +22,15 @@ export APP_VERSION=$VERSION
basename=firmware-$1-$VERSION basename=firmware-$1-$VERSION
pio run --environment $1 -t mtjson # -v pio run --environment $1 # -v
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
echo "Copying uf2 file" echo "Copying uf2 file"
cp $BUILDDIR/$basename.uf2 $OUTDIR/$basename.uf2 cp $BUILDDIR/$basename.uf2 $OUTDIR/$basename.uf2
echo "Copying manifest" # 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 $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json

View File

@@ -22,12 +22,15 @@ export APP_VERSION=$VERSION
basename=firmware-$1-$VERSION basename=firmware-$1-$VERSION
pio run --environment $1 -t mtjson # -v pio run --environment $1 # -v
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
echo "Copying STM32 bin file" echo "Copying STM32 bin file"
cp $BUILDDIR/$basename.bin $OUTDIR/$basename.bin cp $BUILDDIR/$basename.bin $OUTDIR/$basename.bin
echo "Copying manifest" # 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 $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json

View File

@@ -184,8 +184,6 @@ Input:
Logging: Logging:
LogLevel: info # debug, info, warn, error LogLevel: info # debug, info, warn, error
# TraceFile: /var/log/meshtasticd.json # TraceFile: /var/log/meshtasticd.json
# JSONFile: /packets.json # File location for JSON output of decoded packets
# JSONFilter: position # filter for packets to save to JSON file
# AsciiLogs: true # default if not specified is !isatty() on stdout # AsciiLogs: true # default if not specified is !isatty() on stdout
Webserver: Webserver:

View File

@@ -159,22 +159,20 @@ def load_boot_logo(source, target, env):
# Load the boot logo on TFT builds # Load the boot logo on TFT builds
if ("HAS_TFT", 1) in env.get("CPPDEFINES", []): if ("HAS_TFT", 1) in env.get("CPPDEFINES", []):
env.AddPreAction(f"$BUILD_DIR/{lfsbin}", load_boot_logo) env.AddPreAction('$BUILD_DIR/littlefs.bin', load_boot_logo)
mtjson_deps = ["buildprog"] # Rename (mv) littlefs.bin to include the PROGNAME
if platform.name == "espressif32": # This ensures the littlefs.bin is named consistently with the firmware
# Build littlefs image as part of mtjson target env.AddPostAction('$BUILD_DIR/littlefs.bin', env.VerboseAction(
# Equivalent to `pio run -t buildfs` f'mv $BUILD_DIR/littlefs.bin $BUILD_DIR/{lfsbin}',
target_lfs = env.DataToBin( f'Renaming littlefs.bin to {lfsbin}'
join("$BUILD_DIR", "${ESP32_FS_IMAGE_NAME}"), "$PROJECT_DATA_DIR" ))
)
mtjson_deps.append(target_lfs)
env.AddCustomTarget( env.AddCustomTarget(
name="mtjson", name="mtjson",
dependencies=mtjson_deps, dependencies=None,
actions=[manifest_gather], actions=[manifest_gather],
title="Meshtastic Manifest", title="Meshtastic Manifest",
description="Generating Meshtastic manifest JSON + Checksums", description="Generating Meshtastic manifest JSON + Checksums",
always_build=False, always_build=True,
) )

View File

@@ -11,9 +11,6 @@ else:
prefsLoc = env["PROJECT_DIR"] + "/version.properties" prefsLoc = env["PROJECT_DIR"] + "/version.properties"
verObj = readProps(prefsLoc) verObj = readProps(prefsLoc)
env.Replace(PROGNAME=f"firmware-{env.get('PIOENV')}-{verObj['long']}") env.Replace(PROGNAME=f"firmware-{env.get('PIOENV')}-{verObj['long']}")
env.Replace(ESP32_FS_IMAGE_NAME=f"littlefs-{env.get('PIOENV')}-{verObj['long']}")
# Print the new program name for verification # Print the new program name for verification
print(f"PROGNAME: {env.get('PROGNAME')}") print(f"PROGNAME: {env.get('PROGNAME')}")
if platform.name == "espressif32":
print(f"ESP32_FS_IMAGE_NAME: {env.get('ESP32_FS_IMAGE_NAME')}")

View File

@@ -10,12 +10,6 @@ Import("env")
platform = env.PioPlatform() platform = env.PioPlatform()
sys.path.append(join(platform.get_package_dir("tool-esptoolpy"))) sys.path.append(join(platform.get_package_dir("tool-esptoolpy")))
# IntelHex workaround, remove after fixed upstream
# https://github.com/platformio/platform-espressif32/issues/1632
try:
import intelhex
except ImportError:
env.Execute("$PYTHONEXE -m pip install intelhex")
import esptool import esptool

View File

@@ -123,7 +123,7 @@ lib_deps =
[device-ui_base] [device-ui_base]
lib_deps = lib_deps =
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master # renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
https://github.com/meshtastic/device-ui/archive/862ed040c4ab44f0dfbbe492691f144886102588.zip https://github.com/meshtastic/device-ui/archive/4fb5f24787caa841b58dbf623a52c4c5861d6722.zip
; Common libs for environmental measurements in telemetry module ; Common libs for environmental measurements in telemetry module
[environmental_base] [environmental_base]

View File

@@ -31,9 +31,6 @@ const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaC
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST: case meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST:
return useShortName ? "LongF" : "LongFast"; return useShortName ? "LongF" : "LongFast";
break; break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO:
return useShortName ? "LongT" : "LongTurbo";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE: case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
return useShortName ? "LongM" : "LongMod"; return useShortName ? "LongM" : "LongMod";
break; break;

View File

@@ -10,7 +10,10 @@
*/ */
#include "FSCommon.h" #include "FSCommon.h"
#include "SPILock.h" #include "SPILock.h"
#include "SafeFile.h"
#include "configuration.h" #include "configuration.h"
#include <pb_decode.h>
#include <pb_encode.h>
// Software SPI is used by MUI so disable SD card here until it's also implemented // Software SPI is used by MUI so disable SD card here until it's also implemented
#if defined(HAS_SDCARD) && !defined(SDCARD_USE_SOFT_SPI) #if defined(HAS_SDCARD) && !defined(SDCARD_USE_SOFT_SPI)
@@ -335,4 +338,63 @@ void setupSDCard()
LOG_DEBUG("Total space: %lu MB", (uint32_t)(SD.totalBytes() / (1024 * 1024))); LOG_DEBUG("Total space: %lu MB", (uint32_t)(SD.totalBytes() / (1024 * 1024)));
LOG_DEBUG("Used space: %lu MB", (uint32_t)(SD.usedBytes() / (1024 * 1024))); LOG_DEBUG("Used space: %lu MB", (uint32_t)(SD.usedBytes() / (1024 * 1024)));
#endif #endif
}
/** Load a protobuf from a file, return LoadFileResult */
LoadFileResult loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, void *dest_struct)
{
LoadFileResult state = LoadFileResult::OTHER_FAILURE;
#ifdef FSCom
concurrency::LockGuard g(spiLock);
auto f = FSCom.open(filename, FILE_O_READ);
if (f) {
LOG_INFO("Load %s", filename);
pb_istream_t stream = {&readcb, &f, protoSize};
if (fields != &meshtastic_NodeDatabase_msg) // contains a vector object
memset(dest_struct, 0, objSize);
if (!pb_decode(&stream, fields, dest_struct)) {
LOG_ERROR("Error: can't decode protobuf %s", PB_GET_ERROR(&stream));
state = LoadFileResult::DECODE_FAILED;
} else {
LOG_INFO("Loaded %s successfully", filename);
state = LoadFileResult::LOAD_SUCCESS;
}
f.close();
} else {
LOG_ERROR("Could not open / read %s", filename);
}
#else
LOG_ERROR("ERROR: Filesystem not implemented");
state = LoadFileResult::NO_FILESYSTEM;
#endif
return state;
}
/** Save a protobuf from a file, return true for success */
bool saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct, bool fullAtomic)
{
bool okay = false;
#ifdef FSCom
auto f = SafeFile(filename, fullAtomic);
LOG_INFO("Save %s", filename);
pb_ostream_t stream = {&writecb, static_cast<Print *>(&f), protoSize};
if (!pb_encode(&stream, fields, dest_struct)) {
LOG_ERROR("Error: can't encode protobuf %s", PB_GET_ERROR(&stream));
} else {
okay = true;
}
bool writeSucceeded = f.close();
if (!okay || !writeSucceeded) {
LOG_ERROR("Can't write prefs!");
}
#else
LOG_ERROR("ERROR: Filesystem not implemented");
#endif
return okay;
} }

View File

@@ -3,6 +3,19 @@
#include "configuration.h" #include "configuration.h"
#include <vector> #include <vector>
enum LoadFileResult {
// Successfully opened the file
LOAD_SUCCESS = 1,
// File does not exist
NOT_FOUND = 2,
// Device does not have a filesystem
NO_FILESYSTEM = 3,
// File exists, but could not decode protobufs
DECODE_FAILED = 4,
// File exists, but open failed for some reason
OTHER_FAILURE = 5
};
// Cross platform filesystem API // Cross platform filesystem API
#if defined(ARCH_PORTDUINO) #if defined(ARCH_PORTDUINO)
@@ -55,4 +68,8 @@ bool renameFile(const char *pathFrom, const char *pathTo);
std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels); std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels);
void listDir(const char *dirname, uint8_t levels, bool del = false); void listDir(const char *dirname, uint8_t levels, bool del = false);
void rmDir(const char *dirname); void rmDir(const char *dirname);
void setupSDCard(); void setupSDCard();
LoadFileResult loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, void *dest_struct);
bool saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct,
bool fullAtomic = true);

View File

@@ -532,10 +532,8 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
const int labelX = x; const int labelX = x;
int barsOffset = (isHighResolution) ? 24 : 0; int barsOffset = (isHighResolution) ? 24 : 0;
#ifdef USE_EINK #ifdef USE_EINK
#ifndef T_DECK_PRO
barsOffset -= 12; barsOffset -= 12;
#endif #endif
#endif
#if defined(M5STACK_UNITC6L) #if defined(M5STACK_UNITC6L)
const int barX = x + 45 + barsOffset; const int barX = x + 45 + barsOffset;
#else #else
@@ -576,7 +574,7 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
#endif #endif
// Value string // Value string
display->setTextAlignment(TEXT_ALIGN_RIGHT); display->setTextAlignment(TEXT_ALIGN_RIGHT);
display->drawString(SCREEN_WIDTH, getTextPositions(display)[line], combinedStr); display->drawString(SCREEN_WIDTH - 2, getTextPositions(display)[line], combinedStr);
}; };
// === Memory values === // === Memory values ===

View File

@@ -1,6 +1,7 @@
#include "configuration.h" #include "configuration.h"
#if HAS_SCREEN #if HAS_SCREEN
#include "ClockRenderer.h" #include "ClockRenderer.h"
#include "FSCommon.h"
#include "GPS.h" #include "GPS.h"
#include "MenuHandler.h" #include "MenuHandler.h"
#include "MeshRadio.h" #include "MeshRadio.h"
@@ -20,41 +21,12 @@
#include "modules/KeyVerificationModule.h" #include "modules/KeyVerificationModule.h"
#include "modules/TraceRouteModule.h" #include "modules/TraceRouteModule.h"
#include <algorithm>
#include <array>
#include <functional> #include <functional>
#include <utility>
extern uint16_t TFT_MESH; extern uint16_t TFT_MESH;
namespace graphics namespace graphics
{ {
namespace
{
// Caller must ensure the provided options array outlives the banner callback.
template <typename T, size_t N, typename Callback>
BannerOverlayOptions createStaticBannerOptions(const char *message, const MenuOption<T> (&options)[N],
std::array<const char *, N> &labels, Callback &&onSelection)
{
for (size_t i = 0; i < N; ++i) {
labels[i] = options[i].label;
}
const MenuOption<T> *optionsPtr = options;
auto callback = std::function<void(const MenuOption<T> &, int)>(std::forward<Callback>(onSelection));
BannerOverlayOptions bannerOptions;
bannerOptions.message = message;
bannerOptions.optionsArrayPtr = labels.data();
bannerOptions.optionsCount = static_cast<uint8_t>(N);
bannerOptions.bannerCallback = [optionsPtr, callback](int selected) -> void { callback(optionsPtr[selected], selected); };
return bannerOptions;
}
} // namespace
menuHandler::screenMenus menuHandler::menuQueue = menu_none; menuHandler::screenMenus menuHandler::menuQueue = menu_none;
bool test_enabled = false; bool test_enabled = false;
uint8_t test_count = 0; uint8_t test_count = 0;
@@ -226,38 +198,48 @@ void menuHandler::DeviceRolePicker()
void menuHandler::RadioPresetPicker() void menuHandler::RadioPresetPicker()
{ {
static const RadioPresetOption presetOptions[] = { static const char *optionsArray[] = {"Back", "LongSlow", "LongModerate", "LongFast", "MediumSlow",
{"Back", OptionsAction::Back}, "MediumFast", "ShortSlow", "ShortFast", "ShortTurbo"};
{"LongTurbo", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO}, enum optionsNumbers {
{"LongModerate", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE}, Back = 0,
{"LongFast", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST}, radiopreset_LongSlow = 1,
{"MediumSlow", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW}, radiopreset_LongModerate = 2,
{"MediumFast", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST}, radiopreset_LongFast = 3,
{"ShortSlow", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW}, radiopreset_MediumSlow = 4,
{"ShortFast", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST}, radiopreset_MediumFast = 5,
{"ShortTurbo", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO}, radiopreset_ShortSlow = 6,
radiopreset_ShortFast = 7,
radiopreset_ShortTurbo = 8
};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Radio Preset";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 9;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Back) {
menuHandler::menuQueue = menuHandler::lora_Menu;
screen->runNow();
return;
} else if (selected == radiopreset_LongSlow) {
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW;
} else if (selected == radiopreset_LongModerate) {
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE;
} else if (selected == radiopreset_LongFast) {
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST;
} else if (selected == radiopreset_MediumSlow) {
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW;
} else if (selected == radiopreset_MediumFast) {
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST;
} else if (selected == radiopreset_ShortSlow) {
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW;
} else if (selected == radiopreset_ShortFast) {
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST;
} else if (selected == radiopreset_ShortTurbo) {
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO;
}
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
}; };
constexpr size_t presetCount = sizeof(presetOptions) / sizeof(presetOptions[0]);
static std::array<const char *, presetCount> presetLabels{};
auto bannerOptions =
createStaticBannerOptions("Radio Preset", presetOptions, presetLabels, [](const RadioPresetOption &option, int) -> void {
if (option.action == OptionsAction::Back) {
menuHandler::menuQueue = menuHandler::lora_Menu;
screen->runNow();
return;
}
if (!option.hasValue) {
return;
}
config.lora.modem_preset = option.value;
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
});
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
@@ -1808,7 +1790,7 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
void menuHandler::saveUIConfig() void menuHandler::saveUIConfig()
{ {
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig); saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig);
} }
} // namespace graphics } // namespace graphics

View File

@@ -99,24 +99,5 @@ class menuHandler
static void BluetoothToggleMenu(); static void BluetoothToggleMenu();
}; };
/* Generic Menu Options designations */
enum class OptionsAction { Back, Select };
template <typename T> struct MenuOption {
const char *label;
OptionsAction action;
bool hasValue;
T value;
MenuOption(const char *labelIn, OptionsAction actionIn, T valueIn)
: label(labelIn), action(actionIn), hasValue(true), value(valueIn)
{
}
MenuOption(const char *labelIn, OptionsAction actionIn) : label(labelIn), action(actionIn), hasValue(false), value() {}
};
using RadioPresetOption = MenuOption<meshtastic_Config_LoRaConfig_ModemPreset>;
} // namespace graphics } // namespace graphics
#endif #endif

View File

@@ -62,22 +62,12 @@ void InkHUD::HeardApplet::populateFromNodeDB()
{ {
// Fill a collection with pointers to each node in db // Fill a collection with pointers to each node in db
std::vector<meshtastic_NodeInfoLite *> ordered; std::vector<meshtastic_NodeInfoLite *> ordered;
for (auto mn = nodeDB->meshNodes->begin(); mn != nodeDB->meshNodes->end(); ++mn) { for (int i = 1; i < maxCards(); i++) {
// Only copy if valid, and not our own node auto mn = nodeDB->getMeshNodeByIndex(i);
if (mn->num != 0 && mn->num != nodeDB->getNodeNum()) if (mn->num != 0 && mn->num != nodeDB->getNodeNum())
ordered.push_back(&*mn); ordered.push_back(&*mn);
} }
// Sort the collection by age
std::sort(ordered.begin(), ordered.end(), [](meshtastic_NodeInfoLite *top, meshtastic_NodeInfoLite *bottom) -> bool {
return (top->last_heard > bottom->last_heard);
});
// Keep the most recent entries only
// Just enough to fill the screen
if (ordered.size() > maxCards())
ordered.resize(maxCards());
// Create card info for these (stale) node observations // Create card info for these (stale) node observations
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
for (meshtastic_NodeInfoLite *node : ordered) { for (meshtastic_NodeInfoLite *node : ordered) {

View File

@@ -53,9 +53,9 @@ void CannedMessageStore::load()
// Attempt to load the bulk canned message data from flash // Attempt to load the bulk canned message data from flash
meshtastic_CannedMessageModuleConfig cannedMessageModuleConfig; meshtastic_CannedMessageModuleConfig cannedMessageModuleConfig;
LoadFileResult result = nodeDB->loadProto("/prefs/cannedConf.proto", meshtastic_CannedMessageModuleConfig_size, LoadFileResult result = loadProto("/prefs/cannedConf.proto", meshtastic_CannedMessageModuleConfig_size,
sizeof(meshtastic_CannedMessageModuleConfig), sizeof(meshtastic_CannedMessageModuleConfig), &meshtastic_CannedMessageModuleConfig_msg,
&meshtastic_CannedMessageModuleConfig_msg, &cannedMessageModuleConfig); &cannedMessageModuleConfig);
// Abort if nothing to load // Abort if nothing to load
if (result != LoadFileResult::LOAD_SUCCESS || strlen(cannedMessageModuleConfig.messages) == 0) if (result != LoadFileResult::LOAD_SUCCESS || strlen(cannedMessageModuleConfig.messages) == 0)
@@ -129,8 +129,8 @@ void CannedMessageStore::handleSet(const meshtastic_AdminMessage *request)
#endif #endif
// Write to flash // Write to flash
nodeDB->saveProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size, saveProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size, &meshtastic_CannedMessageModuleConfig_msg,
&meshtastic_CannedMessageModuleConfig_msg, &cannedMessageModuleConfig); &cannedMessageModuleConfig);
// Reload from flash, to update the canned messages in RAM // Reload from flash, to update the canned messages in RAM
// (This is a lazy way to handle it) // (This is a lazy way to handle it)

View File

@@ -53,7 +53,6 @@ typedef struct _InputEvent {
class InputPollable class InputPollable
{ {
public: public:
virtual ~InputPollable() = default;
virtual void pollOnce() = 0; virtual void pollOnce() = 0;
}; };

View File

@@ -3,9 +3,6 @@
#include "RotaryEncoderImpl.h" #include "RotaryEncoderImpl.h"
#include "InputBroker.h" #include "InputBroker.h"
#include "RotaryEncoder.h" #include "RotaryEncoder.h"
#ifdef ARCH_ESP32
#include "sleep.h"
#endif
#define ORIGIN_NAME "RotaryEncoder" #define ORIGIN_NAME "RotaryEncoder"
@@ -14,20 +11,6 @@ RotaryEncoderImpl *rotaryEncoderImpl;
RotaryEncoderImpl::RotaryEncoderImpl() RotaryEncoderImpl::RotaryEncoderImpl()
{ {
rotary = nullptr; rotary = nullptr;
#ifdef ARCH_ESP32
isFirstInit = true;
#endif
}
RotaryEncoderImpl::~RotaryEncoderImpl()
{
LOG_DEBUG("RotaryEncoderImpl destructor");
detachRotaryEncoderInterrupts();
if (rotary != nullptr) {
delete rotary;
rotary = nullptr;
}
} }
bool RotaryEncoderImpl::init() bool RotaryEncoderImpl::init()
@@ -42,22 +25,15 @@ bool RotaryEncoderImpl::init()
eventCcw = static_cast<input_broker_event>(moduleConfig.canned_message.inputbroker_event_ccw); eventCcw = static_cast<input_broker_event>(moduleConfig.canned_message.inputbroker_event_ccw);
eventPressed = static_cast<input_broker_event>(moduleConfig.canned_message.inputbroker_event_press); eventPressed = static_cast<input_broker_event>(moduleConfig.canned_message.inputbroker_event_press);
if (rotary == nullptr) { rotary = new RotaryEncoder(moduleConfig.canned_message.inputbroker_pin_a, moduleConfig.canned_message.inputbroker_pin_b,
rotary = new RotaryEncoder(moduleConfig.canned_message.inputbroker_pin_a, moduleConfig.canned_message.inputbroker_pin_b, moduleConfig.canned_message.inputbroker_pin_press);
moduleConfig.canned_message.inputbroker_pin_press); rotary->resetButton();
}
attachRotaryEncoderInterrupts(); interruptInstance = this;
auto interruptHandler = []() { inputBroker->requestPollSoon(interruptInstance); };
#ifdef ARCH_ESP32 attachInterrupt(moduleConfig.canned_message.inputbroker_pin_a, interruptHandler, CHANGE);
// Register callbacks for before and after lightsleep attachInterrupt(moduleConfig.canned_message.inputbroker_pin_b, interruptHandler, CHANGE);
// Used to detach and reattach interrupts attachInterrupt(moduleConfig.canned_message.inputbroker_pin_press, interruptHandler, CHANGE);
if (isFirstInit) {
lsObserver.observe(&notifyLightSleep);
lsEndObserver.observe(&notifyLightSleepEnd);
isFirstInit = false;
}
#endif
LOG_INFO("RotaryEncoder initialized pins(%d, %d, %d), events(%d, %d, %d)", moduleConfig.canned_message.inputbroker_pin_a, LOG_INFO("RotaryEncoder initialized pins(%d, %d, %d), events(%d, %d, %d)", moduleConfig.canned_message.inputbroker_pin_a,
moduleConfig.canned_message.inputbroker_pin_b, moduleConfig.canned_message.inputbroker_pin_press, eventCw, eventCcw, moduleConfig.canned_message.inputbroker_pin_b, moduleConfig.canned_message.inputbroker_pin_press, eventCw, eventCcw,
@@ -95,50 +71,6 @@ void RotaryEncoderImpl::pollOnce()
} }
} }
void RotaryEncoderImpl::detachRotaryEncoderInterrupts()
{
LOG_DEBUG("RotaryEncoderImpl detach button interrupts");
if (interruptInstance == this) {
detachInterrupt(moduleConfig.canned_message.inputbroker_pin_a);
detachInterrupt(moduleConfig.canned_message.inputbroker_pin_b);
detachInterrupt(moduleConfig.canned_message.inputbroker_pin_press);
interruptInstance = nullptr;
} else {
LOG_WARN("RotaryEncoderImpl: interrupts already detached");
}
}
void RotaryEncoderImpl::attachRotaryEncoderInterrupts()
{
LOG_DEBUG("RotaryEncoderImpl attach button interrupts");
if (rotary != nullptr && interruptInstance == nullptr) {
rotary->resetButton();
interruptInstance = this;
auto interruptHandler = []() { inputBroker->requestPollSoon(interruptInstance); };
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_a, interruptHandler, CHANGE);
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_b, interruptHandler, CHANGE);
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_press, interruptHandler, CHANGE);
} else {
LOG_WARN("RotaryEncoderImpl: interrupts already attached");
}
}
#ifdef ARCH_ESP32
int RotaryEncoderImpl::beforeLightSleep(void *unused)
{
detachRotaryEncoderInterrupts();
return 0; // Indicates success;
}
int RotaryEncoderImpl::afterLightSleep(esp_sleep_wakeup_cause_t cause)
{
attachRotaryEncoderInterrupts();
return 0; // Indicates success;
}
#endif
RotaryEncoderImpl *RotaryEncoderImpl::interruptInstance; RotaryEncoderImpl *RotaryEncoderImpl::interruptInstance;
#endif #endif

View File

@@ -8,18 +8,12 @@
class RotaryEncoder; class RotaryEncoder;
class RotaryEncoderImpl final : public InputPollable class RotaryEncoderImpl : public InputPollable
{ {
public: public:
RotaryEncoderImpl(); RotaryEncoderImpl();
~RotaryEncoderImpl() override; bool init(void);
bool init();
virtual void pollOnce() override; virtual void pollOnce() override;
// Disconnect and reconnect interrupts for light sleep
#ifdef ARCH_ESP32
int beforeLightSleep(void *unused);
int afterLightSleep(esp_sleep_wakeup_cause_t cause);
#endif
protected: protected:
static RotaryEncoderImpl *interruptInstance; static RotaryEncoderImpl *interruptInstance;
@@ -29,21 +23,6 @@ class RotaryEncoderImpl final : public InputPollable
input_broker_event eventPressed = INPUT_BROKER_NONE; input_broker_event eventPressed = INPUT_BROKER_NONE;
RotaryEncoder *rotary; RotaryEncoder *rotary;
private:
#ifdef ARCH_ESP32
bool isFirstInit;
#endif
void detachRotaryEncoderInterrupts();
void attachRotaryEncoderInterrupts();
#ifdef ARCH_ESP32
// Get notified when lightsleep begins and ends
CallbackObserver<RotaryEncoderImpl, void *> lsObserver =
CallbackObserver<RotaryEncoderImpl, void *>(this, &RotaryEncoderImpl::beforeLightSleep);
CallbackObserver<RotaryEncoderImpl, esp_sleep_wakeup_cause_t> lsEndObserver =
CallbackObserver<RotaryEncoderImpl, esp_sleep_wakeup_cause_t>(this, &RotaryEncoderImpl::afterLightSleep);
#endif
}; };
extern RotaryEncoderImpl *rotaryEncoderImpl; extern RotaryEncoderImpl *rotaryEncoderImpl;

View File

@@ -225,4 +225,4 @@ class MeshModule
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet /** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
* This ensures that if the request packet was sent reliably, the reply is sent that way as well. * This ensures that if the request packet was sent reliably, the reply is sent that way as well.
*/ */
void setReplyTo(meshtastic_MeshPacket *p, const meshtastic_MeshPacket &to); void setReplyTo(meshtastic_MeshPacket *p, const meshtastic_MeshPacket &to);

View File

@@ -15,7 +15,6 @@
#include "RTC.h" #include "RTC.h"
#include "Router.h" #include "Router.h"
#include "SPILock.h" #include "SPILock.h"
#include "SafeFile.h"
#include "TypeConversions.h" #include "TypeConversions.h"
#include "error.h" #include "error.h"
#include "main.h" #include "main.h"
@@ -314,7 +313,7 @@ NodeDB::NodeDB()
LOG_DEBUG("Number of Device Reboots: %d", myNodeInfo.reboot_count); LOG_DEBUG("Number of Device Reboots: %d", myNodeInfo.reboot_count);
#endif #endif
resetRadioConfig(); // If bogus settings got saved, then fix them _resetRadioConfig(); // If bogus settings got saved, then fix them
// nodeDB->LOG_DEBUG("region=%d, NODENUM=0x%x, dbsize=%d", config.lora.region, myNodeInfo.my_node_num, numMeshNodes); // nodeDB->LOG_DEBUG("region=%d, NODENUM=0x%x, dbsize=%d", config.lora.region, myNodeInfo.my_node_num, numMeshNodes);
// Uncomment below to always enable UDP broadcasts // Uncomment below to always enable UDP broadcasts
@@ -425,13 +424,13 @@ NodeDB::NodeDB()
config.has_position = true; config.has_position = true;
info->has_position = true; info->has_position = true;
info->position = TypeConversions::ConvertToPositionLite(fixedGPS); info->position = TypeConversions::ConvertToPositionLite(fixedGPS);
nodeDB->setLocalPosition(fixedGPS); nodeDB->_setLocalPosition(fixedGPS);
config.position.fixed_position = true; config.position.fixed_position = true;
#endif #endif
} }
#endif #endif
sortMeshDB(); sortMeshDB();
saveToDisk(saveWhat); _saveToDisk(saveWhat);
} }
/** /**
@@ -460,7 +459,7 @@ bool isBroadcast(uint32_t dest)
return dest == NODENUM_BROADCAST || dest == NODENUM_BROADCAST_NO_LORA; return dest == NODENUM_BROADCAST || dest == NODENUM_BROADCAST_NO_LORA;
} }
void NodeDB::resetRadioConfig(bool is_fresh_install) void NodeDB::_resetRadioConfig(bool is_fresh_install)
{ {
if (is_fresh_install) { if (is_fresh_install) {
radioGeneration++; radioGeneration++;
@@ -480,6 +479,7 @@ void NodeDB::resetRadioConfig(bool is_fresh_install)
bool NodeDB::factoryReset(bool eraseBleBonds) bool NodeDB::factoryReset(bool eraseBleBonds)
{ {
FUNCTION_START("factoryReset");
LOG_INFO("Perform factory reset!"); LOG_INFO("Perform factory reset!");
// first, remove the "/prefs" (this removes most prefs) // first, remove the "/prefs" (this removes most prefs)
spiLock->lock(); spiLock->lock();
@@ -498,7 +498,7 @@ bool NodeDB::factoryReset(bool eraseBleBonds)
installDefaultModuleConfig(); installDefaultModuleConfig();
installDefaultChannels(); installDefaultChannels();
// third, write everything to disk // third, write everything to disk
saveToDisk(); _saveToDisk();
if (eraseBleBonds) { if (eraseBleBonds) {
LOG_INFO("Erase BLE bonds"); LOG_INFO("Erase BLE bonds");
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
@@ -513,6 +513,7 @@ bool NodeDB::factoryReset(bool eraseBleBonds)
Bluefruit.Central.clearBonds(); Bluefruit.Central.clearBonds();
#endif #endif
} }
FUNCTION_END;
return true; return true;
} }
@@ -649,7 +650,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
config.device.node_info_broadcast_secs = default_node_info_broadcast_secs; config.device.node_info_broadcast_secs = default_node_info_broadcast_secs;
config.security.serial_enabled = true; config.security.serial_enabled = true;
config.security.admin_channel_enabled = false; config.security.admin_channel_enabled = false;
resetRadioConfig(true); // This also triggers NodeInfo/Position requests since we're fresh _resetRadioConfig(true); // This also triggers NodeInfo/Position requests since we're fresh
strncpy(config.network.ntp_server, "meshtastic.pool.ntp.org", 32); strncpy(config.network.ntp_server, "meshtastic.pool.ntp.org", 32);
#if (defined(T_DECK) || defined(T_WATCH_S3) || defined(UNPHONE) || defined(PICOMPUTER_S3) || defined(SENSECAP_INDICATOR) || \ #if (defined(T_DECK) || defined(T_WATCH_S3) || defined(UNPHONE) || defined(PICOMPUTER_S3) || defined(SENSECAP_INDICATOR) || \
@@ -746,7 +747,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
#ifdef USERPREFS_CONFIG_DEVICE_ROLE #ifdef USERPREFS_CONFIG_DEVICE_ROLE
// Apply role-specific defaults when role is set via user preferences // Apply role-specific defaults when role is set via user preferences
installRoleDefaults(config.device.role); _installRoleDefaults(config.device.role);
#endif #endif
initConfigIntervals(); initConfigIntervals();
@@ -805,15 +806,11 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.external_notification.output_ms = 500; moduleConfig.external_notification.output_ms = 500;
moduleConfig.external_notification.nag_timeout = 2; moduleConfig.external_notification.nag_timeout = 2;
#endif #endif
#if defined(RAK4630) || defined(RAK11310) || defined(RAK3312) || defined(MUZI_BASE) || defined(ELECROW_ThinkNode_M3) #if defined(RAK4630) || defined(RAK11310) || defined(RAK3312)
// Default to PIN_LED2 for external notification output (LED color depends on device variant) // Default to RAK led pin 2 (blue)
moduleConfig.external_notification.enabled = true; moduleConfig.external_notification.enabled = true;
moduleConfig.external_notification.output = PIN_LED2; moduleConfig.external_notification.output = PIN_LED2;
#if defined(MUZI_BASE) || defined(ELECROW_ThinkNode_M3)
moduleConfig.external_notification.active = false;
#else
moduleConfig.external_notification.active = true; moduleConfig.external_notification.active = true;
#endif
moduleConfig.external_notification.alert_message = true; moduleConfig.external_notification.alert_message = true;
moduleConfig.external_notification.output_ms = 1000; moduleConfig.external_notification.output_ms = 1000;
moduleConfig.external_notification.nag_timeout = default_ringtone_nag_secs; moduleConfig.external_notification.nag_timeout = default_ringtone_nag_secs;
@@ -908,7 +905,7 @@ void NodeDB::installDefaultModuleConfig()
initModuleConfigIntervals(); initModuleConfigIntervals();
} }
void NodeDB::installRoleDefaults(meshtastic_Config_DeviceConfig_Role role) void NodeDB::_installRoleDefaults(meshtastic_Config_DeviceConfig_Role role)
{ {
if (role == meshtastic_Config_DeviceConfig_Role_ROUTER) { if (role == meshtastic_Config_DeviceConfig_Role_ROUTER) {
initConfigIntervals(); initConfigIntervals();
@@ -994,6 +991,7 @@ void NodeDB::installDefaultChannels()
void NodeDB::resetNodes(bool keepFavorites) void NodeDB::resetNodes(bool keepFavorites)
{ {
FUNCTION_START("resetNodes");
if (!config.position.fixed_position) if (!config.position.fixed_position)
clearLocalPosition(); clearLocalPosition();
numMeshNodes = 1; numMeshNodes = 1;
@@ -1017,10 +1015,12 @@ void NodeDB::resetNodes(bool keepFavorites)
saveDeviceStateToDisk(); saveDeviceStateToDisk();
if (neighborInfoModule && moduleConfig.neighbor_info.enabled) if (neighborInfoModule && moduleConfig.neighbor_info.enabled)
neighborInfoModule->resetNeighbors(); neighborInfoModule->resetNeighbors();
FUNCTION_END;
} }
void NodeDB::removeNodeByNum(NodeNum nodeNum) void NodeDB::removeNodeByNum(NodeNum nodeNum)
{ {
FUNCTION_START("removeNodeByNum");
int newPos = 0, removed = 0; int newPos = 0, removed = 0;
for (int i = 0; i < numMeshNodes; i++) { for (int i = 0; i < numMeshNodes; i++) {
if (meshNodes->at(i).num != nodeNum) if (meshNodes->at(i).num != nodeNum)
@@ -1033,16 +1033,17 @@ void NodeDB::removeNodeByNum(NodeNum nodeNum)
meshtastic_NodeInfoLite()); meshtastic_NodeInfoLite());
LOG_DEBUG("NodeDB::removeNodeByNum purged %d entries. Save changes", removed); LOG_DEBUG("NodeDB::removeNodeByNum purged %d entries. Save changes", removed);
saveNodeDatabaseToDisk(); saveNodeDatabaseToDisk();
FUNCTION_END;
} }
void NodeDB::clearLocalPosition() void NodeDB::_clearLocalPosition()
{ {
meshtastic_NodeInfoLite *node = getMeshNode(nodeDB->getNodeNum()); meshtastic_NodeInfoLite *node = _getMeshNode(nodeDB->getNodeNum());
node->position.latitude_i = 0; node->position.latitude_i = 0;
node->position.longitude_i = 0; node->position.longitude_i = 0;
node->position.altitude = 0; node->position.altitude = 0;
node->position.time = 0; node->position.time = 0;
setLocalPosition(meshtastic_Position_init_default); _setLocalPosition(meshtastic_Position_init_default);
} }
void NodeDB::cleanupMeshDB() void NodeDB::cleanupMeshDB()
@@ -1118,7 +1119,7 @@ void NodeDB::pickNewNodeNum()
} }
meshtastic_NodeInfoLite *found; meshtastic_NodeInfoLite *found;
while (((found = getMeshNode(nodeNum)) && memcmp(found->user.macaddr, ourMacAddr, sizeof(ourMacAddr)) != 0) || while (((found = _getMeshNode(nodeNum)) && memcmp(found->user.macaddr, ourMacAddr, sizeof(ourMacAddr)) != 0) ||
(nodeNum == NODENUM_BROADCAST || nodeNum < NUM_RESERVED)) { (nodeNum == NODENUM_BROADCAST || nodeNum < NUM_RESERVED)) {
NodeNum candidate = random(NUM_RESERVED, LONG_MAX); // try a new random choice NodeNum candidate = random(NUM_RESERVED, LONG_MAX); // try a new random choice
if (found) if (found)
@@ -1132,39 +1133,6 @@ void NodeDB::pickNewNodeNum()
myNodeInfo.my_node_num = nodeNum; myNodeInfo.my_node_num = nodeNum;
} }
/** Load a protobuf from a file, return LoadFileResult */
LoadFileResult NodeDB::loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields,
void *dest_struct)
{
LoadFileResult state = LoadFileResult::OTHER_FAILURE;
#ifdef FSCom
concurrency::LockGuard g(spiLock);
auto f = FSCom.open(filename, FILE_O_READ);
if (f) {
LOG_INFO("Load %s", filename);
pb_istream_t stream = {&readcb, &f, protoSize};
if (fields != &meshtastic_NodeDatabase_msg) // contains a vector object
memset(dest_struct, 0, objSize);
if (!pb_decode(&stream, fields, dest_struct)) {
LOG_ERROR("Error: can't decode protobuf %s", PB_GET_ERROR(&stream));
state = LoadFileResult::DECODE_FAILED;
} else {
LOG_INFO("Loaded %s successfully", filename);
state = LoadFileResult::LOAD_SUCCESS;
}
f.close();
} else {
LOG_ERROR("Could not open / read %s", filename);
}
#else
LOG_ERROR("ERROR: Filesystem not implemented");
state = LoadFileResult::NO_FILESYSTEM;
#endif
return state;
}
void NodeDB::loadFromDisk() void NodeDB::loadFromDisk()
{ {
// Mark the current device state as completely unusable, so that if we fail reading the entire file from // Mark the current device state as completely unusable, so that if we fail reading the entire file from
@@ -1264,7 +1232,7 @@ void NodeDB::loadFromDisk()
if (backupSecurity.private_key.size > 0) { if (backupSecurity.private_key.size > 0) {
LOG_DEBUG("Restoring backup of security config"); LOG_DEBUG("Restoring backup of security config");
config.security = backupSecurity; config.security = backupSecurity;
saveToDisk(SEGMENT_CONFIG); _saveToDisk(SEGMENT_CONFIG);
} }
// Make sure we load hard coded admin keys even when the configuration file has none. // Make sure we load hard coded admin keys even when the configuration file has none.
@@ -1315,7 +1283,7 @@ void NodeDB::loadFromDisk()
if (numAdminKeys > 0) { if (numAdminKeys > 0) {
LOG_INFO("Saving %d hard coded admin keys.", numAdminKeys); LOG_INFO("Saving %d hard coded admin keys.", numAdminKeys);
config.security.admin_key_count = numAdminKeys; config.security.admin_key_count = numAdminKeys;
saveToDisk(SEGMENT_CONFIG); _saveToDisk(SEGMENT_CONFIG);
} }
state = loadProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, sizeof(meshtastic_LocalModuleConfig), state = loadProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, sizeof(meshtastic_LocalModuleConfig),
@@ -1367,7 +1335,7 @@ void NodeDB::loadFromDisk()
if (moduleConfig.paxcounter.paxcounter_update_interval == 900) if (moduleConfig.paxcounter.paxcounter_update_interval == 900)
moduleConfig.paxcounter.paxcounter_update_interval = 0; moduleConfig.paxcounter.paxcounter_update_interval = 0;
saveToDisk(SEGMENT_MODULECONFIG); _saveToDisk(SEGMENT_MODULECONFIG);
} }
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
// set any config overrides // set any config overrides
@@ -1378,34 +1346,6 @@ void NodeDB::loadFromDisk()
#endif #endif
} }
/** Save a protobuf from a file, return true for success */
bool NodeDB::saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct,
bool fullAtomic)
{
bool okay = false;
#ifdef FSCom
auto f = SafeFile(filename, fullAtomic);
LOG_INFO("Save %s", filename);
pb_ostream_t stream = {&writecb, static_cast<Print *>(&f), protoSize};
if (!pb_encode(&stream, fields, dest_struct)) {
LOG_ERROR("Error: can't encode protobuf %s", PB_GET_ERROR(&stream));
} else {
okay = true;
}
bool writeSucceeded = f.close();
if (!okay || !writeSucceeded) {
LOG_ERROR("Can't write prefs!");
}
#else
LOG_ERROR("ERROR: Filesystem not implemented");
#endif
return okay;
}
bool NodeDB::saveChannelsToDisk() bool NodeDB::saveChannelsToDisk()
{ {
#ifdef FSCom #ifdef FSCom
@@ -1494,7 +1434,7 @@ bool NodeDB::saveToDiskNoRetry(int saveWhat)
return success; return success;
} }
bool NodeDB::saveToDisk(int saveWhat) bool NodeDB::_saveToDisk(int saveWhat)
{ {
LOG_DEBUG("Save to disk %d", saveWhat); LOG_DEBUG("Save to disk %d", saveWhat);
bool success = saveToDiskNoRetry(saveWhat); bool success = saveToDiskNoRetry(saveWhat);
@@ -1518,10 +1458,12 @@ bool NodeDB::saveToDisk(int saveWhat)
const meshtastic_NodeInfoLite *NodeDB::readNextMeshNode(uint32_t &readIndex) const meshtastic_NodeInfoLite *NodeDB::readNextMeshNode(uint32_t &readIndex)
{ {
FUNCTION_START("readNextMeshNode");
meshtastic_NodeInfoLite *retVal = nullptr;
if (readIndex < numMeshNodes) if (readIndex < numMeshNodes)
return &meshNodes->at(readIndex++); retVal = &meshNodes->at(readIndex++);
else FUNCTION_END;
return NULL; return retVal;
} }
/// Given a node, return how many seconds in the past (vs now) that we last heard from it /// Given a node, return how many seconds in the past (vs now) that we last heard from it
@@ -1549,7 +1491,7 @@ uint32_t sinceReceived(const meshtastic_MeshPacket *p)
#define NUM_ONLINE_SECS (60 * 60 * 2) // 2 hrs to consider someone offline #define NUM_ONLINE_SECS (60 * 60 * 2) // 2 hrs to consider someone offline
size_t NodeDB::getNumOnlineMeshNodes(bool localOnly) size_t NodeDB::_getNumOnlineMeshNodes(bool localOnly)
{ {
size_t numseen = 0; size_t numseen = 0;
@@ -1571,8 +1513,10 @@ size_t NodeDB::getNumOnlineMeshNodes(bool localOnly)
*/ */
void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSource src) void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSource src)
{ {
FUNCTION_START("updatePosition");
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId); meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId);
if (!info) { if (!info) {
FUNCTION_END;
return; return;
} }
@@ -1581,7 +1525,7 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
LOG_INFO("updatePosition LOCAL pos@%x time=%u lat=%d lon=%d alt=%d", p.timestamp, p.time, p.latitude_i, p.longitude_i, LOG_INFO("updatePosition LOCAL pos@%x time=%u lat=%d lon=%d alt=%d", p.timestamp, p.time, p.latitude_i, p.longitude_i,
p.altitude); p.altitude);
setLocalPosition(p); _setLocalPosition(p);
info->position = TypeConversions::ConvertToPositionLite(p); info->position = TypeConversions::ConvertToPositionLite(p);
} else if ((p.time > 0) && !p.latitude_i && !p.longitude_i && !p.timestamp && !p.location_source) { } else if ((p.time > 0) && !p.latitude_i && !p.longitude_i && !p.timestamp && !p.location_source) {
// FIXME SPECIAL TIME SETTING PACKET FROM EUD TO RADIO // FIXME SPECIAL TIME SETTING PACKET FROM EUD TO RADIO
@@ -1608,7 +1552,8 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
} }
info->has_position = true; info->has_position = true;
updateGUIforNode = info; updateGUIforNode = info;
notifyObservers(true); // Force an update whether or not our node counts have changed _notifyObservers(true); // Force an update whether or not our node counts have changed
FUNCTION_END;
} }
/** Update telemetry info for this node based on received metrics /** Update telemetry info for this node based on received metrics
@@ -1616,9 +1561,11 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
*/ */
void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxSource src) void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxSource src)
{ {
FUNCTION_START("updatePosition");
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId); meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId);
// Environment metrics should never go to NodeDb but we'll safegaurd anyway // Environment metrics should never go to NodeDb but we'll safegaurd anyway
if (!info || t.which_variant != meshtastic_Telemetry_device_metrics_tag) { if (!info || t.which_variant != meshtastic_Telemetry_device_metrics_tag) {
FUNCTION_END;
return; return;
} }
@@ -1631,7 +1578,8 @@ void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxS
info->device_metrics = t.variant.device_metrics; info->device_metrics = t.variant.device_metrics;
info->has_device_metrics = true; info->has_device_metrics = true;
updateGUIforNode = info; updateGUIforNode = info;
notifyObservers(true); // Force an update whether or not our node counts have changed _notifyObservers(true); // Force an update whether or not our node counts have changed
FUNCTION_END;
} }
/** /**
@@ -1639,8 +1587,10 @@ void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxS
*/ */
void NodeDB::addFromContact(meshtastic_SharedContact contact) void NodeDB::addFromContact(meshtastic_SharedContact contact)
{ {
FUNCTION_START("addFromContact");
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(contact.node_num); meshtastic_NodeInfoLite *info = getOrCreateMeshNode(contact.node_num);
if (!info || !contact.has_user) { if (!info || !contact.has_user) {
FUNCTION_END;
return; return;
} }
// If the local node has this node marked as manually verified // If the local node has this node marked as manually verified
@@ -1649,6 +1599,7 @@ void NodeDB::addFromContact(meshtastic_SharedContact contact)
if ((info->bitfield & NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK) && !contact.manually_verified) { if ((info->bitfield & NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK) && !contact.manually_verified) {
if (contact.user.public_key.size != info->user.public_key.size || if (contact.user.public_key.size != info->user.public_key.size ||
memcmp(contact.user.public_key.bytes, info->user.public_key.bytes, info->user.public_key.size) != 0) { memcmp(contact.user.public_key.bytes, info->user.public_key.bytes, info->user.public_key.size) != 0) {
FUNCTION_END;
return; return;
} }
} }
@@ -1692,22 +1643,26 @@ void NodeDB::addFromContact(meshtastic_SharedContact contact)
// Mark the node's key as manually verified to indicate trustworthiness. // Mark the node's key as manually verified to indicate trustworthiness.
updateGUIforNode = info; updateGUIforNode = info;
sortMeshDB(); sortMeshDB();
notifyObservers(true); // Force an update whether or not our node counts have changed _notifyObservers(true); // Force an update whether or not our node counts have changed
} }
saveNodeDatabaseToDisk(); saveNodeDatabaseToDisk();
FUNCTION_END;
} }
/** Update user info and channel for this node based on received user data /** Update user info and channel for this node based on received user data
*/ */
bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex) bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex)
{ {
FUNCTION_START("updateUser");
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId); meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId);
if (!info) { if (!info) {
FUNCTION_END;
return false; return false;
} }
#if !(MESHTASTIC_EXCLUDE_PKI) #if !(MESHTASTIC_EXCLUDE_PKI)
if (p.public_key.size == 32 && nodeId != nodeDB->getNodeNum()) { if (p.public_key.size == 32 && nodeId != getNodeNum()) {
printBytes("Incoming Pubkey: ", p.public_key.bytes, 32); printBytes("Incoming Pubkey: ", p.public_key.bytes, 32);
// Alert the user if a remote node is advertising public key that matches our own // Alert the user if a remote node is advertising public key that matches our own
@@ -1724,6 +1679,7 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde
sprintf(cn->message, warning, p.long_name); sprintf(cn->message, warning, p.long_name);
service->sendClientNotification(cn); service->sendClientNotification(cn);
} }
FUNCTION_END;
return false; return false;
} }
} }
@@ -1731,6 +1687,7 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde
// if the key doesn't match, don't update nodeDB at all. // if the key doesn't match, don't update nodeDB at all.
if (p.public_key.size != 32 || (memcmp(p.public_key.bytes, info->user.public_key.bytes, 32) != 0)) { if (p.public_key.size != 32 || (memcmp(p.public_key.bytes, info->user.public_key.bytes, 32) != 0)) {
LOG_WARN("Public Key mismatch, dropping NodeInfo"); LOG_WARN("Public Key mismatch, dropping NodeInfo");
FUNCTION_END;
return false; return false;
} }
LOG_INFO("Public Key set for node, not updating!"); LOG_INFO("Public Key set for node, not updating!");
@@ -1758,19 +1715,19 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde
if (changed) { if (changed) {
updateGUIforNode = info; updateGUIforNode = info;
notifyObservers(true); // Force an update whether or not our node counts have changed _notifyObservers(true); // Force an update whether or not our node counts have changed
// We just changed something about a User, // We just changed something about a User,
// store our DB unless we just did so less than a minute ago // store our DB unless we just did so less than a minute ago
if (!Throttle::isWithinTimespanMs(lastNodeDbSave, ONE_MINUTE_MS)) { if (!Throttle::isWithinTimespanMs(lastNodeDbSave, ONE_MINUTE_MS)) {
saveToDisk(SEGMENT_NODEDATABASE); _saveToDisk(SEGMENT_NODEDATABASE);
lastNodeDbSave = millis(); lastNodeDbSave = millis();
} else { } else {
LOG_DEBUG("Defer NodeDB saveToDisk for now"); LOG_DEBUG("Defer NodeDB saveToDisk for now");
} }
} }
FUNCTION_END;
return changed; return changed;
} }
@@ -1778,107 +1735,121 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde
/// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw /// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw
void NodeDB::updateFrom(const meshtastic_MeshPacket &mp) void NodeDB::updateFrom(const meshtastic_MeshPacket &mp)
{ {
FUNCTION_START("updateFrom");
if (mp.from == getNodeNum()) { if (mp.from == getNodeNum()) {
LOG_DEBUG("Ignore update from self"); LOG_DEBUG("Ignore update from self");
return; } else if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.from) {
}
if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.from) {
LOG_DEBUG("Update DB node 0x%x, rx_time=%u", mp.from, mp.rx_time); LOG_DEBUG("Update DB node 0x%x, rx_time=%u", mp.from, mp.rx_time);
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getFrom(&mp)); meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getFrom(&mp));
if (!info) { if (info) {
return; if (mp.rx_time) // if the packet has a valid timestamp use it to update our last_heard
info->last_heard = mp.rx_time;
if (mp.rx_snr)
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
info->via_mqtt = mp.via_mqtt; // Store if we received this packet via MQTT
// If hopStart was set and there wasn't someone messing with the limit in the middle, add hopsAway
if (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start) {
info->has_hops_away = true;
info->hops_away = mp.hop_start - mp.hop_limit;
}
sortMeshDB();
} }
if (mp.rx_time) // if the packet has a valid timestamp use it to update our last_heard
info->last_heard = mp.rx_time;
if (mp.rx_snr)
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
info->via_mqtt = mp.via_mqtt; // Store if we received this packet via MQTT
// If hopStart was set and there wasn't someone messing with the limit in the middle, add hopsAway
if (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start) {
info->has_hops_away = true;
info->hops_away = mp.hop_start - mp.hop_limit;
}
sortMeshDB();
} }
FUNCTION_END;
} }
void NodeDB::set_favorite(bool is_favorite, uint32_t nodeId) void NodeDB::set_favorite(bool is_favorite, uint32_t nodeId)
{ {
meshtastic_NodeInfoLite *lite = getMeshNode(nodeId); FUNCTION_START("set_favorite");
meshtastic_NodeInfoLite *lite = _getMeshNode(nodeId);
if (lite && lite->is_favorite != is_favorite) { if (lite && lite->is_favorite != is_favorite) {
lite->is_favorite = is_favorite; lite->is_favorite = is_favorite;
sortMeshDB(); sortMeshDB();
saveNodeDatabaseToDisk(); saveNodeDatabaseToDisk();
} }
FUNCTION_END;
} }
// returns true if nodeId is_favorite; false if not or not found
bool NodeDB::isFavorite(uint32_t nodeId) bool NodeDB::isFavorite(uint32_t nodeId)
{ {
// returns true if nodeId is_favorite; false if not or not found FUNCTION_START("set_favorite");
// NODENUM_BROADCAST will never be in the DB // NODENUM_BROADCAST will never be in the DB
if (nodeId == NODENUM_BROADCAST) if (nodeId == NODENUM_BROADCAST) {
FUNCTION_END;
return false; return false;
}
meshtastic_NodeInfoLite *lite = getMeshNode(nodeId); meshtastic_NodeInfoLite *lite = _getMeshNode(nodeId);
if (lite) { if (lite) {
FUNCTION_END;
return lite->is_favorite; return lite->is_favorite;
} }
FUNCTION_END;
return false; return false;
} }
bool NodeDB::isFromOrToFavoritedNode(const meshtastic_MeshPacket &p) bool NodeDB::isFromOrToFavoritedNode(const meshtastic_MeshPacket &p)
{ {
FUNCTION_START("isFromOrToFavoritedNode");
// This method is logically equivalent to: // This method is logically equivalent to:
// return isFavorite(p.from) || isFavorite(p.to); // return isFavorite(p.from) || isFavorite(p.to);
// but is more efficient by: // but is more efficient by:
// 1. doing only one pass through the database, instead of two // 1. doing only one pass through the database, instead of two
// 2. exiting early when a favorite is found, or if both from and to have been seen // 2. exiting early when a favorite is found, or if both from and to have been seen
if (p.to == NODENUM_BROADCAST)
return isFavorite(p.from); // we never store NODENUM_BROADCAST in the DB, so we only need to check p.from
meshtastic_NodeInfoLite *lite = NULL; meshtastic_NodeInfoLite *lite = NULL;
bool seenFrom = false; bool seenFrom = false;
bool seenTo = false; bool seenTo = false;
if (p.to == NODENUM_BROADCAST)
seenTo = true;
for (int i = 0; i < numMeshNodes; i++) { for (int i = 0; i < numMeshNodes; i++) {
lite = &meshNodes->at(i); lite = &meshNodes->at(i);
if (lite->num == p.from) { if (!seenFrom && lite->num == p.from) {
if (lite->is_favorite) if (lite->is_favorite) {
FUNCTION_END;
return true; return true;
}
seenFrom = true; seenFrom = true;
} }
if (lite->num == p.to) { if (!seenTo && lite->num == p.to) {
if (lite->is_favorite) if (lite->is_favorite) {
FUNCTION_END;
return true; return true;
}
seenTo = true; seenTo = true;
} }
if (seenFrom && seenTo) if (seenFrom && seenTo) {
FUNCTION_END;
return false; // we've seen both, and neither is a favorite, so we can stop searching early return false; // we've seen both, and neither is a favorite, so we can stop searching early
}
// Note: if we knew that sortMeshDB was always called after any change to is_favorite, we could exit early after searching // Note: if we knew that sortMeshDB was always called after any change to is_favorite, we could exit early after searching
// all favorited nodes first. // all favorited nodes first.
} }
FUNCTION_END;
return false; return false;
} }
void NodeDB::pause_sort(bool paused) void NodeDB::pause_sort(bool paused)
{ {
// Including the mutex macro for completeness, but it's possible it isn't appropriate here
FUNCTION_START("pause_sort");
sortingIsPaused = paused; sortingIsPaused = paused;
FUNCTION_END;
} }
void NodeDB::sortMeshDB() void NodeDB::sortMeshDB()
@@ -1913,10 +1884,13 @@ void NodeDB::sortMeshDB()
uint8_t NodeDB::getMeshNodeChannel(NodeNum n) uint8_t NodeDB::getMeshNodeChannel(NodeNum n)
{ {
const meshtastic_NodeInfoLite *info = getMeshNode(n); FUNCTION_START("getMeshNodeChannel");
const meshtastic_NodeInfoLite *info = _getMeshNode(n);
if (!info) { if (!info) {
FUNCTION_END;
return 0; // defaults to PRIMARY return 0; // defaults to PRIMARY
} }
FUNCTION_END;
return info->channel; return info->channel;
} }
@@ -1929,7 +1903,7 @@ std::string NodeDB::getNodeId() const
/// Find a node in our DB, return null for missing /// Find a node in our DB, return null for missing
/// NOTE: This function might be called from an ISR /// NOTE: This function might be called from an ISR
meshtastic_NodeInfoLite *NodeDB::getMeshNode(NodeNum n) meshtastic_NodeInfoLite *NodeDB::_getMeshNode(NodeNum n)
{ {
for (int i = 0; i < numMeshNodes; i++) for (int i = 0; i < numMeshNodes; i++)
if (meshNodes->at(i).num == n) if (meshNodes->at(i).num == n)
@@ -1939,7 +1913,7 @@ meshtastic_NodeInfoLite *NodeDB::getMeshNode(NodeNum n)
} }
// returns true if the maximum number of nodes is reached or we are running low on memory // returns true if the maximum number of nodes is reached or we are running low on memory
bool NodeDB::isFull() bool NodeDB::_isFull()
{ {
return (numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < MINIMUM_SAFE_FREE_HEAP); return (numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < MINIMUM_SAFE_FREE_HEAP);
} }
@@ -1947,7 +1921,7 @@ bool NodeDB::isFull()
/// Find a node in our DB, create an empty NodeInfo if missing /// Find a node in our DB, create an empty NodeInfo if missing
meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n) meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
{ {
meshtastic_NodeInfoLite *lite = getMeshNode(n); meshtastic_NodeInfoLite *lite = _getMeshNode(n);
if (!lite) { if (!lite) {
if (isFull()) { if (isFull()) {
@@ -2002,18 +1976,25 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
/// valid lat/lon /// valid lat/lon
bool NodeDB::hasValidPosition(const meshtastic_NodeInfoLite *n) bool NodeDB::hasValidPosition(const meshtastic_NodeInfoLite *n)
{ {
return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0); FUNCTION_START("hasValidPosition");
auto retVal = n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0);
FUNCTION_END;
return retVal;
} }
/// If we have a node / user and they report is_licensed = true /// If we have a node / user and they report is_licensed = true
/// we consider them licensed /// we consider them licensed
UserLicenseStatus NodeDB::getLicenseStatus(uint32_t nodeNum) UserLicenseStatus NodeDB::getLicenseStatus(uint32_t nodeNum)
{ {
meshtastic_NodeInfoLite *info = getMeshNode(nodeNum); FUNCTION_START("getLicenseStatus");
meshtastic_NodeInfoLite *info = _getMeshNode(nodeNum);
if (!info || !info->has_user) { if (!info || !info->has_user) {
FUNCTION_END;
return UserLicenseStatus::NotKnown; return UserLicenseStatus::NotKnown;
} }
return info->user.is_licensed ? UserLicenseStatus::Licensed : UserLicenseStatus::NotLicensed; auto retVal = info->user.is_licensed ? UserLicenseStatus::Licensed : UserLicenseStatus::NotLicensed;
FUNCTION_END;
return retVal;
} }
#if !defined(MESHTASTIC_EXCLUDE_PKI) #if !defined(MESHTASTIC_EXCLUDE_PKI)
@@ -2035,6 +2016,7 @@ bool NodeDB::checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_pub
bool NodeDB::backupPreferences(meshtastic_AdminMessage_BackupLocation location) bool NodeDB::backupPreferences(meshtastic_AdminMessage_BackupLocation location)
{ {
FUNCTION_START("backupPreferences");
bool success = false; bool success = false;
lastBackupAttempt = millis(); lastBackupAttempt = millis();
#ifdef FSCom #ifdef FSCom
@@ -2068,11 +2050,13 @@ bool NodeDB::backupPreferences(meshtastic_AdminMessage_BackupLocation location)
// TODO: After more mainline SD card support // TODO: After more mainline SD card support
} }
#endif #endif
FUNCTION_END;
return success; return success;
} }
bool NodeDB::restorePreferences(meshtastic_AdminMessage_BackupLocation location, int restoreWhat) bool NodeDB::restorePreferences(meshtastic_AdminMessage_BackupLocation location, int restoreWhat)
{ {
FUNCTION_START("backupPreferences");
bool success = false; bool success = false;
#ifdef FSCom #ifdef FSCom
if (location == meshtastic_AdminMessage_BackupLocation_FLASH) { if (location == meshtastic_AdminMessage_BackupLocation_FLASH) {
@@ -2080,6 +2064,7 @@ bool NodeDB::restorePreferences(meshtastic_AdminMessage_BackupLocation location,
if (!FSCom.exists(backupFileName)) { if (!FSCom.exists(backupFileName)) {
spiLock->unlock(); spiLock->unlock();
LOG_WARN("Could not restore. No backup file found"); LOG_WARN("Could not restore. No backup file found");
FUNCTION_END;
return false; return false;
} else { } else {
spiLock->unlock(); spiLock->unlock();
@@ -2105,7 +2090,7 @@ bool NodeDB::restorePreferences(meshtastic_AdminMessage_BackupLocation location,
LOG_DEBUG("Restored channels"); LOG_DEBUG("Restored channels");
} }
success = saveToDisk(restoreWhat); success = _saveToDisk(restoreWhat);
if (success) { if (success) {
LOG_INFO("Restored preferences from backup"); LOG_INFO("Restored preferences from backup");
} else { } else {
@@ -2117,6 +2102,7 @@ bool NodeDB::restorePreferences(meshtastic_AdminMessage_BackupLocation location,
} else if (location == meshtastic_AdminMessage_BackupLocation_SD) { } else if (location == meshtastic_AdminMessage_BackupLocation_SD) {
// TODO: After more mainline SD card support // TODO: After more mainline SD card support
} }
FUNCTION_END;
return success; return success;
#endif #endif
} }

View File

@@ -18,6 +18,13 @@
#include "PortduinoGlue.h" #include "PortduinoGlue.h"
#endif #endif
#define FUNCTION_START(FUNCTION_NAME) \
if (fakeMutex) \
LOG_ERROR("Concurrency violation in " FUNCTION_NAME); \
fakeMutex = true;
#define FUNCTION_END fakeMutex = false;
#if !defined(MESHTASTIC_EXCLUDE_PKI) #if !defined(MESHTASTIC_EXCLUDE_PKI)
// E3B0C442 is the blank hash // E3B0C442 is the blank hash
static const uint8_t LOW_ENTROPY_HASHES[][32] = { static const uint8_t LOW_ENTROPY_HASHES[][32] = {
@@ -110,19 +117,6 @@ uint32_t sinceLastSeen(const meshtastic_NodeInfoLite *n);
/// Given a packet, return how many seconds in the past (vs now) it was received /// Given a packet, return how many seconds in the past (vs now) it was received
uint32_t sinceReceived(const meshtastic_MeshPacket *p); uint32_t sinceReceived(const meshtastic_MeshPacket *p);
enum LoadFileResult {
// Successfully opened the file
LOAD_SUCCESS = 1,
// File does not exist
NOT_FOUND = 2,
// Device does not have a filesystem
NO_FILESYSTEM = 3,
// File exists, but could not decode protobufs
DECODE_FAILED = 4,
// File exists, but open failed for some reason
OTHER_FAILURE = 5
};
enum UserLicenseStatus { NotKnown, NotLicensed, Licensed }; enum UserLicenseStatus { NotKnown, NotLicensed, Licensed };
class NodeDB class NodeDB
@@ -135,7 +129,6 @@ class NodeDB
// Note: these two references just point into our static array we serialize to/from disk // Note: these two references just point into our static array we serialize to/from disk
public: public:
std::vector<meshtastic_NodeInfoLite> *meshNodes;
bool updateGUI = false; // we think the gui should definitely be redrawn, screen will clear this once handled bool updateGUI = false; // we think the gui should definitely be redrawn, screen will clear this once handled
meshtastic_NodeInfoLite *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI meshtastic_NodeInfoLite *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI
Observable<const meshtastic::NodeStatus *> newStatus; Observable<const meshtastic::NodeStatus *> newStatus;
@@ -151,17 +144,26 @@ class NodeDB
/// write to flash /// write to flash
/// @return true if the save was successful /// @return true if the save was successful
bool saveToDisk(int saveWhat = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS | bool saveToDisk(int saveWhat = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS |
SEGMENT_NODEDATABASE); SEGMENT_NODEDATABASE)
{
FUNCTION_START("saveToDisk");
auto retVal = _saveToDisk(saveWhat);
FUNCTION_END;
return retVal;
}
/** Reinit radio config if needed, because either: /** Reinit radio config if needed, because either:
* a) sometimes a buggy android app might send us bogus settings or * a) sometimes a buggy android app might send us bogus settings or
* b) the client set factory_reset * b) the client set factory_reset
* *
* @param factory_reset if true, reset all settings to factory defaults
* @param is_fresh_install set to true after a fresh install, to trigger NodeInfo/Position requests * @param is_fresh_install set to true after a fresh install, to trigger NodeInfo/Position requests
* @return true if the config was completely reset, in that case, we should send it back to the client
*/ */
void resetRadioConfig(bool is_fresh_install = false); void resetRadioConfig(bool is_fresh_install = false)
{
FUNCTION_START("resetRadioConfig");
_resetRadioConfig(is_fresh_install);
FUNCTION_END;
}
/// given a subpacket sniffed from the network, update our DB state /// given a subpacket sniffed from the network, update our DB state
/// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw /// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw
@@ -208,7 +210,13 @@ class NodeDB
std::string getNodeId() const; std::string getNodeId() const;
// @return last byte of a NodeNum, 0xFF if it ended at 0x00 // @return last byte of a NodeNum, 0xFF if it ended at 0x00
uint8_t getLastByteOfNodeNum(NodeNum num) { return (uint8_t)((num & 0xFF) ? (num & 0xFF) : 0xFF); } uint8_t getLastByteOfNodeNum(NodeNum num)
{
FUNCTION_START("getLastByteOfNodeNum");
auto retVal = (uint8_t)((num & 0xFF) ? (num & 0xFF) : 0xFF);
FUNCTION_END;
return retVal;
}
/// if returns false, that means our node should send a DenyNodeNum response. If true, we think the number is okay for use /// if returns false, that means our node should send a DenyNodeNum response. If true, we think the number is okay for use
// bool handleWantNodeNum(NodeNum n); // bool handleWantNodeNum(NodeNum n);
@@ -227,79 +235,104 @@ class NodeDB
/* Return the number of nodes we've heard from recently (within the last 2 hrs?) /* Return the number of nodes we've heard from recently (within the last 2 hrs?)
* @param localOnly if true, ignore nodes heard via MQTT * @param localOnly if true, ignore nodes heard via MQTT
*/ */
size_t getNumOnlineMeshNodes(bool localOnly = false); size_t getNumOnlineMeshNodes(bool localOnly = false)
{
FUNCTION_START("getNumOnlineMeshNodes");
auto retVal = _getNumOnlineMeshNodes(localOnly);
FUNCTION_END;
return retVal;
}
void initConfigIntervals(), initModuleConfigIntervals(), resetNodes(bool keepFavorites = false), void resetNodes(bool keepFavorites = false), removeNodeByNum(NodeNum nodeNum);
removeNodeByNum(NodeNum nodeNum);
bool factoryReset(bool eraseBleBonds = false); bool factoryReset(bool eraseBleBonds = false);
LoadFileResult loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, void installRoleDefaults(meshtastic_Config_DeviceConfig_Role role)
void *dest_struct); {
bool saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct, FUNCTION_START("installRoleDefaults");
bool fullAtomic = true); _installRoleDefaults(role);
FUNCTION_END;
void installRoleDefaults(meshtastic_Config_DeviceConfig_Role role); }
const meshtastic_NodeInfoLite *readNextMeshNode(uint32_t &readIndex); const meshtastic_NodeInfoLite *readNextMeshNode(uint32_t &readIndex);
meshtastic_NodeInfoLite *getMeshNodeByIndex(size_t x) meshtastic_NodeInfoLite *getMeshNodeByIndex(size_t x)
{ {
assert(x < numMeshNodes); FUNCTION_START("getMeshNodeByIndex");
return &meshNodes->at(x); meshtastic_NodeInfoLite *retValue = nullptr;
if (x < numMeshNodes)
retValue = &meshNodes->at(x);
FUNCTION_END;
return retValue;
} }
virtual meshtastic_NodeInfoLite *getMeshNode(NodeNum n); virtual meshtastic_NodeInfoLite *getMeshNode(NodeNum n)
size_t getNumMeshNodes() { return numMeshNodes; } {
FUNCTION_START("getMeshNode");
auto retVal = _getMeshNode(n);
FUNCTION_END;
return retVal;
}
size_t getNumMeshNodes()
{
FUNCTION_START("getNumMeshNodes");
auto retVal = numMeshNodes;
FUNCTION_END;
return retVal;
}
UserLicenseStatus getLicenseStatus(uint32_t nodeNum); UserLicenseStatus getLicenseStatus(uint32_t nodeNum);
size_t getMaxNodesAllocatedSize() // returns true if the maximum number of nodes is reached or we are running low on memory
bool isFull()
{ {
meshtastic_NodeDatabase emptyNodeDatabase; FUNCTION_START("isFull");
emptyNodeDatabase.version = DEVICESTATE_CUR_VER; auto retVal = _isFull();
size_t nodeDatabaseSize; FUNCTION_END;
pb_get_encoded_size(&nodeDatabaseSize, meshtastic_NodeDatabase_fields, &emptyNodeDatabase); return retVal;
return nodeDatabaseSize + (MAX_NUM_NODES * meshtastic_NodeInfoLite_size);
} }
// returns true if the maximum number of nodes is reached or we are running low on memory void clearLocalPosition()
bool isFull(); {
FUNCTION_START("clearLocalPosition");
void clearLocalPosition(); _clearLocalPosition();
FUNCTION_END;
}
void setLocalPosition(meshtastic_Position position, bool timeOnly = false) void setLocalPosition(meshtastic_Position position, bool timeOnly = false)
{ {
if (timeOnly) { FUNCTION_START("setLocalPosition");
LOG_DEBUG("Set local position time only: time=%u timestamp=%u", position.time, position.timestamp); _setLocalPosition(position, timeOnly);
localPosition.time = position.time; FUNCTION_END;
localPosition.timestamp = position.timestamp > 0 ? position.timestamp : position.time;
return;
}
LOG_DEBUG("Set local position: lat=%i lon=%i time=%u timestamp=%u", position.latitude_i, position.longitude_i,
position.time, position.timestamp);
localPosition = position;
} }
bool hasValidPosition(const meshtastic_NodeInfoLite *n); bool hasValidPosition(const meshtastic_NodeInfoLite *n);
#if !defined(MESHTASTIC_EXCLUDE_PKI)
bool checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest);
#endif
bool backupPreferences(meshtastic_AdminMessage_BackupLocation location); bool backupPreferences(meshtastic_AdminMessage_BackupLocation location);
bool restorePreferences(meshtastic_AdminMessage_BackupLocation location, bool restorePreferences(meshtastic_AdminMessage_BackupLocation location,
int restoreWhat = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS); int restoreWhat = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS);
/// Notify observers of changes to the DB
void notifyObservers(bool forceUpdate = false) void notifyObservers(bool forceUpdate = false)
{ {
// Notify observers of the current node state FUNCTION_START("notifyObservers");
const meshtastic::NodeStatus status = meshtastic::NodeStatus(getNumOnlineMeshNodes(), getNumMeshNodes(), forceUpdate); _notifyObservers(forceUpdate);
newStatus.notifyObservers(&status); FUNCTION_END;
} }
private: private:
bool fakeMutex = false;
/// Notify observers of changes to the DB
void _notifyObservers(bool forceUpdate = false)
{
// Notify observers of the current node state
const meshtastic::NodeStatus status = meshtastic::NodeStatus(_getNumOnlineMeshNodes(), numMeshNodes, forceUpdate);
newStatus.notifyObservers(&status);
}
std::vector<meshtastic_NodeInfoLite> *meshNodes;
bool duplicateWarned = false; bool duplicateWarned = false;
uint32_t lastNodeDbSave = 0; // when we last saved our db to flash uint32_t lastNodeDbSave = 0; // when we last saved our db to flash
uint32_t lastBackupAttempt = 0; // when we last tried a backup automatically or manually uint32_t lastBackupAttempt = 0; // when we last tried a backup automatically or manually
@@ -333,6 +366,51 @@ class NodeDB
bool saveDeviceStateToDisk(); bool saveDeviceStateToDisk();
bool saveNodeDatabaseToDisk(); bool saveNodeDatabaseToDisk();
void sortMeshDB(); void sortMeshDB();
void initConfigIntervals(), initModuleConfigIntervals();
size_t getMaxNodesAllocatedSize()
{
meshtastic_NodeDatabase emptyNodeDatabase;
emptyNodeDatabase.version = DEVICESTATE_CUR_VER;
size_t nodeDatabaseSize;
pb_get_encoded_size(&nodeDatabaseSize, meshtastic_NodeDatabase_fields, &emptyNodeDatabase);
return nodeDatabaseSize + (MAX_NUM_NODES * meshtastic_NodeInfoLite_size);
}
bool checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest);
// wrapped private functions:
bool _saveToDisk(int saveWhat = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS |
SEGMENT_NODEDATABASE);
void _resetRadioConfig(bool is_fresh_install = false);
/* Return the number of nodes we've heard from recently (within the last 2 hrs?)
* @param localOnly if true, ignore nodes heard via MQTT
*/
size_t _getNumOnlineMeshNodes(bool localOnly = false);
void _installRoleDefaults(meshtastic_Config_DeviceConfig_Role role);
meshtastic_NodeInfoLite *_getMeshNode(NodeNum n);
bool _isFull();
void _clearLocalPosition();
void _setLocalPosition(meshtastic_Position position, bool timeOnly = false)
{
if (timeOnly) {
LOG_DEBUG("Set local position time only: time=%u timestamp=%u", position.time, position.timestamp);
localPosition.time = position.time;
localPosition.timestamp = position.timestamp > 0 ? position.timestamp : position.time;
return;
}
LOG_DEBUG("Set local position: lat=%i lon=%i time=%u timestamp=%u", position.latitude_i, position.longitude_i,
position.time, position.timestamp);
localPosition = position;
}
}; };
extern NodeDB *nodeDB; extern NodeDB *nodeDB;

View File

@@ -503,11 +503,6 @@ void RadioInterface::applyModemConfig()
cr = 5; cr = 5;
sf = 10; sf = 10;
break; break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO:
bw = (myRegion->wideLora) ? 1625.0 : 500;
cr = 8;
sf = 11;
break;
default: // Config_LoRaConfig_ModemPreset_LONG_FAST is default. Gracefully use this is preset is something illegal. default: // Config_LoRaConfig_ModemPreset_LONG_FAST is default. Gracefully use this is preset is something illegal.
bw = (myRegion->wideLora) ? 812.5 : 250; bw = (myRegion->wideLora) ? 812.5 : 250;
cr = 5; cr = 5;
@@ -544,26 +539,13 @@ void RadioInterface::applyModemConfig()
} }
if ((myRegion->freqEnd - myRegion->freqStart) < bw / 1000) { if ((myRegion->freqEnd - myRegion->freqStart) < bw / 1000) {
const float regionSpanKHz = (myRegion->freqEnd - myRegion->freqStart) * 1000.0f; static const char *err_string = "Regional frequency range is smaller than bandwidth. Fall back to default preset";
const float requestedBwKHz = bw; LOG_ERROR(err_string);
const bool isWideRequest = requestedBwKHz >= 499.5f; // treat as 500 kHz preset
const char *presetName =
DisplayFormatters::getModemPresetDisplayName(loraConfig.modem_preset, false, loraConfig.use_preset);
char err_string[160];
if (isWideRequest) {
snprintf(err_string, sizeof(err_string), "%s region too narrow for 500kHz preset (%s). Falling back to LongFast.",
myRegion->name, presetName);
} else {
snprintf(err_string, sizeof(err_string), "%s region span %.0fkHz < requested %.0fkHz. Falling back to LongFast.",
myRegion->name, regionSpanKHz, requestedBwKHz);
}
LOG_ERROR("%s", err_string);
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
cn->level = meshtastic_LogRecord_Level_ERROR; cn->level = meshtastic_LogRecord_Level_ERROR;
snprintf(cn->message, sizeof(cn->message), "%s", err_string); sprintf(cn->message, err_string);
service->sendClientNotification(cn); service->sendClientNotification(cn);
// Set to default modem preset // Set to default modem preset

View File

@@ -150,9 +150,7 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
PacketId nakId = (c && c->error_reason != meshtastic_Routing_Error_NONE) ? p->decoded.request_id : 0; PacketId nakId = (c && c->error_reason != meshtastic_Routing_Error_NONE) ? p->decoded.request_id : 0;
// We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records // We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records
if ((ackId || nakId) && if (ackId || nakId) {
// Implicit ACKs from MQTT should not stop retransmissions
!(isFromUs(p) && p->transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MQTT)) {
LOG_DEBUG("Received a %s for 0x%x, stopping retransmissions", ackId ? "ACK" : "NAK", ackId); LOG_DEBUG("Received a %s for 0x%x, stopping retransmissions", ackId ? "ACK" : "NAK", ackId);
if (ackId) { if (ackId) {
stopRetransmission(p->to, ackId); stopRetransmission(p->to, ackId);

View File

@@ -526,10 +526,6 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p)
#elif ARCH_PORTDUINO #elif ARCH_PORTDUINO
if (portduino_config.traceFilename != "" || portduino_config.logoutputlevel == level_trace) { if (portduino_config.traceFilename != "" || portduino_config.logoutputlevel == level_trace) {
LOG_TRACE("%s", MeshPacketSerializer::JsonSerialize(p, false).c_str()); LOG_TRACE("%s", MeshPacketSerializer::JsonSerialize(p, false).c_str());
} else if (portduino_config.JSONFilename != "") {
if (portduino_config.JSONFilter == (_meshtastic_PortNum)0 || portduino_config.JSONFilter == p->decoded.portnum) {
JSONFile << MeshPacketSerializer::JsonSerialize(p, false) << std::endl;
}
} }
#endif #endif
return DecodeState::DECODE_SUCCESS; return DecodeState::DECODE_SUCCESS;

View File

@@ -293,8 +293,7 @@ typedef enum _meshtastic_Config_LoRaConfig_RegionCode {
typedef enum _meshtastic_Config_LoRaConfig_ModemPreset { typedef enum _meshtastic_Config_LoRaConfig_ModemPreset {
/* Long Range - Fast */ /* Long Range - Fast */
meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST = 0, meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST = 0,
/* Long Range - Slow /* Long Range - Slow */
Deprecated in 2.7: Unpopular slow preset. */
meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW = 1, meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW = 1,
/* Very Long Range - Slow /* Very Long Range - Slow
Deprecated in 2.5: Works only with txco and is unusably slow */ Deprecated in 2.5: Works only with txco and is unusably slow */
@@ -312,10 +311,7 @@ typedef enum _meshtastic_Config_LoRaConfig_ModemPreset {
/* Short Range - Turbo /* Short Range - Turbo
This is the fastest preset and the only one with 500kHz bandwidth. This is the fastest preset and the only one with 500kHz bandwidth.
It is not legal to use in all regions due to this wider bandwidth. */ It is not legal to use in all regions due to this wider bandwidth. */
meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO = 8, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO = 8
/* Long Range - Turbo
This preset performs similarly to LongFast, but with 500Khz bandwidth. */
meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO = 9
} meshtastic_Config_LoRaConfig_ModemPreset; } meshtastic_Config_LoRaConfig_ModemPreset;
typedef enum _meshtastic_Config_BluetoothConfig_PairingMode { typedef enum _meshtastic_Config_BluetoothConfig_PairingMode {
@@ -693,8 +689,8 @@ extern "C" {
#define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_BR_902+1)) #define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_BR_902+1))
#define _meshtastic_Config_LoRaConfig_ModemPreset_MIN meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST #define _meshtastic_Config_LoRaConfig_ModemPreset_MIN meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST
#define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO #define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO
#define _meshtastic_Config_LoRaConfig_ModemPreset_ARRAYSIZE ((meshtastic_Config_LoRaConfig_ModemPreset)(meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO+1)) #define _meshtastic_Config_LoRaConfig_ModemPreset_ARRAYSIZE ((meshtastic_Config_LoRaConfig_ModemPreset)(meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO+1))
#define _meshtastic_Config_BluetoothConfig_PairingMode_MIN meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN #define _meshtastic_Config_BluetoothConfig_PairingMode_MIN meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN
#define _meshtastic_Config_BluetoothConfig_PairingMode_MAX meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN #define _meshtastic_Config_BluetoothConfig_PairingMode_MAX meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN

View File

@@ -1322,7 +1322,7 @@ void AdminModule::saveChanges(int saveWhat, bool shouldReboot)
void AdminModule::handleStoreDeviceUIConfig(const meshtastic_DeviceUIConfig &uicfg) void AdminModule::handleStoreDeviceUIConfig(const meshtastic_DeviceUIConfig &uicfg)
{ {
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uicfg); saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uicfg);
} }
void AdminModule::handleSetHamMode(const meshtastic_HamParameters &p) void AdminModule::handleSetHamMode(const meshtastic_HamParameters &p)

View File

@@ -2273,9 +2273,9 @@ ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket &
void CannedMessageModule::loadProtoForModule() void CannedMessageModule::loadProtoForModule()
{ {
if (nodeDB->loadProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size, if (loadProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size,
sizeof(meshtastic_CannedMessageModuleConfig), &meshtastic_CannedMessageModuleConfig_msg, sizeof(meshtastic_CannedMessageModuleConfig), &meshtastic_CannedMessageModuleConfig_msg,
&cannedMessageModuleConfig) != LoadFileResult::LOAD_SUCCESS) { &cannedMessageModuleConfig) != LoadFileResult::LOAD_SUCCESS) {
installDefaultCannedMessageModuleConfig(); installDefaultCannedMessageModuleConfig();
} }
} }
@@ -2295,8 +2295,8 @@ bool CannedMessageModule::saveProtoForModule()
spiLock->unlock(); spiLock->unlock();
#endif #endif
okay &= nodeDB->saveProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size, okay &= saveProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size,
&meshtastic_CannedMessageModuleConfig_msg, &cannedMessageModuleConfig); &meshtastic_CannedMessageModuleConfig_msg, &cannedMessageModuleConfig);
return okay; return okay;
} }

View File

@@ -14,6 +14,7 @@
* @date [Insert Date] * @date [Insert Date]
*/ */
#include "ExternalNotificationModule.h" #include "ExternalNotificationModule.h"
#include "FSCommon.h"
#include "MeshService.h" #include "MeshService.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "RTC.h" #include "RTC.h"
@@ -370,8 +371,8 @@ ExternalNotificationModule::ExternalNotificationModule()
if (inputBroker) // put our callback in the inputObserver list if (inputBroker) // put our callback in the inputObserver list
inputObserver.observe(inputBroker); inputObserver.observe(inputBroker);
#endif #endif
if (nodeDB->loadProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, sizeof(meshtastic_RTTTLConfig), if (loadProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, sizeof(meshtastic_RTTTLConfig), &meshtastic_RTTTLConfig_msg,
&meshtastic_RTTTLConfig_msg, &rtttlConfig) != LoadFileResult::LOAD_SUCCESS) { &rtttlConfig) != LoadFileResult::LOAD_SUCCESS) {
memset(rtttlConfig.ringtone, 0, sizeof(rtttlConfig.ringtone)); memset(rtttlConfig.ringtone, 0, sizeof(rtttlConfig.ringtone));
// The default ringtone is always loaded from userPrefs.jsonc // The default ringtone is always loaded from userPrefs.jsonc
strncpy(rtttlConfig.ringtone, USERPREFS_RINGTONE_RTTTL, sizeof(rtttlConfig.ringtone)); strncpy(rtttlConfig.ringtone, USERPREFS_RINGTONE_RTTTL, sizeof(rtttlConfig.ringtone));
@@ -640,7 +641,7 @@ void ExternalNotificationModule::handleSetRingtone(const char *from_msg)
} }
if (changed) { if (changed) {
nodeDB->saveProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, &meshtastic_RTTTLConfig_msg, &rtttlConfig); saveProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, &meshtastic_RTTTLConfig_msg, &rtttlConfig);
} }
} }

View File

@@ -217,7 +217,7 @@ void setupModules()
} }
#endif // HAS_BUTTON #endif // HAS_BUTTON
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR && portduino_config.i2cdev != "") { if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
seesawRotary = new SeesawRotary("SeesawRotary"); seesawRotary = new SeesawRotary("SeesawRotary");
if (!seesawRotary->init()) { if (!seesawRotary->init()) {
delete seesawRotary; delete seesawRotary;

View File

@@ -45,12 +45,8 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
{ {
auto p = *pptr; auto p = *pptr;
const auto transport = mp.transport_mechanism; // If inbound message is a replay (or spoof!) of our own messages, we shouldn't process
if (isFromUs(&mp) && !IS_ONE_OF(transport, meshtastic_MeshPacket_TransportMechanism_TRANSPORT_INTERNAL, // (why use second-hand sources for our own data?)
meshtastic_MeshPacket_TransportMechanism_TRANSPORT_API)) {
LOG_WARN("Ignoring packet supposedly from us over external transport");
return true;
}
// FIXME this can in fact happen with packets sent from EUD (src=RX_SRC_USER) // FIXME this can in fact happen with packets sent from EUD (src=RX_SRC_USER)
// to set fixed location, EUD-GPS location or just the time (see also issue #900) // to set fixed location, EUD-GPS location or just the time (see also issue #900)
@@ -476,53 +472,19 @@ void PositionModule::sendLostAndFoundText()
delete[] message; delete[] message;
} }
// Helper: return imprecise (truncated + centered) lat/lon as int32 using current precision
static inline void computeImpreciseLatLon(int32_t inLat, int32_t inLon, uint8_t precisionBits, int32_t &outLat, int32_t &outLon)
{
if (precisionBits > 0 && precisionBits < 32) {
// Build mask for top 'precisionBits' bits of a 32-bit unsigned field
const uint32_t mask = (precisionBits == 32) ? UINT32_MAX : (UINT32_MAX << (32 - precisionBits));
// Note: latitude_i/longitude_i are stored as signed 32-bit in meshtastic code but
// the bitmask logic used previously operated as unsigned—preserve that behavior by
// casting to uint32_t for masking, then back to int32_t.
uint32_t lat_u = static_cast<uint32_t>(inLat) & mask;
uint32_t lon_u = static_cast<uint32_t>(inLon) & mask;
// Add the "center of cell" offset used elsewhere:
// The code previously added (1 << (31 - precision)) to produce the middle of the possible location.
uint32_t center_offset = (1u << (31 - precisionBits));
lat_u += center_offset;
lon_u += center_offset;
outLat = static_cast<int32_t>(lat_u);
outLon = static_cast<int32_t>(lon_u);
} else {
// full precision: return input unchanged
outLat = inLat;
outLon = inLon;
}
}
struct SmartPosition PositionModule::getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition) struct SmartPosition PositionModule::getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition)
{ {
// The minimum distance to travel before we are able to send a new position packet.
const uint32_t distanceTravelThreshold = const uint32_t distanceTravelThreshold =
Default::getConfiguredOrDefault(config.position.broadcast_smart_minimum_distance, 100); Default::getConfiguredOrDefault(config.position.broadcast_smart_minimum_distance, 100);
int32_t lastLatImprecise, lastLonImprecise; // Determine the distance in meters between two points on the globe
int32_t currentLatImprecise, currentLonImprecise; float distanceTraveledSinceLastSend = GeoCoord::latLongToMeter(
lastGpsLatitude * 1e-7, lastGpsLongitude * 1e-7, currentPosition.latitude_i * 1e-7, currentPosition.longitude_i * 1e-7);
computeImpreciseLatLon(lastGpsLatitude, lastGpsLongitude, precision, lastLatImprecise, lastLonImprecise); return SmartPosition{.distanceTraveled = abs(distanceTraveledSinceLastSend),
computeImpreciseLatLon(currentPosition.latitude_i, currentPosition.longitude_i, precision, currentLatImprecise,
currentLonImprecise);
float distMeters = GeoCoord::latLongToMeter(lastLatImprecise * 1e-7, lastLonImprecise * 1e-7, currentLatImprecise * 1e-7,
currentLonImprecise * 1e-7);
float distanceTraveled = fabsf(distMeters);
return SmartPosition{.distanceTraveled = distanceTraveled,
.distanceThreshold = distanceTravelThreshold, .distanceThreshold = distanceTravelThreshold,
.hasTraveledOverThreshold = distanceTraveled >= distanceTravelThreshold}; .hasTraveledOverThreshold = abs(distanceTraveledSinceLastSend) >= distanceTravelThreshold};
} }
void PositionModule::handleNewPosition() void PositionModule::handleNewPosition()

View File

@@ -75,12 +75,6 @@ uint8_t RoutingModule::getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit
return Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit); // Use the default hop limit return Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit); // Use the default hop limit
} }
meshtastic_MeshPacket *RoutingModule::allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex,
uint8_t hopLimit)
{
return MeshModule::allocAckNak(err, to, idFrom, chIndex, hopLimit);
}
RoutingModule::RoutingModule() : ProtobufModule("routing", meshtastic_PortNum_ROUTING_APP, &meshtastic_Routing_msg) RoutingModule::RoutingModule() : ProtobufModule("routing", meshtastic_PortNum_ROUTING_APP, &meshtastic_Routing_msg)
{ {
isPromiscuous = true; isPromiscuous = true;

View File

@@ -16,9 +16,6 @@ class RoutingModule : public ProtobufModule<meshtastic_Routing>
virtual void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopLimit = 0, virtual void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopLimit = 0,
bool ackWantsAck = false); bool ackWantsAck = false);
meshtastic_MeshPacket *allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex,
uint8_t hopLimit = 0);
// Given the hopStart and hopLimit upon reception of a request, return the hop limit to use for the response // Given the hopStart and hopLimit upon reception of a request, return the hop limit to use for the response
uint8_t getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit); uint8_t getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit);

View File

@@ -26,11 +26,7 @@ int StatusLEDModule::handleStatusUpdate(const meshtastic::Status *arg)
power_state = charged; power_state = charged;
} }
} else { } else {
if (powerStatus->getBatteryChargePercent() > 5) { power_state = discharging;
power_state = discharging;
} else {
power_state = critical;
}
} }
break; break;
} }
@@ -62,33 +58,16 @@ int StatusLEDModule::handleStatusUpdate(const meshtastic::Status *arg)
int32_t StatusLEDModule::runOnce() int32_t StatusLEDModule::runOnce()
{ {
my_interval = 1000;
if (power_state == charging) { if (power_state == charging) {
CHARGE_LED_state = !CHARGE_LED_state; CHARGE_LED_state = !CHARGE_LED_state;
} else if (power_state == charged) { } else if (power_state == charged) {
CHARGE_LED_state = LED_STATE_ON; CHARGE_LED_state = LED_STATE_ON;
} else if (power_state == critical) {
if (POWER_LED_starttime + 30000 < millis() && !doing_fast_blink) {
doing_fast_blink = true;
POWER_LED_starttime = millis();
}
if (doing_fast_blink) {
PAIRING_LED_state = LED_STATE_OFF;
CHARGE_LED_state = !CHARGE_LED_state;
my_interval = 250;
if (POWER_LED_starttime + 2000 < millis()) {
doing_fast_blink = false;
}
} else {
CHARGE_LED_state = LED_STATE_OFF;
}
} else { } else {
CHARGE_LED_state = LED_STATE_OFF; CHARGE_LED_state = LED_STATE_OFF;
} }
if (!config.bluetooth.enabled || PAIRING_LED_starttime + 30 * 1000 < millis() || doing_fast_blink) { if (!config.bluetooth.enabled || PAIRING_LED_starttime + 30 * 1000 < millis()) {
PAIRING_LED_state = LED_STATE_OFF; PAIRING_LED_state = LED_STATE_OFF;
} else if (ble_state == unpaired) { } else if (ble_state == unpaired) {
if (slowTrack) { if (slowTrack) {

View File

@@ -31,10 +31,8 @@ class StatusLEDModule : private concurrency::OSThread
bool PAIRING_LED_state = LED_STATE_OFF; bool PAIRING_LED_state = LED_STATE_OFF;
uint32_t PAIRING_LED_starttime = 0; uint32_t PAIRING_LED_starttime = 0;
uint32_t POWER_LED_starttime = 0;
bool doing_fast_blink = false;
enum PowerState { discharging, charging, charged, critical }; enum PowerState { discharging, charging, charged };
PowerState power_state = discharging; PowerState power_state = discharging;

View File

@@ -87,13 +87,10 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length)
// Generate an implicit ACK towards ourselves (handled and processed only locally!) for this message. // Generate an implicit ACK towards ourselves (handled and processed only locally!) for this message.
// We do this because packets are not rebroadcasted back into MQTT anymore and we assume that at least one node // We do this because packets are not rebroadcasted back into MQTT anymore and we assume that at least one node
// receives it when we get our own packet back. Then we'll stop our retransmissions. // receives it when we get our own packet back. Then we'll stop our retransmissions.
if (isFromUs(e.packet)) { if (isFromUs(e.packet))
auto pAck = routingModule->allocAckNak(meshtastic_Routing_Error_NONE, getFrom(e.packet), e.packet->id, ch.index); routingModule->sendAckNak(meshtastic_Routing_Error_NONE, getFrom(e.packet), e.packet->id, ch.index);
pAck->transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MQTT; else
router->sendLocal(pAck);
} else {
LOG_INFO("Ignore downlink message we originally sent"); LOG_INFO("Ignore downlink message we originally sent");
}
return; return;
} }
if (isFromUs(e.packet)) { if (isFromUs(e.packet)) {

View File

@@ -14,11 +14,11 @@
#include <atomic> #include <atomic>
#include <mutex> #include <mutex>
#ifdef NIMBLE_TWO
#include "NimBLEAdvertising.h" #include "NimBLEAdvertising.h"
#ifdef CONFIG_BT_NIMBLE_EXT_ADV
#include "NimBLEExtAdvertising.h" #include "NimBLEExtAdvertising.h"
#endif
#include "PowerStatus.h" #include "PowerStatus.h"
#endif
#if defined(CONFIG_NIMBLE_CPP_IDF) #if defined(CONFIG_NIMBLE_CPP_IDF)
#include "host/ble_gap.h" #include "host/ble_gap.h"
@@ -26,12 +26,15 @@
#include "nimble/nimble/host/include/host/ble_gap.h" #include "nimble/nimble/host/include/host/ble_gap.h"
#endif #endif
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)
namespace namespace
{ {
constexpr uint16_t kPreferredBleMtu = 517; constexpr uint16_t kPreferredBleMtu = 517;
constexpr uint16_t kPreferredBleTxOctets = 251; constexpr uint16_t kPreferredBleTxOctets = 251;
constexpr uint16_t kPreferredBleTxTimeUs = (kPreferredBleTxOctets + 14) * 8; constexpr uint16_t kPreferredBleTxTimeUs = (kPreferredBleTxOctets + 14) * 8;
} // namespace } // namespace
#endif
// Debugging options: careful, they slow things down quite a bit! // Debugging options: careful, they slow things down quite a bit!
// #define DEBUG_NIMBLE_ON_READ_TIMING // uncomment to time onRead duration // #define DEBUG_NIMBLE_ON_READ_TIMING // uncomment to time onRead duration
@@ -310,9 +313,11 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
{ {
PhoneAPI::onNowHasData(fromRadioNum); PhoneAPI::onNowHasData(fromRadioNum);
#ifdef DEBUG_NIMBLE_NOTIFY
int currentNotifyCount = notifyCount.fetch_add(1); int currentNotifyCount = notifyCount.fetch_add(1);
uint8_t cc = bleServer->getConnectedCount(); uint8_t cc = bleServer->getConnectedCount();
#ifdef DEBUG_NIMBLE_NOTIFY
// This logging slows things down when there are lots of packets going to the phone, like initial connection: // This logging slows things down when there are lots of packets going to the phone, like initial connection:
LOG_DEBUG("BLE notify(%d) fromNum: %d connections: %d", currentNotifyCount, fromRadioNum, cc); LOG_DEBUG("BLE notify(%d) fromNum: %d connections: %d", currentNotifyCount, fromRadioNum, cc);
#endif #endif
@@ -321,7 +326,13 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
put_le32(val, fromRadioNum); put_le32(val, fromRadioNum);
fromNumCharacteristic->setValue(val, sizeof(val)); fromNumCharacteristic->setValue(val, sizeof(val));
#ifdef NIMBLE_TWO
// NOTE: I don't have any NIMBLE_TWO devices, but this line makes me suspicious, and I suspect it needs to just be
// notify().
fromNumCharacteristic->notify(val, sizeof(val), BLE_HS_CONN_HANDLE_NONE); fromNumCharacteristic->notify(val, sizeof(val), BLE_HS_CONN_HANDLE_NONE);
#else
fromNumCharacteristic->notify();
#endif
} }
/// Check the current underlying physical link to see if the client is currently connected /// Check the current underlying physical link to see if the client is currently connected
@@ -386,7 +397,12 @@ static uint8_t lastToRadio[MAX_TO_FROM_RADIO_SIZE];
class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
{ {
void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &) override #ifdef NIMBLE_TWO
virtual void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo)
#else
virtual void onWrite(NimBLECharacteristic *pCharacteristic)
#endif
{ {
// CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce. // CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce.
// Assumption: onWrite is serialized by NimBLE, so we don't need to lock here against multiple concurrent onWrite calls. // Assumption: onWrite is serialized by NimBLE, so we don't need to lock here against multiple concurrent onWrite calls.
@@ -433,7 +449,11 @@ class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
{ {
void onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &) override #ifdef NIMBLE_TWO
virtual void onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo)
#else
virtual void onRead(NimBLECharacteristic *pCharacteristic)
#endif
{ {
// CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce. // CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce.
@@ -541,27 +561,32 @@ class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
class NimbleBluetoothServerCallback : public NimBLEServerCallbacks class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
{ {
#ifdef NIMBLE_TWO
public: public:
explicit NimbleBluetoothServerCallback(NimbleBluetooth *ble) : ble(ble) {} NimbleBluetoothServerCallback(NimbleBluetooth *ble) { this->ble = ble; }
private: private:
NimbleBluetooth *ble; NimbleBluetooth *ble;
uint32_t onPassKeyDisplay() override virtual uint32_t onPassKeyDisplay()
#else
virtual uint32_t onPassKeyRequest()
#endif
{ {
uint32_t passkey = config.bluetooth.fixed_pin; uint32_t passkey = config.bluetooth.fixed_pin;
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN) { if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN) {
LOG_INFO("Use random passkey"); LOG_INFO("Use random passkey");
// This is the passkey to be entered on peer - we pick a number >100,000 to ensure 6 digits
passkey = random(100000, 999999); passkey = random(100000, 999999);
} }
LOG_INFO("*** Enter passkey %06u on the peer side ***", passkey); LOG_INFO("*** Enter passkey %d on the peer side ***", passkey);
powerFSM.trigger(EVENT_BLUETOOTH_PAIR); powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
meshtastic::BluetoothStatus newStatus(std::to_string(passkey)); meshtastic::BluetoothStatus newStatus(std::to_string(passkey));
bluetoothStatus->updateStatus(&newStatus); bluetoothStatus->updateStatus(&newStatus);
#if HAS_SCREEN #if HAS_SCREEN // Todo: migrate this display code back into Screen class, and observe bluetoothStatus
if (screen) { if (screen) {
screen->startAlert([passkey](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void { screen->startAlert([passkey](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
char btPIN[16] = "888888"; char btPIN[16] = "888888";
@@ -590,29 +615,39 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
}); });
} }
#endif #endif
passkeyShowing = true; passkeyShowing = true;
return passkey; return passkey;
} }
void onAuthenticationComplete(NimBLEConnInfo &connInfo) override #ifdef NIMBLE_TWO
virtual void onAuthenticationComplete(NimBLEConnInfo &connInfo)
#else
virtual void onAuthenticationComplete(ble_gap_conn_desc *desc)
#endif
{ {
LOG_INFO("BLE authentication complete"); LOG_INFO("BLE authentication complete");
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::CONNECTED); meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::CONNECTED);
bluetoothStatus->updateStatus(&newStatus); bluetoothStatus->updateStatus(&newStatus);
// Todo: migrate this display code back into Screen class, and observe bluetoothStatus
if (passkeyShowing) { if (passkeyShowing) {
passkeyShowing = false; passkeyShowing = false;
if (screen) { if (screen)
screen->endAlert(); screen->endAlert();
}
} }
// Store the connection handle for future use
#ifdef NIMBLE_TWO
nimbleBluetoothConnHandle = connInfo.getConnHandle(); nimbleBluetoothConnHandle = connInfo.getConnHandle();
#else
nimbleBluetoothConnHandle = desc->conn_handle;
#endif
} }
void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo) override #ifdef NIMBLE_TWO
virtual void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo)
{ {
LOG_INFO("BLE incoming connection %s", connInfo.getAddress().toString().c_str()); LOG_INFO("BLE incoming connection %s", connInfo.getAddress().toString().c_str());
@@ -637,12 +672,21 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
LOG_INFO("BLE conn %u initial MTU %u (target %u)", connHandle, connInfo.getMTU(), kPreferredBleMtu); LOG_INFO("BLE conn %u initial MTU %u (target %u)", connHandle, connInfo.getMTU(), kPreferredBleMtu);
pServer->updateConnParams(connHandle, 6, 12, 0, 200); pServer->updateConnParams(connHandle, 6, 12, 0, 200);
} }
#endif
void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason) override #ifdef NIMBLE_TWO
virtual void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason)
{ {
LOG_INFO("BLE disconnect reason: %d", reason); LOG_INFO("BLE disconnect reason: %d", reason);
#else
virtual void onDisconnect(NimBLEServer *pServer, ble_gap_conn_desc *desc)
{
LOG_INFO("BLE disconnect");
#endif
#ifdef NIMBLE_TWO
if (ble->isDeInit) if (ble->isDeInit)
return; return;
#endif
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED); meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED);
bluetoothStatus->updateStatus(&newStatus); bluetoothStatus->updateStatus(&newStatus);
@@ -666,69 +710,35 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
bluetoothPhoneAPI->writeCount = 0; bluetoothPhoneAPI->writeCount = 0;
} }
// Clear the last ToRadio packet buffer to avoid rejecting first packet from new connection
memset(lastToRadio, 0, sizeof(lastToRadio)); memset(lastToRadio, 0, sizeof(lastToRadio));
nimbleBluetoothConnHandle = BLE_HS_CONN_HANDLE_NONE; nimbleBluetoothConnHandle = BLE_HS_CONN_HANDLE_NONE; // BLE_HS_CONN_HANDLE_NONE means "no connection"
#ifdef NIMBLE_TWO
// Restart Advertising
ble->startAdvertising(); ble->startAdvertising();
#else
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
if (!pAdvertising->start(0)) {
if (pAdvertising->isAdvertising()) {
LOG_DEBUG("BLE advertising already running");
} else {
LOG_ERROR("BLE failed to restart advertising");
}
}
#endif
} }
}; };
static NimbleBluetoothToRadioCallback *toRadioCallbacks; static NimbleBluetoothToRadioCallback *toRadioCallbacks;
static NimbleBluetoothFromRadioCallback *fromRadioCallbacks; static NimbleBluetoothFromRadioCallback *fromRadioCallbacks;
void NimbleBluetooth::startAdvertising()
{
#if defined(CONFIG_BT_NIMBLE_EXT_ADV)
NimBLEExtAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
NimBLEExtAdvertisement legacyAdvertising;
legacyAdvertising.setLegacyAdvertising(true);
legacyAdvertising.setScannable(true);
legacyAdvertising.setConnectable(true);
legacyAdvertising.setFlags(BLE_HS_ADV_F_DISC_GEN);
if (powerStatus->getHasBattery() == 1) {
legacyAdvertising.setCompleteServices(NimBLEUUID((uint16_t)0x180f));
}
legacyAdvertising.setCompleteServices(NimBLEUUID(MESH_SERVICE_UUID));
legacyAdvertising.setMinInterval(500);
legacyAdvertising.setMaxInterval(1000);
NimBLEExtAdvertisement legacyScanResponse;
legacyScanResponse.setLegacyAdvertising(true);
legacyScanResponse.setConnectable(true);
legacyScanResponse.setName(getDeviceName());
if (!pAdvertising->setInstanceData(0, legacyAdvertising)) {
LOG_ERROR("BLE failed to set legacyAdvertising");
} else if (!pAdvertising->setScanResponseData(0, legacyScanResponse)) {
LOG_ERROR("BLE failed to set legacyScanResponse");
} else if (!pAdvertising->start(0, 0, 0)) {
LOG_ERROR("BLE failed to start legacyAdvertising");
}
#else
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->reset();
pAdvertising->addServiceUUID(MESH_SERVICE_UUID);
if (powerStatus->getHasBattery() == 1) {
pAdvertising->addServiceUUID(NimBLEUUID((uint16_t)0x180f));
}
NimBLEAdvertisementData scan;
scan.setName(getDeviceName());
pAdvertising->setScanResponseData(scan);
pAdvertising->enableScanResponse(true);
if (!pAdvertising->start(0)) {
LOG_ERROR("BLE failed to start advertising");
}
#endif
LOG_DEBUG("BLE Advertising started");
}
void NimbleBluetooth::shutdown() void NimbleBluetooth::shutdown()
{ {
// No measurable power saving for ESP32 during light-sleep(?)
#ifndef ARCH_ESP32 #ifndef ARCH_ESP32
// Shutdown bluetooth for minimum power draw
LOG_INFO("Disable bluetooth"); LOG_INFO("Disable bluetooth");
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->reset(); pAdvertising->reset();
@@ -736,6 +746,7 @@ void NimbleBluetooth::shutdown()
#endif #endif
} }
// Proper shutdown for ESP32. Needs reboot to reverse.
void NimbleBluetooth::deinit() void NimbleBluetooth::deinit()
{ {
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
@@ -749,17 +760,21 @@ void NimbleBluetooth::deinit()
digitalWrite(BLE_LED, LOW); digitalWrite(BLE_LED, LOW);
#endif #endif
#endif #endif
#ifndef NIMBLE_TWO
NimBLEDevice::deinit();
#endif
#endif #endif
} }
// Has initial setup been completed
bool NimbleBluetooth::isActive() bool NimbleBluetooth::isActive()
{ {
return bleServer != nullptr; return bleServer;
} }
bool NimbleBluetooth::isConnected() bool NimbleBluetooth::isConnected()
{ {
return bleServer && bleServer->getConnectedCount() > 0; return bleServer->getConnectedCount() > 0;
} }
int NimbleBluetooth::getRssi() int NimbleBluetooth::getRssi()
@@ -803,7 +818,7 @@ void NimbleBluetooth::setup()
LOG_INFO("Init the NimBLE bluetooth module"); LOG_INFO("Init the NimBLE bluetooth module");
NimBLEDevice::init(getDeviceName()); NimBLEDevice::init(getDeviceName());
NimBLEDevice::setPower(9); NimBLEDevice::setPower(ESP_PWR_LVL_P9);
#if NIMBLE_ENABLE_2M_PHY && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)) #if NIMBLE_ENABLE_2M_PHY && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6))
int mtuResult = NimBLEDevice::setMTU(kPreferredBleMtu); int mtuResult = NimBLEDevice::setMTU(kPreferredBleMtu);
@@ -836,7 +851,11 @@ void NimbleBluetooth::setup()
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY);
} }
bleServer = NimBLEDevice::createServer(); bleServer = NimBLEDevice::createServer();
auto *serverCallbacks = new NimbleBluetoothServerCallback(this); #ifdef NIMBLE_TWO
NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback(this);
#else
NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback();
#endif
bleServer->setCallbacks(serverCallbacks, true); bleServer->setCallbacks(serverCallbacks, true);
setupService(); setupService();
startAdvertising(); startAdvertising();
@@ -881,7 +900,11 @@ void NimbleBluetooth::setupService()
NimBLEService *batteryService = bleServer->createService(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service NimBLEService *batteryService = bleServer->createService(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
BatteryCharacteristic = batteryService->createCharacteristic( // 0x2A19 is the Battery Level characteristic) BatteryCharacteristic = batteryService->createCharacteristic( // 0x2A19 is the Battery Level characteristic)
(uint16_t)0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY, 1); (uint16_t)0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY, 1);
#ifdef NIMBLE_TWO
NimBLE2904 *batteryLevelDescriptor = BatteryCharacteristic->create2904(); NimBLE2904 *batteryLevelDescriptor = BatteryCharacteristic->create2904();
#else
NimBLE2904 *batteryLevelDescriptor = (NimBLE2904 *)BatteryCharacteristic->createDescriptor((uint16_t)0x2904);
#endif
batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8); batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8);
batteryLevelDescriptor->setNamespace(1); batteryLevelDescriptor->setNamespace(1);
batteryLevelDescriptor->setUnit(0x27ad); batteryLevelDescriptor->setUnit(0x27ad);
@@ -889,12 +912,54 @@ void NimbleBluetooth::setupService()
batteryService->start(); batteryService->start();
} }
void NimbleBluetooth::startAdvertising()
{
#ifdef NIMBLE_TWO
NimBLEExtAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
NimBLEExtAdvertisement legacyAdvertising;
legacyAdvertising.setLegacyAdvertising(true);
legacyAdvertising.setScannable(true);
legacyAdvertising.setConnectable(true);
legacyAdvertising.setFlags(BLE_HS_ADV_F_DISC_GEN);
if (powerStatus->getHasBattery() == 1) {
legacyAdvertising.setCompleteServices(NimBLEUUID((uint16_t)0x180f));
}
legacyAdvertising.setCompleteServices(NimBLEUUID(MESH_SERVICE_UUID));
legacyAdvertising.setMinInterval(500);
legacyAdvertising.setMaxInterval(1000);
NimBLEExtAdvertisement legacyScanResponse;
legacyScanResponse.setLegacyAdvertising(true);
legacyScanResponse.setConnectable(true);
legacyScanResponse.setName(getDeviceName());
if (!pAdvertising->setInstanceData(0, legacyAdvertising)) {
LOG_ERROR("BLE failed to set legacyAdvertising");
} else if (!pAdvertising->setScanResponseData(0, legacyScanResponse)) {
LOG_ERROR("BLE failed to set legacyScanResponse");
} else if (!pAdvertising->start(0, 0, 0)) {
LOG_ERROR("BLE failed to start legacyAdvertising");
}
#else
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->reset();
pAdvertising->addServiceUUID(MESH_SERVICE_UUID);
pAdvertising->addServiceUUID(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
pAdvertising->start(0);
#endif
}
/// Given a level between 0-100, update the BLE attribute /// Given a level between 0-100, update the BLE attribute
void updateBatteryLevel(uint8_t level) void updateBatteryLevel(uint8_t level)
{ {
if ((config.bluetooth.enabled == true) && bleServer && nimbleBluetooth->isConnected()) { if ((config.bluetooth.enabled == true) && bleServer && nimbleBluetooth->isConnected()) {
BatteryCharacteristic->setValue(&level, 1); BatteryCharacteristic->setValue(&level, 1);
#ifdef NIMBLE_TWO
BatteryCharacteristic->notify(&level, 1, BLE_HS_CONN_HANDLE_NONE); BatteryCharacteristic->notify(&level, 1, BLE_HS_CONN_HANDLE_NONE);
#else
BatteryCharacteristic->notify();
#endif
} }
} }
@@ -909,7 +974,11 @@ void NimbleBluetooth::sendLog(const uint8_t *logMessage, size_t length)
if (!bleServer || !isConnected() || length > 512) { if (!bleServer || !isConnected() || length > 512) {
return; return;
} }
#ifdef NIMBLE_TWO
logRadioCharacteristic->notify(logMessage, length, BLE_HS_CONN_HANDLE_NONE); logRadioCharacteristic->notify(logMessage, length, BLE_HS_CONN_HANDLE_NONE);
#else
logRadioCharacteristic->notify(logMessage, length, true);
#endif
} }
void clearNVS() void clearNVS()

View File

@@ -12,11 +12,16 @@ class NimbleBluetooth : BluetoothApi
bool isConnected(); bool isConnected();
int getRssi(); int getRssi();
void sendLog(const uint8_t *logMessage, size_t length); void sendLog(const uint8_t *logMessage, size_t length);
#if defined(NIMBLE_TWO)
void startAdvertising(); void startAdvertising();
#endif
bool isDeInit = false; bool isDeInit = false;
private: private:
void setupService(); void setupService();
#if !defined(NIMBLE_TWO)
void startAdvertising();
#endif
}; };
void setBluetoothEnable(bool enable); void setBluetoothEnable(bool enable);

View File

@@ -337,7 +337,7 @@ void cpuDeepSleep(uint32_t msecToWake)
#endif #endif
#ifdef TTGO_T_ECHO #ifdef TTGO_T_ECHO
// To power off the T-Echo, the display must be set // To power off the T-Echo, the display must be set
// as an input pin; otherwise, there will be leakage current. // as an input pin; otherwise, there will be leakage current.
pinMode(PIN_EINK_CS, INPUT); pinMode(PIN_EINK_CS, INPUT);
pinMode(PIN_EINK_DC, INPUT); pinMode(PIN_EINK_DC, INPUT);

View File

@@ -29,7 +29,6 @@
portduino_config_struct portduino_config; portduino_config_struct portduino_config;
std::ofstream traceFile; std::ofstream traceFile;
std::ofstream JSONFile;
Ch341Hal *ch341Hal = nullptr; Ch341Hal *ch341Hal = nullptr;
char *configPath = nullptr; char *configPath = nullptr;
char *optionMac = nullptr; char *optionMac = nullptr;
@@ -464,7 +463,6 @@ void portduinoSetup()
if (portduino_config.lora_spi_dev != "" && portduino_config.lora_spi_dev != "ch341") { if (portduino_config.lora_spi_dev != "" && portduino_config.lora_spi_dev != "ch341") {
SPI.begin(portduino_config.lora_spi_dev.c_str()); SPI.begin(portduino_config.lora_spi_dev.c_str());
} }
if (portduino_config.traceFilename != "") { if (portduino_config.traceFilename != "") {
try { try {
traceFile.open(portduino_config.traceFilename, std::ios::out | std::ios::app); traceFile.open(portduino_config.traceFilename, std::ios::out | std::ios::app);
@@ -472,21 +470,6 @@ void portduinoSetup()
std::cout << "*** traceFile Exception " << e.what() << std::endl; std::cout << "*** traceFile Exception " << e.what() << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (!traceFile.is_open()) {
std::cout << "*** traceFile open failure" << std::endl;
exit(EXIT_FAILURE);
}
} else if (portduino_config.JSONFilename != "") {
try {
JSONFile.open(portduino_config.JSONFilename, std::ios::out | std::ios::app);
} catch (std::ofstream::failure &e) {
std::cout << "*** JSONFile Exception " << e.what() << std::endl;
exit(EXIT_FAILURE);
}
if (!JSONFile.is_open()) {
std::cout << "*** JSONFile open failure" << std::endl;
exit(EXIT_FAILURE);
}
} }
if (verboseEnabled && portduino_config.logoutputlevel != level_trace) { if (verboseEnabled && portduino_config.logoutputlevel != level_trace) {
portduino_config.logoutputlevel = level_debug; portduino_config.logoutputlevel = level_debug;
@@ -534,29 +517,6 @@ bool loadConfig(const char *configPath)
portduino_config.logoutputlevel = level_error; portduino_config.logoutputlevel = level_error;
} }
portduino_config.traceFilename = yamlConfig["Logging"]["TraceFile"].as<std::string>(""); portduino_config.traceFilename = yamlConfig["Logging"]["TraceFile"].as<std::string>("");
portduino_config.JSONFilename = yamlConfig["Logging"]["JSONFile"].as<std::string>("");
portduino_config.JSONFilter = (_meshtastic_PortNum)yamlConfig["Logging"]["JSONFilter"].as<int>(0);
if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "textmessage")
portduino_config.JSONFilter = meshtastic_PortNum_TEXT_MESSAGE_APP;
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "telemetry")
portduino_config.JSONFilter = meshtastic_PortNum_TELEMETRY_APP;
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "nodeinfo")
portduino_config.JSONFilter = meshtastic_PortNum_NODEINFO_APP;
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "position")
portduino_config.JSONFilter = meshtastic_PortNum_POSITION_APP;
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "waypoint")
portduino_config.JSONFilter = meshtastic_PortNum_WAYPOINT_APP;
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "neighborinfo")
portduino_config.JSONFilter = meshtastic_PortNum_NEIGHBORINFO_APP;
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "traceroute")
portduino_config.JSONFilter = meshtastic_PortNum_TRACEROUTE_APP;
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "detection")
portduino_config.JSONFilter = meshtastic_PortNum_DETECTION_SENSOR_APP;
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "paxcounter")
portduino_config.JSONFilter = meshtastic_PortNum_PAXCOUNTER_APP;
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "remotehardware")
portduino_config.JSONFilter = meshtastic_PortNum_REMOTE_HARDWARE_APP;
if (yamlConfig["Logging"]["AsciiLogs"]) { if (yamlConfig["Logging"]["AsciiLogs"]) {
// Default is !isatty(1) but can be set explicitly in config.yaml // Default is !isatty(1) but can be set explicitly in config.yaml
portduino_config.ascii_logs = yamlConfig["Logging"]["AsciiLogs"].as<bool>(); portduino_config.ascii_logs = yamlConfig["Logging"]["AsciiLogs"].as<bool>();

View File

@@ -5,7 +5,6 @@
#include "LR11x0Interface.h" #include "LR11x0Interface.h"
#include "Module.h" #include "Module.h"
#include "mesh/generated/meshtastic/mesh.pb.h"
#include "platform/portduino/USBHal.h" #include "platform/portduino/USBHal.h"
#include "yaml-cpp/yaml.h" #include "yaml-cpp/yaml.h"
@@ -47,8 +46,6 @@ struct pinMapping {
}; };
extern std::ofstream traceFile; extern std::ofstream traceFile;
extern std::ofstream JSONFile;
extern Ch341Hal *ch341Hal; extern Ch341Hal *ch341Hal;
int initGPIOPin(int pinNum, std::string gpioChipname, int line); int initGPIOPin(int pinNum, std::string gpioChipname, int line);
bool loadConfig(const char *configPath); bool loadConfig(const char *configPath);
@@ -151,9 +148,6 @@ extern struct portduino_config_struct {
bool ascii_logs = !isatty(1); bool ascii_logs = !isatty(1);
bool ascii_logs_explicit = false; bool ascii_logs_explicit = false;
std::string JSONFilename;
meshtastic_PortNum JSONFilter = (_meshtastic_PortNum)0;
// Webserver // Webserver
std::string webserver_root_path = ""; std::string webserver_root_path = "";
std::string webserver_ssl_key_path = "/etc/meshtasticd/ssl/private_key.pem"; std::string webserver_ssl_key_path = "/etc/meshtasticd/ssl/private_key.pem";
@@ -419,29 +413,6 @@ extern struct portduino_config_struct {
} }
if (traceFilename != "") if (traceFilename != "")
out << YAML::Key << "TraceFile" << YAML::Value << traceFilename; out << YAML::Key << "TraceFile" << YAML::Value << traceFilename;
if (JSONFilename != "") {
out << YAML::Key << "JSONFile" << YAML::Value << JSONFilename;
if (JSONFilter == meshtastic_PortNum_TEXT_MESSAGE_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "textmessage";
else if (JSONFilter == meshtastic_PortNum_TELEMETRY_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "telemetry";
else if (JSONFilter == meshtastic_PortNum_NODEINFO_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "nodeinfo";
else if (JSONFilter == meshtastic_PortNum_POSITION_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "position";
else if (JSONFilter == meshtastic_PortNum_WAYPOINT_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "waypoint";
else if (JSONFilter == meshtastic_PortNum_NEIGHBORINFO_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "neighborinfo";
else if (JSONFilter == meshtastic_PortNum_TRACEROUTE_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "traceroute";
else if (JSONFilter == meshtastic_PortNum_DETECTION_SENSOR_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "detection";
else if (JSONFilter == meshtastic_PortNum_PAXCOUNTER_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "paxcounter";
else if (JSONFilter == meshtastic_PortNum_REMOTE_HARDWARE_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "remotehardware";
}
if (ascii_logs_explicit) { if (ascii_logs_explicit) {
out << YAML::Key << "AsciiLogs" << YAML::Value << ascii_logs; out << YAML::Key << "AsciiLogs" << YAML::Value << ascii_logs;
} }

View File

@@ -605,13 +605,12 @@ void test_receiveAcksOwnSentMessages(void)
unitTest->publish(&p, nodeDB->getNodeId().c_str()); unitTest->publish(&p, nodeDB->getNodeId().c_str());
// FIXME: Better assertion for this test TEST_ASSERT_TRUE(mockRouter->packets_.empty());
// TEST_ASSERT_TRUE(mockRouter->packets_.empty()); TEST_ASSERT_EQUAL(1, mockRoutingModule->ackNacks_.size());
// TEST_ASSERT_EQUAL(1, mockRoutingModule->ackNacks_.size()); const auto &[err, to, idFrom, chIndex, hopLimit] = mockRoutingModule->ackNacks_.front();
// const auto &[err, to, idFrom, chIndex, hopLimit] = mockRoutingModule->ackNacks_.front(); TEST_ASSERT_EQUAL(meshtastic_Routing_Error_NONE, err);
// TEST_ASSERT_EQUAL(meshtastic_Routing_Error_NONE, err); TEST_ASSERT_EQUAL(myNodeInfo.my_node_num, to);
// TEST_ASSERT_EQUAL(myNodeInfo.my_node_num, to); TEST_ASSERT_EQUAL(p.id, idFrom);
// TEST_ASSERT_EQUAL(p.id, idFrom);
} }
// Should ignore our own messages from MQTT that were heard by other nodes. // Should ignore our own messages from MQTT that were heard by other nodes.

View File

@@ -2,7 +2,6 @@
[env:meshtastic-dr-dev] [env:meshtastic-dr-dev]
extends = esp32_base extends = esp32_base
board = esp32doit-devkit-v1 board = esp32doit-devkit-v1
board_level = extra
board_upload.maximum_size = 4194304 board_upload.maximum_size = 4194304
board_upload.maximum_ram_size = 532480 board_upload.maximum_ram_size = 532480
build_flags = build_flags =

View File

@@ -5,7 +5,7 @@ custom_esp32_kind = esp32
custom_mtjson_part = custom_mtjson_part =
platform = platform =
# renovate: datasource=custom.pio depName=platformio/espressif32 packageName=platformio/platform/espressif32 # renovate: datasource=custom.pio depName=platformio/espressif32 packageName=platformio/platform/espressif32
platformio/espressif32@6.12.0 platformio/espressif32@6.11.0
extra_scripts = extra_scripts =
${env.extra_scripts} ${env.extra_scripts}
@@ -38,7 +38,6 @@ build_flags =
-DAXP_DEBUG_PORT=Serial -DAXP_DEBUG_PORT=Serial
-DCONFIG_BT_NIMBLE_ENABLED -DCONFIG_BT_NIMBLE_ENABLED
-DCONFIG_BT_NIMBLE_MAX_BONDS=6 # default is 3 -DCONFIG_BT_NIMBLE_MAX_BONDS=6 # default is 3
-DCONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=2 -DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20 -DCONFIG_BT_NIMBLE_MAX_CCCDS=20
-DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192 -DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192
@@ -60,7 +59,7 @@ lib_deps =
# renovate: datasource=git-refs depName=meshtastic-esp32_https_server packageName=https://github.com/meshtastic/esp32_https_server gitBranch=master # renovate: datasource=git-refs depName=meshtastic-esp32_https_server packageName=https://github.com/meshtastic/esp32_https_server gitBranch=master
https://github.com/meshtastic/esp32_https_server/archive/3223704846752e6d545139204837bdb2a55459ca.zip https://github.com/meshtastic/esp32_https_server/archive/3223704846752e6d545139204837bdb2a55459ca.zip
# renovate: datasource=custom.pio depName=NimBLE-Arduino packageName=h2zero/library/NimBLE-Arduino # renovate: datasource=custom.pio depName=NimBLE-Arduino packageName=h2zero/library/NimBLE-Arduino
h2zero/NimBLE-Arduino@^2.3.7 h2zero/NimBLE-Arduino@^1.4.3
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master # renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
# renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib # renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib

View File

@@ -2,7 +2,7 @@
[env:tbeam] [env:tbeam]
extends = esp32_base extends = esp32_base
board = ttgo-t-beam board = ttgo-t-beam
board_level = extra board_level = pr
board_check = true board_check = true
lib_deps = ${esp32_base.lib_deps} lib_deps = ${esp32_base.lib_deps}
build_flags = ${esp32_base.build_flags} build_flags = ${esp32_base.build_flags}

View File

@@ -4,8 +4,3 @@ custom_esp32_kind = esp32c3
monitor_speed = 115200 monitor_speed = 115200
monitor_filters = esp32_c3_exception_decoder monitor_filters = esp32_c3_exception_decoder
build_flags =
${esp32_base.build_flags}
-DCONFIG_BT_NIMBLE_EXT_ADV=1
-DCONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES=2

View File

@@ -13,7 +13,7 @@ build_unflags =
lib_deps = lib_deps =
${esp32c6_base.lib_deps} ${esp32c6_base.lib_deps}
adafruit/Adafruit NeoPixel@^1.12.3 adafruit/Adafruit NeoPixel@^1.12.3
h2zero/NimBLE-Arduino@^2.3.7 h2zero/NimBLE-Arduino@^2.3.6
build_flags = build_flags =
${esp32c6_base.build_flags} ${esp32c6_base.build_flags}
-D M5STACK_UNITC6L -D M5STACK_UNITC6L
@@ -24,6 +24,7 @@ build_flags =
-D HAS_BLUETOOTH=1 -D HAS_BLUETOOTH=1
-DCONFIG_BT_NIMBLE_EXT_ADV=1 -DCONFIG_BT_NIMBLE_EXT_ADV=1
-DCONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES=2 -DCONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES=2
-D NIMBLE_TWO
monitor_speed=115200 monitor_speed=115200
lib_ignore = lib_ignore =
NonBlockingRTTTL NonBlockingRTTTL

View File

@@ -3,8 +3,3 @@ extends = esp32_base
custom_esp32_kind = esp32s3 custom_esp32_kind = esp32s3
monitor_speed = 115200 monitor_speed = 115200
build_flags =
${esp32_base.build_flags}
-DCONFIG_BT_NIMBLE_EXT_ADV=1
-DCONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES=2

View File

@@ -1,7 +1,6 @@
[env:link32-s3-v1] [env:link32-s3-v1]
extends = esp32s3_base extends = esp32s3_base
board = esp32-s3-devkitc-1 board = esp32-s3-devkitc-1
board_level = extra
build_flags = build_flags =
${esp32_base.build_flags} ${esp32_base.build_flags}
-D LINK_32 -D LINK_32

View File

@@ -63,20 +63,9 @@ void initVariant()
// called from main-nrf52.cpp during the cpuDeepSleep() function // called from main-nrf52.cpp during the cpuDeepSleep() function
void variant_shutdown() void variant_shutdown()
{ {
digitalWrite(red_LED_PIN, HIGH);
digitalWrite(green_LED_PIN, HIGH);
digitalWrite(LED_BLUE, HIGH);
digitalWrite(PIN_EN1, LOW);
digitalWrite(PIN_EN2, LOW);
digitalWrite(EEPROM_POWER, LOW); digitalWrite(EEPROM_POWER, LOW);
digitalWrite(KEY_POWER, LOW); digitalWrite(KEY_POWER, LOW);
digitalWrite(DHT_POWER, LOW);
digitalWrite(ACC_POWER, LOW);
digitalWrite(Battery_POWER, LOW);
digitalWrite(GPS_POWER, LOW);
// This sets the pin to OUTPUT and LOW for the pins *not* in the if block.
for (int pin = 0; pin < 48; pin++) { for (int pin = 0; pin < 48; pin++) {
if (pin == PIN_POWER_USB || pin == BUTTON_PIN || pin == PIN_EN1 || pin == PIN_EN2 || pin == DHT_POWER || if (pin == PIN_POWER_USB || pin == BUTTON_PIN || pin == PIN_EN1 || pin == PIN_EN2 || pin == DHT_POWER ||
pin == ACC_POWER || pin == Battery_POWER || pin == GPS_POWER || pin == LR1110_SPI_MISO_PIN || pin == ACC_POWER || pin == Battery_POWER || pin == GPS_POWER || pin == LR1110_SPI_MISO_PIN ||

View File

@@ -54,7 +54,6 @@ extern "C" {
#define LED_POWER red_LED_PIN #define LED_POWER red_LED_PIN
#define LED_CHARGE LED_POWER // Signals the Status LED Module to handle this LED #define LED_CHARGE LED_POWER // Signals the Status LED Module to handle this LED
#define green_LED_PIN 35 #define green_LED_PIN 35
#define PIN_LED2 green_LED_PIN
#define LED_BLUE 37 #define LED_BLUE 37
#define LED_PAIRING LED_BLUE // Signals the Status LED Module to handle this LED #define LED_PAIRING LED_BLUE // Signals the Status LED Module to handle this LED

View File

@@ -19,6 +19,7 @@ build_type = release
build_flags = build_flags =
-include variants/nrf52840/cpp_overrides/lfs_util.h -include variants/nrf52840/cpp_overrides/lfs_util.h
${arduino_base.build_flags} ${arduino_base.build_flags}
-DSERIAL_BUFFER_SIZE=1024
-Wno-unused-variable -Wno-unused-variable
-Isrc/platform/nrf52 -Isrc/platform/nrf52
-DLFS_NO_ASSERT ; Disable LFS assertions , see https://github.com/meshtastic/firmware/pull/3818 -DLFS_NO_ASSERT ; Disable LFS assertions , see https://github.com/meshtastic/firmware/pull/3818

View File

@@ -1,9 +1,7 @@
[nrf52832_base] [nrf52832_base]
extends = nrf52_base extends = nrf52_base
build_flags = build_flags = ${nrf52_base.build_flags}
${nrf52_base.build_flags}
-DSERIAL_BUFFER_SIZE=1024
lib_deps = lib_deps =
${nrf52_base.lib_deps} ${nrf52_base.lib_deps}

View File

@@ -1,9 +1,7 @@
[nrf52840_base] [nrf52840_base]
extends = nrf52_base extends = nrf52_base
build_flags = build_flags = ${nrf52_base.build_flags}
${nrf52_base.build_flags}
-DSERIAL_BUFFER_SIZE=4096
lib_deps = lib_deps =
${nrf52_base.lib_deps} ${nrf52_base.lib_deps}

View File

@@ -1,7 +1,6 @@
[env:feather_rp2040_rfm95] [env:feather_rp2040_rfm95]
extends = rp2040_base extends = rp2040_base
board = adafruit_feather board = adafruit_feather
board_level = extra
upload_protocol = picotool upload_protocol = picotool
# add our variants files to the include and src paths # add our variants files to the include and src paths
build_flags = build_flags =