diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index e45fd5d93..d83d052b0 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -13,16 +13,13 @@
},
"customizations": {
"vscode": {
- "extensions": [
- "ms-vscode.cpptools",
- "platformio.platformio-ide",
- ]
+ "extensions": ["ms-vscode.cpptools", "platformio.platformio-ide"]
}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
- "forwardPorts": [ 4403 ],
+ "forwardPorts": [4403],
// Run commands to prepare the container for use
- "postCreateCommand": ".devcontainer/setup.sh",
+ "postCreateCommand": ".devcontainer/setup.sh"
}
diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh
index 866a4a417..0b2665f84 100755
--- a/.devcontainer/setup.sh
+++ b/.devcontainer/setup.sh
@@ -1,3 +1,3 @@
#!/usr/bin/env sh
-git submodule update --init
\ No newline at end of file
+git submodule update --init
diff --git a/.github/ISSUE_TEMPLATE/Bug Report.yml b/.github/ISSUE_TEMPLATE/Bug Report.yml
index b5ca0db40..f2d2f6507 100644
--- a/.github/ISSUE_TEMPLATE/Bug Report.yml
+++ b/.github/ISSUE_TEMPLATE/Bug Report.yml
@@ -49,10 +49,24 @@ body:
- Heltec V3
- Heltec Wireless Paper
- Heltec Wireless Tracker
+ - Heltec Mesh Node T114
+ - Heltec Vision Master E213
+ - Heltec Vision Master E290
+ - Heltec Vision Master T190
+ - Nano G1
+ - Nano G1 Explorer
+ - Nano G2 Ultra
- Raspberry Pi Pico (W)
- Relay v1
- Relay v2
- Seeed Wio Tracker 1110
+ - Seeed Card Tracker T1000-E
+ - Station G1
+ - Station G2
+ - unPhone
+ - CanaryOne
+ - Chatter
+ - Linux Native
- DIY
- Other
validations:
diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml
index b027a36cc..b50ccac26 100644
--- a/.github/ISSUE_TEMPLATE/feature.yml
+++ b/.github/ISSUE_TEMPLATE/feature.yml
@@ -18,6 +18,7 @@ body:
- ESP32
- RP2040
- Linux Native
+ - Cross-Platform
- other
validations:
required: true
diff --git a/.github/actions/build-variant/action.yml b/.github/actions/build-variant/action.yml
new file mode 100644
index 000000000..80d2a56bb
--- /dev/null
+++ b/.github/actions/build-variant/action.yml
@@ -0,0 +1,94 @@
+name: Setup Build Variant Composite Action
+description: Variant build actions for Meshtastic Platform IO steps
+
+inputs:
+ board:
+ description: The board to build for
+ required: true
+ github_token:
+ description: GitHub token
+ required: true
+ build-script-path:
+ description: Path to the build script
+ required: true
+ remove-debug-flags:
+ description: A space separated list of files to remove debug flags from
+ required: false
+ default: ""
+ ota-firmware-source:
+ description: The OTA firmware file to pull
+ required: false
+ default: ""
+ ota-firmware-target:
+ description: The target path to store the OTA firmware file
+ required: false
+ default: ""
+ artifact-paths:
+ description: A newline separated list of paths to store as artifacts
+ required: false
+ default: ""
+ include-web-ui:
+ description: Include the web UI in the build
+ required: false
+ default: "false"
+ arch:
+ description: Processor arch name
+ required: true
+ default: "esp32"
+
+runs:
+ using: composite
+ steps:
+ - name: Build base
+ id: base
+ uses: ./.github/actions/setup-base
+
+ - name: Pull web ui
+ if: inputs.include-web-ui == 'true'
+ uses: dsaltares/fetch-gh-release-asset@master
+ with:
+ repo: meshtastic/web
+ file: build.tar
+ target: build.tar
+ token: ${{ inputs.github_token }}
+
+ - name: Unpack web ui
+ if: inputs.include-web-ui == 'true'
+ shell: bash
+ run: |
+ tar -xf build.tar -C data/static
+ rm build.tar
+
+ - name: Remove debug flags for release
+ shell: bash
+ if: inputs.remove-debug-flags != ''
+ run: |
+ for INI_FILE in ${{ inputs.remove-debug-flags }}; do
+ sed -i '/DDEBUG_HEAP/d' ${INI_FILE}
+ done
+
+ - name: Build ${{ inputs.board }}
+ shell: bash
+ run: ${{ inputs.build-script-path }} ${{ inputs.board }}
+
+ - name: Pull OTA Firmware
+ if: inputs.ota-firmware-source != '' && inputs.ota-firmware-target != ''
+ uses: dsaltares/fetch-gh-release-asset@master
+ with:
+ repo: meshtastic/firmware-ota
+ file: ${{ inputs.ota-firmware-source }}
+ target: ${{ inputs.ota-firmware-target }}
+ token: ${{ inputs.github_token }}
+
+ - name: Get release version string
+ shell: bash
+ run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
+ id: version
+
+ - name: Store binaries as an artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: firmware-${{ inputs.arch }}-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip
+ overwrite: true
+ path: |
+ ${{ inputs.artifact-paths }}
diff --git a/.github/actions/setup-base/action.yml b/.github/actions/setup-base/action.yml
index 929c1df38..c0f6c4e66 100644
--- a/.github/actions/setup-base/action.yml
+++ b/.github/actions/setup-base/action.yml
@@ -11,6 +11,11 @@ runs:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
+ - name: Uncomment build epoch
+ shell: bash
+ run: |
+ sed -i 's/#-DBUILD_EPOCH=$UNIX_TIME/-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini
+
- name: Install dependencies
shell: bash
run: |
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..616c16ce2
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,26 @@
+version: 2
+updates:
+ - package-ecosystem: docker
+ directory: devcontainer
+ schedule:
+ interval: daily
+ time: "05:00" # trunk-ignore(yamllint/quoted-strings): required by dependabot syntax check
+ timezone: US/Pacific
+ - package-ecosystem: docker
+ directory: /
+ schedule:
+ interval: daily
+ time: "05:00" # trunk-ignore(yamllint/quoted-strings): required by dependabot syntax check
+ timezone: US/Pacific
+ - package-ecosystem: gitsubmodule
+ directory: /
+ schedule:
+ interval: daily
+ time: "05:00" # trunk-ignore(yamllint/quoted-strings): required by dependabot syntax check
+ timezone: US/Pacific
+ - package-ecosystem: github-actions
+ directory: /.github/workflows
+ schedule:
+ interval: daily
+ time: "05:00" # trunk-ignore(yamllint/quoted-strings): required by dependabot syntax check
+ timezone: US/Pacific
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 512dea311..6ccb4a105 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -1,9 +1,9 @@
+### ❌ (Please delete all these tips and replace them with your text) ❌
+
## Thank you for sending in a pull request, here's some tips to get started!
-(Please delete all these tips and replace with your text)
-
- Before starting on some new big chunk of code, it it is optional but highly recommended to open an issue first
- to say "hey, I think this idea X should be implemented and I'm starting work on it. My general plan is Y, any feedback
+ to say "Hey, I think this idea X should be implemented and I'm starting work on it. My general plan is Y, any feedback
is appreciated." This will allow other devs to potentially save you time by not accidentially duplicating work etc...
- Please do not check in files that don't have real changes
- Please do not reformat lines that you didn't have to change the code on
@@ -12,3 +12,4 @@
- If your PR fixes a bug, mention "fixes #bugnum" somewhere in your pull request description.
- If your other co-developers have comments on your PR please tweak as needed.
- Please also enable "Allow edits by maintainers".
+- If your PR gets accepted you can request a "Contributor" role in the Meshtastic Discord
diff --git a/.github/workflows/build_esp32.yml b/.github/workflows/build_esp32.yml
index 4cbb4c7a4..7d069e3db 100644
--- a/.github/workflows/build_esp32.yml
+++ b/.github/workflows/build_esp32.yml
@@ -12,52 +12,23 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - name: Build base
- id: base
- uses: ./.github/actions/setup-base
-
- - name: Pull web ui
- uses: dsaltares/fetch-gh-release-asset@master
- with:
- repo: meshtastic/web
- file: build.tar
- target: build.tar
- token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Unpack web ui
- run: |
- tar -xf build.tar -C data/static
- rm build.tar
-
- - name: Remove debug flags for release
- if: ${{ github.event_name == 'workflow_dispatch' }}
- run: |
- sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32.ini
- sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s2.ini
- sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s3.ini
- sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32c3.ini
- name: Build ESP32
- run: bin/build-esp32.sh ${{ inputs.board }}
-
- - name: Pull OTA Firmware
- uses: dsaltares/fetch-gh-release-asset@master
+ id: build
+ uses: ./.github/actions/build-variant
with:
- repo: meshtastic/firmware-ota
- file: firmware.bin
- target: release/bleota.bin
- token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Get release version string
- shell: bash
- run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
- id: version
-
- - name: Store binaries as an artifact
- uses: actions/upload-artifact@v4
- with:
- name: firmware-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip
- overwrite: true
- path: |
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ board: ${{ inputs.board }}
+ remove-debug-flags: >-
+ ./arch/esp32/esp32.ini
+ ./arch/esp32/esp32s2.ini
+ ./arch/esp32/esp32s3.ini
+ ./arch/esp32/esp32c3.ini
+ build-script-path: bin/build-esp32.sh
+ ota-firmware-source: firmware.bin
+ ota-firmware-target: release/bleota.bin
+ artifact-paths: |
release/*.bin
release/*.elf
+ include-web-ui: true
+ arch: esp32
diff --git a/.github/workflows/build_esp32_c3.yml b/.github/workflows/build_esp32_c3.yml
index 07727d711..5234dbe81 100644
--- a/.github/workflows/build_esp32_c3.yml
+++ b/.github/workflows/build_esp32_c3.yml
@@ -14,50 +14,22 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - name: Build base
- id: base
- uses: ./.github/actions/setup-base
- - name: Pull web ui
- uses: dsaltares/fetch-gh-release-asset@master
+ - name: Build ESP32-C3
+ id: build
+ uses: ./.github/actions/build-variant
with:
- repo: meshtastic/web
- file: build.tar
- target: build.tar
- token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Unpack web ui
- run: |
- tar -xf build.tar -C data/static
- rm build.tar
- - name: Remove debug flags for release
- if: ${{ github.event_name == 'workflow_dispatch' }}
- run: |
- sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32.ini
- sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s2.ini
- sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s3.ini
- sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32c3.ini
- - name: Build ESP32
- run: bin/build-esp32.sh ${{ inputs.board }}
-
- - name: Pull OTA Firmware
- uses: dsaltares/fetch-gh-release-asset@master
- with:
- repo: meshtastic/firmware-ota
- file: firmware-c3.bin
- target: release/bleota-c3.bin
- token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Get release version string
- shell: bash
- run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
- id: version
-
- - name: Store binaries as an artifact
- uses: actions/upload-artifact@v4
- with:
- name: firmware-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip
- overwrite: true
- path: |
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ board: ${{ inputs.board }}
+ remove-debug-flags: >-
+ ./arch/esp32/esp32.ini
+ ./arch/esp32/esp32s2.ini
+ ./arch/esp32/esp32s3.ini
+ ./arch/esp32/esp32c3.ini
+ build-script-path: bin/build-esp32.sh
+ ota-firmware-source: firmware-c3.bin
+ ota-firmware-target: release/bleota-c3.bin
+ artifact-paths: |
release/*.bin
release/*.elf
+ arch: esp32c3
diff --git a/.github/workflows/build_esp32_c6.yml b/.github/workflows/build_esp32_c6.yml
new file mode 100644
index 000000000..66f2764a6
--- /dev/null
+++ b/.github/workflows/build_esp32_c6.yml
@@ -0,0 +1,36 @@
+name: Build ESP32-C6
+
+on:
+ workflow_call:
+ inputs:
+ board:
+ required: true
+ type: string
+
+permissions: read-all
+
+jobs:
+ build-esp32-c6:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Build ESP32-C6
+ id: build
+ uses: ./.github/actions/build-variant
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ board: ${{ inputs.board }}
+ remove-debug-flags: >-
+ ./arch/esp32/esp32.ini
+ ./arch/esp32/esp32s2.ini
+ ./arch/esp32/esp32s3.ini
+ ./arch/esp32/esp32c3.ini
+ ./arch/esp32/esp32c6.ini
+ build-script-path: bin/build-esp32.sh
+ ota-firmware-source: firmware-c3.bin
+ ota-firmware-target: release/bleota-c3.bin
+ artifact-paths: |
+ release/*.bin
+ release/*.elf
+ arch: esp32c6
diff --git a/.github/workflows/build_esp32_s3.yml b/.github/workflows/build_esp32_s3.yml
index 10773833e..554b37cef 100644
--- a/.github/workflows/build_esp32_s3.yml
+++ b/.github/workflows/build_esp32_s3.yml
@@ -12,50 +12,23 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - name: Build base
- id: base
- uses: ./.github/actions/setup-base
- - name: Pull web ui
- uses: dsaltares/fetch-gh-release-asset@master
+ - name: Build ESP32-S3
+ id: build
+ uses: ./.github/actions/build-variant
with:
- repo: meshtastic/web
- file: build.tar
- target: build.tar
- token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Unpack web ui
- run: |
- tar -xf build.tar -C data/static
- rm build.tar
- - name: Remove debug flags for release
- if: ${{ github.event_name == 'workflow_dispatch' }}
- run: |
- sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32.ini
- sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s2.ini
- sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s3.ini
- sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32c3.ini
- - name: Build ESP32
- run: bin/build-esp32.sh ${{ inputs.board }}
-
- - name: Pull OTA Firmware
- uses: dsaltares/fetch-gh-release-asset@master
- with:
- repo: meshtastic/firmware-ota
- file: firmware-s3.bin
- target: release/bleota-s3.bin
- token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Get release version string
- shell: bash
- run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
- id: version
-
- - name: Store binaries as an artifact
- uses: actions/upload-artifact@v4
- with:
- name: firmware-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip
- overwrite: true
- path: |
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ board: ${{ inputs.board }}
+ remove-debug-flags: >-
+ ./arch/esp32/esp32.ini
+ ./arch/esp32/esp32s2.ini
+ ./arch/esp32/esp32s3.ini
+ ./arch/esp32/esp32c3.ini
+ build-script-path: bin/build-esp32.sh
+ ota-firmware-source: firmware-s3.bin
+ ota-firmware-target: release/bleota-s3.bin
+ artifact-paths: |
release/*.bin
release/*.elf
+ include-web-ui: true
+ arch: esp32s3
diff --git a/.github/workflows/build_native.yml b/.github/workflows/build_native.yml
index 51bef0c13..1fb44a717 100644
--- a/.github/workflows/build_native.yml
+++ b/.github/workflows/build_native.yml
@@ -67,7 +67,7 @@ jobs:
- name: Docker build and push tagged versions
if: ${{ github.event_name == 'workflow_dispatch' }}
continue-on-error: true # FIXME: Failing docker login auth
- uses: docker/build-push-action@v5
+ uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
@@ -77,7 +77,7 @@ jobs:
- name: Docker build and push
if: ${{ github.ref == 'refs/heads/master' && github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
continue-on-error: true # FIXME: Failing docker login auth
- uses: docker/build-push-action@v5
+ uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
diff --git a/.github/workflows/build_nrf52.yml b/.github/workflows/build_nrf52.yml
index ac509a096..ce26838f2 100644
--- a/.github/workflows/build_nrf52.yml
+++ b/.github/workflows/build_nrf52.yml
@@ -12,24 +12,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - name: Build base
- id: base
- uses: ./.github/actions/setup-base
- name: Build NRF52
- run: bin/build-nrf52.sh ${{ inputs.board }}
-
- - name: Get release version string
- run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
- id: version
-
- - name: Store binaries as an artifact
- uses: actions/upload-artifact@v4
+ id: build
+ uses: ./.github/actions/build-variant
with:
- name: firmware-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip
- overwrite: true
- path: |
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ board: ${{ inputs.board }}
+ build-script-path: bin/build-nrf52.sh
+ artifact-paths: |
release/*.hex
release/*.uf2
release/*.elf
release/*.zip
+ arch: nrf52840
diff --git a/.github/workflows/build_rpi2040.yml b/.github/workflows/build_rpi2040.yml
index 6e258fe2a..492a1f010 100644
--- a/.github/workflows/build_rpi2040.yml
+++ b/.github/workflows/build_rpi2040.yml
@@ -12,22 +12,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - name: Build base
- id: base
- uses: ./.github/actions/setup-base
- name: Build Raspberry Pi 2040
- run: ./bin/build-rpi2040.sh ${{ inputs.board }}
-
- - name: Get release version string
- run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
- id: version
-
- - name: Store binaries as an artifact
- uses: actions/upload-artifact@v4
+ id: build
+ uses: ./.github/actions/build-variant
with:
- name: firmware-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip
- overwrite: true
- path: |
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ board: ${{ inputs.board }}
+ build-script-path: bin/build-rpi2040.sh
+ artifact-paths: |
release/*.uf2
release/*.elf
+ arch: rp2040
diff --git a/.github/workflows/build_stm32.yml b/.github/workflows/build_stm32.yml
index d13c52c8a..b463bab71 100644
--- a/.github/workflows/build_stm32.yml
+++ b/.github/workflows/build_stm32.yml
@@ -12,22 +12,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - name: Build base
- id: base
- uses: ./.github/actions/setup-base
- - name: Build STM32
- run: bin/build-stm32.sh ${{ inputs.board }}
-
- - name: Get release version string
- run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
- id: version
-
- - name: Store binaries as an artifact
- uses: actions/upload-artifact@v4
+ - name: Build STM32WL
+ id: build
+ uses: ./.github/actions/build-variant
with:
- name: firmware-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip
- overwrite: true
- path: |
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ board: ${{ inputs.board }}
+ build-script-path: bin/build-stm32.sh
+ artifact-paths: |
release/*.hex
release/*.bin
+ release/*.elf
+ arch: stm32
diff --git a/.github/workflows/generate-userprefs.yml b/.github/workflows/generate-userprefs.yml
new file mode 100644
index 000000000..10dd1ff7d
--- /dev/null
+++ b/.github/workflows/generate-userprefs.yml
@@ -0,0 +1,35 @@
+name: Generate UsersPrefs JSON manifest
+
+on:
+ push:
+ paths:
+ - userPrefs.h
+ branches:
+ - master
+
+jobs:
+ generate-userprefs:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Install Clang
+ run: sudo apt-get install -y clang
+
+ - name: Install trunk
+ run: curl https://get.trunk.io -fsSL | bash
+
+ - name: Generate userPrefs.jsom
+ run: python3 ./bin/build-userprefs-json.py
+
+ - name: Trunk format json
+ run: trunk format userPrefs.json
+
+ - name: Commit userPrefs.json
+ run: |
+ git config --global user.email "actions@github.com"
+ git config --global user.name "GitHub Actions"
+ git add userPrefs.json
+ git commit -m "Update userPrefs.json"
+ git push
diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml
index 36125f72f..555d4d092 100644
--- a/.github/workflows/main_matrix.yml
+++ b/.github/workflows/main_matrix.yml
@@ -1,7 +1,7 @@
name: CI
-#concurrency:
-# group: ${{ github.ref }}
-# cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
+concurrency:
+ group: ci-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
on:
# # Triggers the workflow on push but only for the master branch
push:
@@ -24,7 +24,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- arch: [esp32, esp32s3, esp32c3, nrf52840, rp2040, stm32, check]
+ arch: [esp32, esp32s3, esp32c3, esp32c6, nrf52840, rp2040, stm32, check]
runs-on: ubuntu-latest
steps:
- id: checkout
@@ -32,13 +32,18 @@ jobs:
name: Checkout base
- id: jsonStep
run: |
- TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}})
- echo "$TARGETS"
+ if [[ "${{ github.head_ref }}" == "" ]]; then
+ TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}})
+ else
+ TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} quick)
+ fi
+ echo "Name: ${{ github.ref_name }} Base: ${{ github.base_ref }} Head: ${{ github.head_ref }} Ref: ${{ github.ref }} Targets: $TARGETS"
echo "${{matrix.arch}}=$(jq -cn --argjson environments "$TARGETS" '{board: $environments}')" >> $GITHUB_OUTPUT
outputs:
esp32: ${{ steps.jsonStep.outputs.esp32 }}
esp32s3: ${{ steps.jsonStep.outputs.esp32s3 }}
esp32c3: ${{ steps.jsonStep.outputs.esp32c3 }}
+ esp32c6: ${{ steps.jsonStep.outputs.esp32c6 }}
nrf52840: ${{ steps.jsonStep.outputs.nrf52840 }}
rp2040: ${{ steps.jsonStep.outputs.rp2040 }}
stm32: ${{ steps.jsonStep.outputs.stm32 }}
@@ -51,6 +56,7 @@ jobs:
matrix: ${{ fromJson(needs.setup.outputs.check) }}
runs-on: ubuntu-latest
+ if: ${{ github.event_name != 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v4
- name: Build base
@@ -86,6 +92,15 @@ jobs:
with:
board: ${{ matrix.board }}
+ build-esp32-c6:
+ needs: setup
+ strategy:
+ fail-fast: false
+ matrix: ${{ fromJson(needs.setup.outputs.esp32c6) }}
+ uses: ./.github/workflows/build_esp32_c6.yml
+ with:
+ board: ${{ matrix.board }}
+
build-nrf52:
needs: setup
strategy:
@@ -119,11 +134,12 @@ jobs:
package-raspbian-armv7l:
uses: ./.github/workflows/package_raspbian_armv7l.yml
- package-native:
- uses: ./.github/workflows/package_amd64.yml
+ # package-native:
+ # uses: ./.github/workflows/package_amd64.yml
after-checks:
runs-on: ubuntu-latest
+ if: ${{ github.event_name != 'workflow_dispatch' }}
needs: [check]
steps:
- name: Checkout code
@@ -136,18 +152,20 @@ jobs:
permissions:
contents: write
pull-requests: write
+ strategy:
+ fail-fast: false
+ matrix:
+ arch: [esp32, esp32s3, esp32c3, esp32c6, nrf52840, rp2040, stm32]
runs-on: ubuntu-latest
needs:
[
build-esp32,
build-esp32-s3,
build-esp32-c3,
+ build-esp32-c6,
build-nrf52,
build-rpi2040,
build-stm32,
- package-raspbian,
- package-raspbian-armv7l,
- package-native,
]
steps:
- name: Checkout code
@@ -159,6 +177,7 @@ jobs:
- uses: actions/download-artifact@v4
with:
path: ./
+ pattern: firmware-${{matrix.arch}}-*
merge-multiple: true
- name: Display structure of downloaded files
@@ -169,12 +188,12 @@ jobs:
id: version
- name: Move files up
- run: mv -b -t ./ ./release/meshtasticd_linux_* ./bin/config-dist.yaml ./bin/device-*.sh ./bin/device-*.bat
+ run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
- name: Repackage in single firmware zip
uses: actions/upload-artifact@v4
with:
- name: firmware-${{ steps.version.outputs.version }}
+ name: firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}
overwrite: true
path: |
./firmware-*.bin
@@ -183,16 +202,14 @@ jobs:
./firmware-*-ota.zip
./device-*.sh
./device-*.bat
- ./meshtasticd_linux_*
- ./config-dist.yaml
./littlefs-*.bin
./bleota*bin
./Meshtastic_nRF52_factory_erase*.uf2
- retention-days: 90
+ retention-days: 30
- uses: actions/download-artifact@v4
with:
- name: firmware-${{ steps.version.outputs.version }}
+ name: firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}
merge-multiple: true
path: ./output
@@ -206,32 +223,34 @@ jobs:
chmod +x ./output/device-update.sh
- name: Zip firmware
- run: zip -j -9 -r ./firmware-${{ steps.version.outputs.version }}.zip ./output
+ run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip ./output
- name: Repackage in single elfs zip
uses: actions/upload-artifact@v4
with:
- name: debug-elfs-${{ steps.version.outputs.version }}.zip
+ name: debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip
overwrite: true
path: ./*.elf
retention-days: 30
- - name: Create request artifacts
- continue-on-error: true # FIXME: Why are we getting 502, but things still work?
- if: ${{ github.event_name == 'pull_request_target' || github.event_name == 'pull_request' }}
- uses: gavv/pull-request-artifacts@v2.1.0
+ - uses: scruplelesswizard/comment-artifact@main
+ if: ${{ github.event_name == 'pull_request' }}
with:
- commit: ${{ (github.event.pull_request_target || github.event.pull_request).head.sha }}
- repo-token: ${{ secrets.GITHUB_TOKEN }}
- artifacts-token: ${{ secrets.ARTIFACTS_TOKEN }}
- artifacts-repo: meshtastic/artifacts
- artifacts-branch: device
- artifacts: ./firmware-${{ steps.version.outputs.version }}.zip
+ name: firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}
+ description: "Download firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip. This artifact will be available for 90 days from creation"
+ github-token: ${{ secrets.GITHUB_TOKEN }}
release-artifacts:
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' }}
- needs: [gather-artifacts, after-checks]
+ outputs:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ needs: [
+ gather-artifacts,
+ package-raspbian,
+ package-raspbian-armv7l,
+ # package-native,
+ ]
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -245,42 +264,6 @@ jobs:
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- - uses: actions/download-artifact@v4
- with:
- name: firmware-${{ steps.version.outputs.version }}
- merge-multiple: true
- path: ./output
-
- - uses: actions/download-artifact@v4
- with:
- pattern: meshtasticd_${{ steps.version.outputs.version }}_*.deb
- merge-multiple: true
- path: ./output
-
- - name: Display structure of downloaded files
- run: ls -R
-
- - name: Device scripts permissions
- run: |
- chmod +x ./output/device-install.sh
- chmod +x ./output/device-update.sh
-
- - name: Zip firmware
- run: zip -j -9 -r ./firmware-${{ steps.version.outputs.version }}.zip ./output -x *.deb
-
- - uses: actions/download-artifact@v4
- with:
- name: debug-elfs-${{ steps.version.outputs.version }}.zip
- merge-multiple: true
- path: ./elfs
-
- - name: Zip Elfs
- run: zip -j -9 -r ./debug-elfs-${{ steps.version.outputs.version }}.zip ./elfs
-
- # For diagnostics
- - name: Show artifacts
- run: ls -lR
-
- name: Create release
uses: actions/create-release@v1
id: create_release
@@ -294,25 +277,16 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
- - name: Add bins to release
- uses: actions/upload-release-asset@v1
- env:
- GITHUB_TOKEN: ${{ github.token }}
+ - name: Download deb files
+ uses: actions/download-artifact@v4
with:
- upload_url: ${{ steps.create_release.outputs.upload_url }}
- asset_path: ./firmware-${{ steps.version.outputs.version }}.zip
- asset_name: firmware-${{ steps.version.outputs.version }}.zip
- asset_content_type: application/zip
+ pattern: meshtasticd_${{ steps.version.outputs.version }}_*.deb
+ merge-multiple: true
+ path: ./output
- - name: Add debug elfs to release
- uses: actions/upload-release-asset@v1
- env:
- GITHUB_TOKEN: ${{ github.token }}
- with:
- upload_url: ${{ steps.create_release.outputs.upload_url }}
- asset_path: ./debug-elfs-${{ steps.version.outputs.version }}.zip
- asset_name: debug-elfs-${{ steps.version.outputs.version }}.zip
- asset_content_type: application/zip
+ # For diagnostics
+ - name: Display structure of downloaded files
+ run: ls -lR
- name: Add raspbian aarch64 .deb
uses: actions/upload-release-asset@v1
@@ -334,22 +308,94 @@ jobs:
asset_name: meshtasticd_${{ steps.version.outputs.version }}_armhf.deb
asset_content_type: application/vnd.debian.binary-package
- - name: Add raspbian amd64 .deb
- uses: actions/upload-release-asset@v1
- env:
- GITHUB_TOKEN: ${{ github.token }}
- with:
- upload_url: ${{ steps.create_release.outputs.upload_url }}
- asset_path: ./output/meshtasticd_${{ steps.version.outputs.version }}_amd64.deb
- asset_name: meshtasticd_${{ steps.version.outputs.version }}_amd64.deb
- asset_content_type: application/vnd.debian.binary-package
+ # - name: Add raspbian amd64 .deb
+ # uses: actions/upload-release-asset@v1
+ # env:
+ # GITHUB_TOKEN: ${{ github.token }}
+ # with:
+ # upload_url: ${{ steps.create_release.outputs.upload_url }}
+ # asset_path: ./output/meshtasticd_${{ steps.version.outputs.version }}_amd64.deb
+ # asset_name: meshtasticd_${{ steps.version.outputs.version }}_amd64.deb
+ # asset_content_type: application/vnd.debian.binary-package
- name: Bump version.properties
run: >-
bin/bump_version.py
- name: Create version.properties pull request
- uses: peter-evans/create-pull-request@v6
+ uses: peter-evans/create-pull-request@v7
with:
+ title: Bump version.properties
add-paths: |
version.properties
+
+ release-firmware:
+ strategy:
+ fail-fast: false
+ matrix:
+ arch: [esp32, esp32s3, esp32c3, esp32c6, nrf52840, rp2040, stm32]
+ runs-on: ubuntu-latest
+ if: ${{ github.event_name == 'workflow_dispatch' }}
+ needs: [release-artifacts]
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: 3.x
+
+ - name: Get release version string
+ run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
+ id: version
+
+ - uses: actions/download-artifact@v4
+ with:
+ pattern: firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}
+ merge-multiple: true
+ path: ./output
+
+ - name: Display structure of downloaded files
+ run: ls -lR
+
+ - name: Device scripts permissions
+ run: |
+ chmod +x ./output/device-install.sh
+ chmod +x ./output/device-update.sh
+
+ - name: Zip firmware
+ run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip ./output
+
+ - uses: actions/download-artifact@v4
+ with:
+ name: debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip
+ merge-multiple: true
+ path: ./elfs
+
+ - name: Zip firmware
+ run: zip -j -9 -r ./debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip ./elfs
+
+ # For diagnostics
+ - name: Display structure of downloaded files
+ run: ls -lR
+
+ - name: Add bins to release
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ with:
+ upload_url: ${{needs.release-artifacts.outputs.upload_url}}
+ asset_path: ./firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip
+ asset_name: firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip
+ asset_content_type: application/zip
+
+ - name: Add debug elfs to release
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ with:
+ upload_url: ${{needs.release-artifacts.outputs.upload_url}}
+ asset_path: ./debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip
+ asset_name: debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip
+ asset_content_type: application/zip
diff --git a/.github/workflows/package_amd64.yml b/.github/workflows/package_amd64.yml
index ae7bf3242..0b5093f24 100644
--- a/.github/workflows/package_amd64.yml
+++ b/.github/workflows/package_amd64.yml
@@ -13,7 +13,7 @@ jobs:
uses: ./.github/workflows/build_native.yml
package-native:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
needs: build-native
steps:
- name: Checkout code
diff --git a/.github/workflows/package_raspbian.yml b/.github/workflows/package_raspbian.yml
index 5471332c5..bcbda53e2 100644
--- a/.github/workflows/package_raspbian.yml
+++ b/.github/workflows/package_raspbian.yml
@@ -13,7 +13,7 @@ jobs:
uses: ./.github/workflows/build_raspbian.yml
package-raspbian:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
needs: build-raspbian
steps:
- name: Checkout code
diff --git a/.github/workflows/package_raspbian_armv7l.yml b/.github/workflows/package_raspbian_armv7l.yml
index 5b9c9aa71..1308fe925 100644
--- a/.github/workflows/package_raspbian_armv7l.yml
+++ b/.github/workflows/package_raspbian_armv7l.yml
@@ -13,7 +13,7 @@ jobs:
uses: ./.github/workflows/build_raspbian_armv7l.yml
package-raspbian_armv7l:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
needs: build-raspbian_armv7l
steps:
- name: Checkout code
diff --git a/.github/workflows/sec_sast_semgrep_cron.yml b/.github/workflows/sec_sast_semgrep_cron.yml
index 2a0361f5e..54bbbe6d2 100644
--- a/.github/workflows/sec_sast_semgrep_cron.yml
+++ b/.github/workflows/sec_sast_semgrep_cron.yml
@@ -12,7 +12,7 @@ jobs:
semgrep-full:
runs-on: ubuntu-latest
container:
- image: returntocorp/semgrep
+ image: semgrep/semgrep
steps:
# step 1
diff --git a/.github/workflows/sec_sast_semgrep_pull.yml b/.github/workflows/sec_sast_semgrep_pull.yml
index b6c288494..9013f1c74 100644
--- a/.github/workflows/sec_sast_semgrep_pull.yml
+++ b/.github/workflows/sec_sast_semgrep_pull.yml
@@ -4,9 +4,9 @@ on: pull_request
jobs:
semgrep-diff:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
container:
- image: returntocorp/semgrep
+ image: semgrep/semgrep
steps:
# step 1
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index f58b38ac9..241598fd0 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -57,35 +57,50 @@ jobs:
reporter: java-junit
hardware-tests:
- runs-on: ubuntu-latest
+ runs-on: test-runner
steps:
- name: Checkout code
uses: actions/checkout@v4
+ # - uses: actions/setup-python@v5
+ # with:
+ # python-version: '3.10'
+
+ # pipx install "setuptools<72"
- name: Upgrade python tools
shell: bash
run: |
- python -m pip install --upgrade pip
- pip install -U --no-build-isolation --no-cache-dir "setuptools<72"
- pip install -U platformio adafruit-nrfutil --no-build-isolation
- pip install -U poetry --no-build-isolation
- pip install -U meshtastic --pre --no-build-isolation
+ pipx install adafruit-nrfutil
+ pipx install poetry
+ pipx install meshtastic --pip-args=--pre
+
+ - name: Install PlatformIO from script
+ shell: bash
+ run: |
+ curl -fsSL -o get-platformio.py https://raw.githubusercontent.com/platformio/platformio-core-installer/master/get-platformio.py
+ python3 get-platformio.py
- name: Upgrade platformio
shell: bash
run: |
+ export PATH=$PATH:$HOME/.local/bin
pio upgrade
+ - name: Setup Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: 18
+
- name: Setup pnpm
- uses: pnpm/action-setup@v2
+ uses: pnpm/action-setup@v4
with:
version: latest
- - name: Install Dependencies
- run: pnpm install
-
- - name: Setup devices
- run: pnpm run setup
-
- - name: Execute end to end tests on connected hardware
- run: pnpm run test
+ - name: Install dependencies, setup devices and run
+ shell: bash
+ run: |
+ git submodule update --init --recursive
+ cd meshtestic/
+ pnpm install
+ pnpm run setup
+ pnpm run test
diff --git a/.github/workflows/update_protobufs.yml b/.github/workflows/update_protobufs.yml
index 4402a280e..f1c92b860 100644
--- a/.github/workflows/update_protobufs.yml
+++ b/.github/workflows/update_protobufs.yml
@@ -1,4 +1,4 @@
-name: "Update protobufs and regenerate classes"
+name: Update protobufs and regenerate classes
on: workflow_dispatch
jobs:
@@ -17,17 +17,18 @@ jobs:
- name: Download nanopb
run: |
- wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8-linux-x86.tar.gz
- tar xvzf nanopb-0.4.8-linux-x86.tar.gz
- mv nanopb-0.4.8-linux-x86 nanopb-0.4.8
+ wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.9-linux-x86.tar.gz
+ tar xvzf nanopb-0.4.9-linux-x86.tar.gz
+ mv nanopb-0.4.9-linux-x86 nanopb-0.4.9
- name: Re-generate protocol buffers
run: |
./bin/regen-protos.sh
- name: Create pull request
- uses: peter-evans/create-pull-request@v6
+ uses: peter-evans/create-pull-request@v7
with:
+ title: Update protobufs and classes
add-paths: |
protobufs
src/mesh
diff --git a/.gitignore b/.gitignore
index 0f2202f8d..28f9a24cc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,4 @@
.pio
-main/configuration.h
-main/credentials.h
# ignore vscode IDE settings files
.vscode/*
@@ -32,4 +30,4 @@ release/
.vscode/extensions.json
/compile_commands.json
src/mesh/raspihttp/certificate.pem
-src/mesh/raspihttp/private_key.pem
\ No newline at end of file
+src/mesh/raspihttp/private_key.pem
diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml
index b20a1ec95..ea4045a16 100644
--- a/.trunk/trunk.yaml
+++ b/.trunk/trunk.yaml
@@ -1,34 +1,34 @@
version: 0.1
cli:
- version: 1.22.3
+ version: 1.22.6
plugins:
sources:
- id: trunk
- ref: v1.6.2
+ ref: v1.6.3
uri: https://github.com/trunk-io/plugins
lint:
enabled:
- - trufflehog@3.81.9
+ - trufflehog@3.82.6
- yamllint@1.35.1
- - bandit@1.7.9
- - checkov@3.2.238
+ - bandit@1.7.10
+ - checkov@3.2.256
- terrascan@1.19.1
- - trivy@0.54.1
+ - trivy@0.55.2
#- trufflehog@3.63.2-rc0
- taplo@0.9.3
- - ruff@0.6.2
+ - ruff@0.6.8
- isort@5.13.2
- - markdownlint@0.41.0
+ - markdownlint@0.42.0
- oxipng@9.1.2
- svgo@3.3.2
- - actionlint@1.7.1
+ - actionlint@1.7.3
- flake8@7.1.1
- hadolint@2.12.0
- shfmt@3.6.0
- shellcheck@0.10.0
- black@24.8.0
- git-diff-check
- - gitleaks@8.18.4
+ - gitleaks@8.20.0
- clang-format@16.0.3
- prettier@3.3.3
ignore:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..f579a7fe0
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,47 @@
+# Contributing to Meshtastic Firmware
+
+We're excited that you're interested in contributing to the Meshtastic firmware! This document provides a high-level overview of how you can get involved.
+
+## Important First Steps
+
+Before you begin, please:
+
+1. **Read our documentation**: Our [official documentation](https://meshtastic.org/docs/) is a crucial resource. It contains essential information about the project.
+
+2. **Check out the firmware build guide**: For specific instructions on setting up your development environment and building the firmware, refer to our [Firmware Build Guide](https://meshtastic.org/docs/development/firmware/build/).
+
+3. Read our [Code of Conduct](https://meshtastic.org/docs/legal/conduct/)
+
+4. Join our [Discord community](https://discord.com/invite/ktMAKGBnBs) to connect with developers and other contributors to get help.
+
+## Getting Help and Discussing Ideas
+
+We encourage open communication and discussion before diving into code changes:
+
+1. **Use GitHub Discussions**: For new ideas, questions, or to discuss potential changes, start a conversation in our [GitHub Discussions](https://github.com/meshtastic/firmware/discussions) first. This helps us collaborate and avoid duplicate work.
+
+2. **Join our Discord**: For real-time chat and quick questions, join our [Discord server](https://discord.com/invite/ktMAKGBnBs). It's a great place to get help and connect with other developers and the community.
+
+3. **Reporting Issues**: If you've identified a bug, please use our bug report template when creating a new issue in the [issue tracker](https://github.com/meshtastic/firmware/issues). Ensure you've searched existing issues to avoid duplicates.
+
+## Making Contributions
+
+> [!IMPORTANT]
+> Before making any contributions, you must sign our Contributor License Agreement (CLA). You can do this by visiting https://cla-assistant.io/meshtastic/firmware. Be sure to use the GitHub account you will use to submit your contributions when signing.
+
+1. Fork the repository
+2. Create a new branch for your feature or bug fix
+3. Make your changes
+4. Test your changes thoroughly
+5. Create a pull request with a clear description, using the provided template, of your changes. Be sure to enable "Allow edits from maintainers".
+
+## Coding Standards
+
+To ensure consistent code formatting across the project:
+
+1. Install the [Trunk](https://marketplace.visualstudio.com/items?itemName=Trunk.io) extension for Visual Studio Code.
+2. Before submitting your changes, run `trunk fmt` to automatically format your code according to our standards.
+
+Adhering to these formatting guidelines helps maintain code consistency and makes the review process smoother.
+
+Thank you for contributing to Meshtastic!
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 000000000..fb4d9005a
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,12 @@
+# Security Policy
+
+## Supported Versions
+
+| Firmware Version | Supported |
+| ---------------- | ------------------ |
+| 2.5.x | :white_check_mark: |
+| <= 2.4.x | :x: |
+
+## Reporting a Vulnerability
+
+We support the private reporting of potential security vulnerabilities. Please go to the Security tab to file a report with a description of the potential vulnerability and reproduction scripts (preferred) or steps, and our developers will review.
diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini
index 2c4d3d595..d8641fe9b 100644
--- a/arch/esp32/esp32.ini
+++ b/arch/esp32/esp32.ini
@@ -2,13 +2,13 @@
[esp32_base]
extends = arduino_base
custom_esp32_kind = esp32
-platform = platformio/espressif32@6.7.0
+platform = platformio/espressif32@6.9.0
build_src_filter =
${arduino_base.build_src_filter}
-
-
- -
+ -
-
-
-
@@ -52,7 +52,7 @@ lib_deps =
https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2
h2zero/NimBLE-Arduino@^1.4.2
https://github.com/dbSuS/libpax.git#7bcd3fcab75037505be9b122ab2b24cc5176b587
- https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
+ lewisxhe/XPowersLib@^0.2.6
https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f
rweather/Crypto@^0.4.0
diff --git a/arch/esp32/esp32c6.ini b/arch/esp32/esp32c6.ini
new file mode 100644
index 000000000..53d7f92ec
--- /dev/null
+++ b/arch/esp32/esp32c6.ini
@@ -0,0 +1,40 @@
+[esp32c6_base]
+extends = esp32_base
+platform = https://github.com/Jason2866/platform-espressif32.git#22faa566df8c789000f8136cd8d0aca49617af55
+build_flags =
+ ${arduino_base.build_flags}
+ -Wall
+ -Wextra
+ -Isrc/platform/esp32
+ -std=c++11
+ -DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
+ -DSERIAL_BUFFER_SIZE=4096
+ -DLIBPAX_ARDUINO
+ -DLIBPAX_WIFI
+ -DLIBPAX_BLE
+ -DMESHTASTIC_EXCLUDE_WEBSERVER
+ ;-DDEBUG_HEAP
+ ; TEMP
+ -DHAS_BLUETOOTH=0
+ -DMESHTASTIC_EXCLUDE_PAXCOUNTER
+ -DMESHTASTIC_EXCLUDE_BLUETOOTH
+
+lib_deps =
+ ${arduino_base.lib_deps}
+ ${networking_base.lib_deps}
+ ${environmental_base.lib_deps}
+ lewisxhe/XPowersLib@^0.2.6
+ https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f
+ rweather/Crypto@^0.4.0
+
+build_src_filter =
+ ${esp32_base.build_src_filter} -
+
+monitor_speed = 460800
+monitor_filters = esp32_c3_exception_decoder
+
+lib_ignore =
+ NonBlockingRTTTL
+ NimBLE-Arduino
+ libpax
+
\ No newline at end of file
diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini
index baeb17abd..4ceb6a0ff 100644
--- a/arch/nrf52/nrf52.ini
+++ b/arch/nrf52/nrf52.ini
@@ -24,7 +24,7 @@ build_src_filter =
-
-
-
- -
+ -
-
-
-
diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini
index eb6d0a56d..76de3e8d3 100644
--- a/arch/portduino/portduino.ini
+++ b/arch/portduino/portduino.ini
@@ -1,6 +1,6 @@
; The Portduino based sim environment on top of any host OS, all hardware will be simulated
[portduino_base]
-platform = https://github.com/meshtastic/platform-native.git#ad8112adf82ce1f5b917092cf32be07a077801a0
+platform = https://github.com/meshtastic/platform-native.git#6b3796d697481c8f6e3f4aa5c111bd9979f29e64
framework = arduino
build_src_filter =
@@ -9,7 +9,7 @@ build_src_filter =
-
-
-
- -
+ -
-
-
-
@@ -25,7 +25,7 @@ lib_deps =
${env.lib_deps}
${networking_base.lib_deps}
rweather/Crypto@^0.4.0
- https://github.com/lovyan03/LovyanGFX.git#5a39989aa2c9492572255b22f033843ec8900233
+ lovyan03/LovyanGFX@^1.1.16
build_flags =
${arduino_base.build_flags}
@@ -35,4 +35,4 @@ build_flags =
-DPORTDUINO_LINUX_HARDWARE
-lbluetooth
-lgpiod
- -lyaml-cpp
+ -lyaml-cpp
\ No newline at end of file
diff --git a/arch/rp2xx0/rp2040.ini b/arch/rp2xx0/rp2040.ini
new file mode 100644
index 000000000..c004a3bec
--- /dev/null
+++ b/arch/rp2xx0/rp2040.ini
@@ -0,0 +1,25 @@
+; Common settings for rp2040 Processor based targets
+[rp2040_base]
+platform = https://github.com/maxgerhardt/platform-raspberrypi.git#v1.2.0-gcc12
+extends = arduino_base
+platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#4.0.3
+
+board_build.core = earlephilhower
+board_build.filesystem_size = 0.5m
+build_flags =
+ ${arduino_base.build_flags} -Wno-unused-variable -Wcast-align
+ -Isrc/platform/rp2xx0
+ -Isrc/platform/rp2xx0/hardware_rosc/include
+ -Isrc/platform/rp2xx0/pico_sleep/include
+ -D__PLAT_RP2040__
+# -D _POSIX_THREADS
+build_src_filter =
+ ${arduino_base.build_src_filter} - - - - - - - - -
+
+lib_ignore =
+ BluetoothOTA
+
+lib_deps =
+ ${arduino_base.lib_deps}
+ ${environmental_base.lib_deps}
+ rweather/Crypto
\ No newline at end of file
diff --git a/arch/rp2040/rp2040.ini b/arch/rp2xx0/rp2350.ini
similarity index 82%
rename from arch/rp2040/rp2040.ini
rename to arch/rp2xx0/rp2350.ini
index 372934758..0579dc15e 100644
--- a/arch/rp2040/rp2040.ini
+++ b/arch/rp2xx0/rp2350.ini
@@ -1,14 +1,14 @@
; Common settings for rp2040 Processor based targets
-[rp2040_base]
-platform = https://github.com/maxgerhardt/platform-raspberrypi.git#60d6ae81fcc73c34b1493ca9e261695e471bc0c2
+[rp2350_base]
+platform = https://github.com/maxgerhardt/platform-raspberrypi.git#9e55f6db5c56b9867c69fe473f388beea4546672
extends = arduino_base
-platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#3.7.2
+platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#a6ab6e1f95bc1428d667d55ea7173c0744acc03c ; 4.0.2+
board_build.core = earlephilhower
board_build.filesystem_size = 0.5m
build_flags =
${arduino_base.build_flags} -Wno-unused-variable
- -Isrc/platform/rp2040
+ -Isrc/platform/rp2xx0
-D__PLAT_RP2040__
# -D _POSIX_THREADS
build_src_filter =
diff --git a/arch/stm32/stm32.ini b/arch/stm32/stm32.ini
index 80bd0017c..c1b783083 100644
--- a/arch/stm32/stm32.ini
+++ b/arch/stm32/stm32.ini
@@ -1,7 +1,7 @@
[stm32_base]
extends = arduino_base
platform = ststm32
-platform_packages = platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32.git#361a7fdb67e2a7104e99b4f42a802469eef8b129
+platform_packages = platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32.git#ea74156acd823b6d14739f389e6cdc648f8ee36e
build_type = release
@@ -22,7 +22,7 @@ build_flags =
-fdata-sections
build_src_filter =
- ${arduino_base.build_src_filter} - - - - - - - - - - - - - - -
+ ${arduino_base.build_src_filter} - - - - - - - - - - - - - - -
board_upload.offset_address = 0x08000000
upload_protocol = stlink
@@ -33,5 +33,5 @@ lib_deps =
https://github.com/caveman99/Crypto.git#f61ae26a53f7a2d0ba5511625b8bf8eff3a35d5e
lib_ignore =
- https://github.com/mathertel/OneButton@~2.6.1
+ mathertel/OneButton@~2.6.1
Wire
\ No newline at end of file
diff --git a/bin/build-userprefs-json.py b/bin/build-userprefs-json.py
new file mode 100644
index 000000000..58f460bcf
--- /dev/null
+++ b/bin/build-userprefs-json.py
@@ -0,0 +1,48 @@
+import json
+import subprocess
+import re
+
+def get_macros_from_header(header_file):
+ # Run clang to preprocess the header file and capture the output
+ result = subprocess.run(['clang', '-E', '-dM', header_file], capture_output=True, text=True)
+ if result.returncode != 0:
+ raise RuntimeError(f"Clang preprocessing failed: {result.stderr}")
+
+ # Extract macros from the output
+ macros = {}
+ macro_pattern = re.compile(r'#define\s+(\w+)\s+(.*)')
+ for line in result.stdout.splitlines():
+ match = macro_pattern.match(line)
+ if match and 'USERPREFS_' in line and '_USERPREFS_' not in line:
+ macros[match.group(1)] = match.group(2)
+
+ return macros
+
+def write_macros_to_json(macros, output_file):
+ with open(output_file, 'w') as f:
+ json.dump(macros, f, indent=4)
+
+def main():
+ header_file = 'userPrefs.h'
+ output_file = 'userPrefs.json'
+ # Uncomment all macros in the header file
+ with open(header_file, 'r') as file:
+ lines = file.readlines()
+
+ uncommented_lines = []
+ for line in lines:
+ stripped_line = line.strip().replace('/*', '').replace('*/', '')
+ if stripped_line.startswith('//') and 'USERPREFS_' in stripped_line:
+ # Replace "//"
+ stripped_line = stripped_line.replace('//', '')
+ uncommented_lines.append(stripped_line + '\n')
+
+ with open(header_file, 'w') as file:
+ for line in uncommented_lines:
+ file.write(line)
+ macros = get_macros_from_header(header_file)
+ write_macros_to_json(macros, output_file)
+ print(f"Macros have been written to {output_file}")
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml
index 5590ed3b0..3a470b7bb 100644
--- a/bin/config-dist.yaml
+++ b/bin/config-dist.yaml
@@ -9,6 +9,7 @@ Lora:
# IRQ: 16
# Busy: 20
# Reset: 18
+# SX126X_ANT_SW: 6
# Module: sx1262 # Waveshare SX1302 LISTEN ONLY AT THIS TIME!
# CS: 7
@@ -153,4 +154,4 @@ Webserver:
General:
MaxNodes: 200
- MaxMessageQueue: 100
+ MaxMessageQueue: 100
\ No newline at end of file
diff --git a/bin/generate_ci_matrix.py b/bin/generate_ci_matrix.py
index 46398dd59..4d8759ecc 100755
--- a/bin/generate_ci_matrix.py
+++ b/bin/generate_ci_matrix.py
@@ -6,6 +6,7 @@ import configparser
import json
import os
import sys
+import random
rootdir = "variants/"
@@ -39,5 +40,7 @@ for subdir, dirs, files in os.walk(rootdir):
"check" in options
):
outlist.append(section)
-
-print(json.dumps(outlist))
+if ("quick" in options) & (len(outlist) > 3):
+ print(json.dumps(random.sample(outlist, 3)))
+else:
+ print(json.dumps(outlist))
diff --git a/bin/regen-protos.bat b/bin/regen-protos.bat
index f28ef0025..7fa8f333d 100644
--- a/bin/regen-protos.bat
+++ b/bin/regen-protos.bat
@@ -1 +1 @@
-cd protobufs && ..\nanopb-0.4.8\generator-bin\protoc.exe --experimental_allow_proto3_optional "--nanopb_out=-S.cpp -v:..\src\mesh\generated" -I=..\protobufs\ ..\protobufs\meshtastic\*.proto
+cd protobufs && ..\nanopb-0.4.9\generator-bin\protoc.exe --experimental_allow_proto3_optional "--nanopb_out=-S.cpp -v:..\src\mesh\generated" -I=..\protobufs\ ..\protobufs\meshtastic\*.proto
diff --git a/bin/regen-protos.sh b/bin/regen-protos.sh
index 2e60784e3..12546bfdc 100755
--- a/bin/regen-protos.sh
+++ b/bin/regen-protos.sh
@@ -2,10 +2,10 @@
set -e
-echo "This script requires https://jpa.kapsi.fi/nanopb/download/ version 0.4.8 to be located in the"
+echo "This script requires https://jpa.kapsi.fi/nanopb/download/ version 0.4.9 to be located in the"
echo "firmware root directory if the following step fails, you should download the correct"
-echo "prebuilt binaries for your computer into nanopb-0.4.8"
+echo "prebuilt binaries for your computer into nanopb-0.4.9"
# the nanopb tool seems to require that the .options file be in the current directory!
cd protobufs
-../nanopb-0.4.8/generator-bin/protoc --experimental_allow_proto3_optional "--nanopb_out=-S.cpp -v:../src/mesh/generated/" -I=../protobufs meshtastic/*.proto
+../nanopb-0.4.9/generator-bin/protoc --experimental_allow_proto3_optional "--nanopb_out=-S.cpp -v:../src/mesh/generated/" -I=../protobufs meshtastic/*.proto
diff --git a/boards/heltec_mesh_node_t114.json b/boards/heltec_mesh_node_t114.json
index 5c97d8c75..2bd306eb9 100644
--- a/boards/heltec_mesh_node_t114.json
+++ b/boards/heltec_mesh_node_t114.json
@@ -5,7 +5,7 @@
},
"core": "nRF5",
"cpu": "cortex-m4",
- "extra_flags": "-DARDUINO_NRF52840_PCA10056 -DNRF52840_XXAA",
+ "extra_flags": "-DHELTEC_T114 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x4405"],
diff --git a/boards/lora-relay-v1.json b/boards/lora-relay-v1.json
deleted file mode 100644
index b390b8404..000000000
--- a/boards/lora-relay-v1.json
+++ /dev/null
@@ -1,47 +0,0 @@
-{
- "build": {
- "arduino": {
- "ldscript": "nrf52840_s140_v6.ld"
- },
- "core": "nRF5",
- "cpu": "cortex-m4",
- "extra_flags": "-DARDUINO_NRF52840_LORA_RELAY_V1 -DNRF52840_XXAA",
- "f_cpu": "64000000L",
- "hwids": [["0x239A", "0x4404"]],
- "usb_product": "LORA_RELAY",
- "mcu": "nrf52840",
- "variant": "lora_relay_v1",
- "variants_dir": "variants",
- "bsp": {
- "name": "adafruit"
- },
- "softdevice": {
- "sd_flags": "-DS140",
- "sd_name": "s140",
- "sd_version": "6.1.1",
- "sd_fwid": "0x00B6"
- },
- "bootloader": {
- "settings_addr": "0xFF000"
- }
- },
- "connectivity": ["bluetooth"],
- "debug": {
- "jlink_device": "nRF52840_xxAA",
- "onboard_tools": ["jlink"],
- "svd_path": "nrf52840.svd",
- "openocd_target": "nrf52840-mdk-rs"
- },
- "frameworks": ["arduino"],
- "name": "Meshtastic Lora Relay V1 (Adafruit BSP)",
- "upload": {
- "maximum_ram_size": 248832,
- "maximum_size": 815104,
- "require_upload_port": true,
- "speed": 115200,
- "protocol": "jlink",
- "protocols": ["jlink", "nrfjprog", "stlink"]
- },
- "url": "https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay",
- "vendor": "BigCorvus"
-}
diff --git a/boards/lora-relay-v2.json b/boards/lora-relay-v2.json
deleted file mode 100644
index 52b775e58..000000000
--- a/boards/lora-relay-v2.json
+++ /dev/null
@@ -1,47 +0,0 @@
-{
- "build": {
- "arduino": {
- "ldscript": "nrf52840_s140_v6.ld"
- },
- "core": "nRF5",
- "cpu": "cortex-m4",
- "extra_flags": "-DARDUINO_NRF52840_LORA_RELAY_V2 -DNRF52840_XXAA",
- "f_cpu": "64000000L",
- "hwids": [["0x239A", "0x4406"]],
- "usb_product": "LORA_RELAY",
- "mcu": "nrf52840",
- "variant": "lora_relay_v2",
- "variants_dir": "variants",
- "bsp": {
- "name": "adafruit"
- },
- "softdevice": {
- "sd_flags": "-DS140",
- "sd_name": "s140",
- "sd_version": "6.1.1",
- "sd_fwid": "0x00B6"
- },
- "bootloader": {
- "settings_addr": "0xFF000"
- }
- },
- "connectivity": ["bluetooth"],
- "debug": {
- "jlink_device": "nRF52840_xxAA",
- "onboard_tools": ["jlink"],
- "svd_path": "nrf52840.svd",
- "openocd_target": "nrf52840-mdk-rs"
- },
- "frameworks": ["arduino"],
- "name": "Meshtastic Lora Relay V1 (Adafruit BSP)",
- "upload": {
- "maximum_ram_size": 248832,
- "maximum_size": 815104,
- "require_upload_port": true,
- "speed": 115200,
- "protocol": "jlink",
- "protocols": ["jlink", "nrfjprog", "stlink"]
- },
- "url": "https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay",
- "vendor": "BigCorvus"
-}
diff --git a/boards/lora_isp4520.json b/boards/lora_isp4520.json
deleted file mode 100644
index 8125aa666..000000000
--- a/boards/lora_isp4520.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
- "build": {
- "arduino": {
- "ldscript": "nrf52832_s132_v6.ld"
- },
- "core": "nRF5",
- "cpu": "cortex-m4",
- "extra_flags": "-DNRF52832_XXAA -DNRF52",
- "f_cpu": "64000000L",
- "mcu": "nrf52832",
- "variant": "lora_isp4520",
- "bsp": {
- "name": "adafruit"
- },
- "softdevice": {
- "sd_flags": "-DS132",
- "sd_name": "s132",
- "sd_version": "6.1.1",
- "sd_fwid": "0x00B7"
- }
- },
- "connectivity": ["bluetooth"],
- "debug": {
- "jlink_device": "nRF52832_xxAA",
- "svd_path": "nrf52.svd",
- "openocd_target": "nrf52840-mdk-rs"
- },
- "frameworks": ["arduino"],
- "name": "lora ISP4520",
- "upload": {
- "maximum_ram_size": 65536,
- "maximum_size": 524288,
- "require_upload_port": true,
- "speed": 115200,
- "protocol": "nrfutil",
- "protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"]
- },
- "url": "",
- "vendor": "PsiSoft"
-}
diff --git a/boards/me25ls01-4y10td.json b/boards/me25ls01-4y10td.json
index 46c526a7c..9e1d63265 100644
--- a/boards/me25ls01-4y10td.json
+++ b/boards/me25ls01-4y10td.json
@@ -5,7 +5,7 @@
},
"core": "nRF5",
"cpu": "cortex-m4",
- "extra_flags": "-DARDUINO_WIO_WM1110 -DNRF52840_XXAA",
+ "extra_flags": "-DME25LS01_4Y10TD -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x8029"],
diff --git a/boards/ms24sf1.json b/boards/ms24sf1.json
index 4e28507da..8356e3012 100644
--- a/boards/ms24sf1.json
+++ b/boards/ms24sf1.json
@@ -5,7 +5,7 @@
},
"core": "nRF5",
"cpu": "cortex-m4",
- "extra_flags": "-DARDUINO_WIO_WM1110 -DNRF52840_XXAA",
+ "extra_flags": "-DMS24SF1 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x8029"],
diff --git a/boards/nordic_pca10059.json b/boards/nordic_pca10059.json
index b99e3c763..6540817a2 100644
--- a/boards/nordic_pca10059.json
+++ b/boards/nordic_pca10059.json
@@ -5,7 +5,7 @@
},
"core": "nRF5",
"cpu": "cortex-m4",
- "extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA",
+ "extra_flags": "-DNORDIC_PCA10059 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x8029"],
diff --git a/boards/nrf52840_dk_modified.json b/boards/nrf52840_dk_modified.json
deleted file mode 100644
index 2932cb4b9..000000000
--- a/boards/nrf52840_dk_modified.json
+++ /dev/null
@@ -1,47 +0,0 @@
-{
- "build": {
- "arduino": {
- "ldscript": "nrf52840_s113_v7.ld"
- },
- "core": "nRF5",
- "cpu": "cortex-m4",
- "extra_flags": "-DARDUINO_NRF52840_PCA10056 -DNRF52840_XXAA",
- "f_cpu": "64000000L",
- "hwids": [["0x239A", "0x4404"]],
- "usb_product": "nrf52840dk",
- "mcu": "nrf52840",
- "variant": "pca10056-rc-clock",
- "variants_dir": "variants",
- "bsp": {
- "name": "adafruit"
- },
- "softdevice": {
- "sd_flags": "-DS140",
- "sd_name": "s140",
- "sd_version": "6.1.1",
- "sd_fwid": "0x00B6"
- },
- "bootloader": {
- "settings_addr": "0xFF000"
- }
- },
- "connectivity": ["bluetooth"],
- "debug": {
- "jlink_device": "nRF52840_xxAA",
- "onboard_tools": ["jlink"],
- "svd_path": "nrf52840.svd",
- "openocd_target": "nrf52840-mdk-rs"
- },
- "frameworks": ["arduino"],
- "name": "A modified NRF52840-DK devboard (Adafruit BSP)",
- "upload": {
- "maximum_ram_size": 248832,
- "maximum_size": 815104,
- "require_upload_port": true,
- "speed": 115200,
- "protocol": "jlink",
- "protocols": ["jlink", "nrfjprog", "stlink"]
- },
- "url": "https://meshtastic.org/",
- "vendor": "Nordic Semi"
-}
diff --git a/boards/ppr.json b/boards/ppr.json
deleted file mode 100644
index 15e3025c0..000000000
--- a/boards/ppr.json
+++ /dev/null
@@ -1,47 +0,0 @@
-{
- "build": {
- "arduino": {
- "ldscript": "nrf52840_s140_v6.ld"
- },
- "core": "nRF5",
- "cpu": "cortex-m4",
- "extra_flags": "-DARDUINO_NRF52840_PPR -DNRF52840_XXAA",
- "f_cpu": "64000000L",
- "hwids": [["0x239A", "0x4403"]],
- "usb_product": "PPR",
- "mcu": "nrf52840",
- "variant": "ppr",
- "variants_dir": "variants",
- "bsp": {
- "name": "adafruit"
- },
- "softdevice": {
- "sd_flags": "-DS140",
- "sd_name": "s140",
- "sd_version": "6.1.1",
- "sd_fwid": "0x00B6"
- },
- "bootloader": {
- "settings_addr": "0xFF000"
- }
- },
- "connectivity": ["bluetooth"],
- "debug": {
- "jlink_device": "nRF52840_xxAA",
- "onboard_tools": ["jlink"],
- "svd_path": "nrf52840.svd",
- "openocd_target": "nrf52840-mdk-rs"
- },
- "frameworks": ["arduino"],
- "name": "Meshtastic PPR (Adafruit BSP)",
- "upload": {
- "maximum_ram_size": 248832,
- "maximum_size": 815104,
- "require_upload_port": true,
- "speed": 115200,
- "protocol": "jlink",
- "protocols": ["jlink", "nrfjprog", "stlink"]
- },
- "url": "https://meshtastic.org/",
- "vendor": "Othernet"
-}
diff --git a/boards/ppr1.json b/boards/ppr1.json
deleted file mode 100644
index 35bf7d1e4..000000000
--- a/boards/ppr1.json
+++ /dev/null
@@ -1,47 +0,0 @@
-{
- "build": {
- "arduino": {
- "ldscript": "nrf52833_s113_v7.ld"
- },
- "core": "nRF5",
- "cpu": "cortex-m4",
- "extra_flags": "-DARDUINO_NRF52833_PPR -DNRF52833_XXAA",
- "f_cpu": "64000000L",
- "hwids": [["0x239A", "0x4406"]],
- "usb_product": "PPR",
- "mcu": "nrf52833",
- "variant": "ppr",
- "variants_dir": "variants",
- "bsp": {
- "name": "adafruit"
- },
- "softdevice": {
- "sd_flags": "-DS113",
- "sd_name": "s113",
- "sd_version": "7.2.0",
- "sd_fwid": "0x00b6"
- },
- "bootloader": {
- "settings_addr": "0xFF000"
- }
- },
- "connectivity": ["bluetooth"],
- "debug": {
- "jlink_device": "nRF52833_xxAA",
- "onboard_tools": ["jlink"],
- "svd_path": "nrf52833.svd",
- "openocd_target": "nrf52840-mdk-rs"
- },
- "frameworks": ["arduino"],
- "name": "Meshtastic PPR1 (Adafruit BSP)",
- "upload": {
- "maximum_ram_size": 248832,
- "maximum_size": 815104,
- "require_upload_port": true,
- "speed": 115200,
- "protocol": "jlink",
- "protocols": ["jlink", "nrfjprog", "stlink"]
- },
- "url": "https://meshtastic.org/",
- "vendor": "Othernet"
-}
diff --git a/boards/seeed-sensecap-indicator.json b/boards/seeed-sensecap-indicator.json
new file mode 100644
index 000000000..3fc57126f
--- /dev/null
+++ b/boards/seeed-sensecap-indicator.json
@@ -0,0 +1,42 @@
+{
+ "build": {
+ "arduino": {
+ "ldscript": "esp32s3_out.ld",
+ "partitions": "default_8MB.csv",
+ "memory_type": "qio_opi"
+ },
+ "core": "esp32",
+ "extra_flags": [
+ "-DBOARD_HAS_PSRAM",
+ "-DARDUINO_USB_CDC_ON_BOOT=0",
+ "-DARDUINO_USB_MODE=1",
+ "-DARDUINO_RUNNING_CORE=1",
+ "-DARDUINO_EVENT_RUNNING_CORE=1"
+ ],
+ "f_cpu": "240000000L",
+ "f_flash": "80000000L",
+ "flash_mode": "qio",
+ "hwids": [["0x1A86", "0x7523"]],
+ "mcu": "esp32s3",
+ "variant": "esp32s3r8"
+ },
+ "connectivity": ["wifi", "bluetooth", "lora"],
+ "debug": {
+ "default_tool": "esp-builtin",
+ "onboard_tools": ["esp-builtin"],
+ "openocd_target": "esp32s3.cfg"
+ },
+ "frameworks": ["arduino"],
+ "name": "Seeed Studio SenseCAP Indicator",
+ "upload": {
+ "flash_size": "8MB",
+ "maximum_ram_size": 327680,
+ "maximum_size": 8388608,
+ "require_upload_port": true,
+ "use_1200bps_touch": true,
+ "wait_for_upload_port": true,
+ "speed": 921600
+ },
+ "url": "https://www.seeedstudio.com/Indicator-for-Meshtastic.html",
+ "vendor": "Seeed Studio"
+}
diff --git a/boards/seeed-xiao-s3.json b/boards/seeed-xiao-s3.json
new file mode 100644
index 000000000..0b7b432a0
--- /dev/null
+++ b/boards/seeed-xiao-s3.json
@@ -0,0 +1,41 @@
+{
+ "build": {
+ "arduino": {
+ "ldscript": "esp32s3_out.ld",
+ "memory_type": "qio_opi"
+ },
+ "core": "esp32",
+ "extra_flags": [
+ "-DBOARD_HAS_PSRAM",
+ "-DARDUINO_USB_CDC_ON_BOOT=1",
+ "-DARDUINO_USB_MODE=0",
+ "-DARDUINO_RUNNING_CORE=1",
+ "-DARDUINO_EVENT_RUNNING_CORE=0"
+ ],
+ "f_cpu": "240000000L",
+ "f_flash": "80000000L",
+ "flash_mode": "qio",
+ "hwids": [["0x2886", "0x0059"]],
+ "mcu": "esp32s3",
+ "variant": "seeed-xiao-s3"
+ },
+ "connectivity": ["wifi", "bluetooth", "lora"],
+ "debug": {
+ "default_tool": "esp-builtin",
+ "onboard_tools": ["esp-builtin"],
+ "openocd_target": "esp32s3.cfg"
+ },
+ "frameworks": ["arduino", "espidf"],
+ "name": "seeed-xiao-s3",
+ "upload": {
+ "flash_size": "8MB",
+ "maximum_ram_size": 8388608,
+ "maximum_size": 8388608,
+ "use_1200bps_touch": true,
+ "wait_for_upload_port": true,
+ "require_upload_port": true,
+ "speed": 921600
+ },
+ "url": "https://www.seeedstudio.com/XIAO-ESP32S3-Sense-p-5639.html",
+ "vendor": "Seeed Studio"
+}
diff --git a/images/compass.png b/images/compass.png
index 8639dde52..c4e5b589b 100644
Binary files a/images/compass.png and b/images/compass.png differ
diff --git a/images/face-24px.svg b/images/face-24px.svg
index 293468664..08e8f5ffc 100644
--- a/images/face-24px.svg
+++ b/images/face-24px.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/images/face.png b/images/face.png
index 036e29f73..eb6b91e13 100644
Binary files a/images/face.png and b/images/face.png differ
diff --git a/images/location_searching-24px.svg b/images/location_searching-24px.svg
index 7f72a31bc..abef0ef78 100644
--- a/images/location_searching-24px.svg
+++ b/images/location_searching-24px.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/images/pin.png b/images/pin.png
index 112a7ce8e..3c91a726c 100644
Binary files a/images/pin.png and b/images/pin.png differ
diff --git a/images/room-24px.svg b/images/room-24px.svg
index 79a4807e7..48e55ab80 100644
--- a/images/room-24px.svg
+++ b/images/room-24px.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/images/textsms-24px.svg b/images/textsms-24px.svg
index 4455f047e..84c4fdcc1 100644
--- a/images/textsms-24px.svg
+++ b/images/textsms-24px.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/meshtestic b/meshtestic
index 31ee3d90c..dcac7e567 160000
--- a/meshtestic
+++ b/meshtestic
@@ -1 +1 @@
-Subproject commit 31ee3d90c8bef61e835c3271be2c7cda8c4a5cc2
+Subproject commit dcac7e5673005f4d8a2b1f0f6e06877b689d7519
diff --git a/platformio.ini b/platformio.ini
index 87eb0afb7..e437b572e 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -17,11 +17,9 @@ default_envs = tbeam
;default_envs = tlora-v2-1-1_6
;default_envs = tlora-v2-1-1_6-tcxo
;default_envs = tlora-t3s3-v1
-;default_envs = lora-relay-v1 # nrf board
;default_envs = t-echo
;default_envs = canaryone
-;default_envs = nrf52840dk-geeksville
-;default_envs = native # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here
+;default_envs = native
;default_envs = nano-g1
;default_envs = pca10059_diy_eink
;default_envs = meshtastic-diy-v1
@@ -29,6 +27,7 @@ default_envs = tbeam
;default_envs = meshtastic-dr-dev
;default_envs = m5stack-coreink
;default_envs = rak4631
+;default_envs = rak4631_eth_gw
;default_envs = rak2560
;default_envs = rak10701
;default_envs = wio-e5
@@ -60,41 +59,42 @@ build_flags = -Wno-missing-field-initializers
-DUSE_THREAD_NAMES
-DTINYGPS_OPTION_NO_CUSTOM_FIELDS
-DPB_ENABLE_MALLOC=1
- -DRADIOLIB_EXCLUDE_CC1101
- -DRADIOLIB_EXCLUDE_NRF24
- -DRADIOLIB_EXCLUDE_RF69
- -DRADIOLIB_EXCLUDE_SX1231
- -DRADIOLIB_EXCLUDE_SX1233
- -DRADIOLIB_EXCLUDE_SI443X
- -DRADIOLIB_EXCLUDE_RFM2X
- -DRADIOLIB_EXCLUDE_AFSK
- -DRADIOLIB_EXCLUDE_BELL
- -DRADIOLIB_EXCLUDE_HELLSCHREIBER
- -DRADIOLIB_EXCLUDE_MORSE
- -DRADIOLIB_EXCLUDE_RTTY
- -DRADIOLIB_EXCLUDE_SSTV
- -DRADIOLIB_EXCLUDE_AX25
- -DRADIOLIB_EXCLUDE_DIRECT_RECEIVE
- -DRADIOLIB_EXCLUDE_BELL
- -DRADIOLIB_EXCLUDE_PAGER
- -DRADIOLIB_EXCLUDE_FSK4
- -DRADIOLIB_EXCLUDE_APRS
- -DRADIOLIB_EXCLUDE_LORAWAN
+ -DRADIOLIB_EXCLUDE_CC1101=1
+ -DRADIOLIB_EXCLUDE_NRF24=1
+ -DRADIOLIB_EXCLUDE_RF69=1
+ -DRADIOLIB_EXCLUDE_SX1231=1
+ -DRADIOLIB_EXCLUDE_SX1233=1
+ -DRADIOLIB_EXCLUDE_SI443X=1
+ -DRADIOLIB_EXCLUDE_RFM2X=1
+ -DRADIOLIB_EXCLUDE_AFSK=1
+ -DRADIOLIB_EXCLUDE_BELL=1
+ -DRADIOLIB_EXCLUDE_HELLSCHREIBER=1
+ -DRADIOLIB_EXCLUDE_MORSE=1
+ -DRADIOLIB_EXCLUDE_RTTY=1
+ -DRADIOLIB_EXCLUDE_SSTV=1
+ -DRADIOLIB_EXCLUDE_AX25=1
+ -DRADIOLIB_EXCLUDE_DIRECT_RECEIVE=1
+ -DRADIOLIB_EXCLUDE_BELL=1
+ -DRADIOLIB_EXCLUDE_PAGER=1
+ -DRADIOLIB_EXCLUDE_FSK4=1
+ -DRADIOLIB_EXCLUDE_APRS=1
+ -DRADIOLIB_EXCLUDE_LORAWAN=1
-DMESHTASTIC_EXCLUDE_DROPZONE=1
+ -DMESHTASTIC_EXCLUDE_REMOTEHARDWARE=1
+ #-DBUILD_EPOCH=$UNIX_TIME
;-D OLED_PL
monitor_speed = 115200
monitor_filters = direct
lib_deps =
-; jgromes/RadioLib@~6.6.0
- https://github.com/jgromes/RadioLib.git#3115fc2d6700a9aee05888791ac930a910f2628f
+ jgromes/RadioLib@~7.0.2
https://github.com/meshtastic/esp8266-oled-ssd1306.git#e16cee124fe26490cb14880c679321ad8ac89c95 ; ESP8266_SSD1306
- https://github.com/mathertel/OneButton@~2.6.1 ; OneButton library for non-blocking button debounce
+ mathertel/OneButton@~2.6.1 ; OneButton library for non-blocking button debounce
https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159
https://github.com/meshtastic/TinyGPSPlus.git#71a82db35f3b973440044c476d4bcdc673b104f4
https://github.com/meshtastic/ArduinoThread.git#1ae8778c85d0a2a729f989e0b1e7d7c4dc84eef0
- nanopb/Nanopb@^0.4.8
+ nanopb/Nanopb@^0.4.9
erriez/ErriezCRC32@^1.0.1
; Used for the code analysis in PIO Home / Inspect
@@ -127,7 +127,7 @@ lib_deps =
; (not included in native / portduino)
[environmental_base]
lib_deps =
- adafruit/Adafruit BusIO@^1.15.0
+ adafruit/Adafruit BusIO@^1.16.1
adafruit/Adafruit Unified Sensor@^1.1.11
adafruit/Adafruit BMP280 Library@^2.6.8
adafruit/Adafruit BMP085 Library@^1.2.4
@@ -136,28 +136,31 @@ lib_deps =
adafruit/Adafruit MCP9808 Library@^2.0.0
adafruit/Adafruit INA260 Library@^1.5.0
adafruit/Adafruit INA219@^1.2.0
+ adafruit/Adafruit MAX1704X@^1.0.3
adafruit/Adafruit SHTC3 Library@^1.0.0
adafruit/Adafruit LPS2X@^2.0.4
adafruit/Adafruit SHT31 Library@^2.2.2
- adafruit/Adafruit PM25 AQI Sensor@^1.0.6
+ adafruit/Adafruit PM25 AQI Sensor@^1.1.1
adafruit/Adafruit MPU6050@^2.2.4
- adafruit/Adafruit LIS3DH@^1.2.4
+ adafruit/Adafruit LIS3DH@^1.3.0
adafruit/Adafruit AHTX0@^2.0.5
adafruit/Adafruit LSM6DS@^4.7.2
adafruit/Adafruit VEML7700 Library@^2.1.6
adafruit/Adafruit SHT4x Library@^1.0.4
adafruit/Adafruit TSL2591 Library@^1.4.5
sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@^1.0.5
+ sparkfun/SparkFun 9DoF IMU Breakout - ICM 20948 - Arduino Library@^1.2.13
ClosedCube OPT3001@^1.1.2
emotibit/EmotiBit MLX90632@^1.0.8
dfrobot/DFRobot_RTU@^1.0.3
-
+ sparkfun/SparkFun MAX3010x Pulse and Proximity Sensor Library@^1.1.2
+ adafruit/Adafruit MLX90614 Library@^2.1.5
https://github.com/boschsensortec/Bosch-BSEC2-Library#v1.7.2502
boschsensortec/BME68x Sensor Library@^1.1.40407
- https://github.com/KodinLanewave/INA3221@^1.0.0
- lewisxhe/SensorLib@^0.2.0
+ https://github.com/KodinLanewave/INA3221@^1.0.1
+ lewisxhe/SensorLib@0.2.0
mprograms/QMC5883LCompass@^1.2.0
-
- https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee
\ No newline at end of file
+ https://github.com/meshtastic/DFRobot_LarkWeatherStation#4de3a9cadef0f6a5220a8a906cf9775b02b0040d
+ https://github.com/gjelsoe/STK8xxx-Accelerometer.git#v0.1.1
diff --git a/protobufs b/protobufs
index 005d7231e..c9ae7fd47 160000
--- a/protobufs
+++ b/protobufs
@@ -1 +1 @@
-Subproject commit 005d7231e59fdbadd74065230132b9ee176f2c87
+Subproject commit c9ae7fd478bffe5f954b30de6cb140821fe9ff52
diff --git a/src/AccelerometerThread.h b/src/AccelerometerThread.h
deleted file mode 100644
index c2910007e..000000000
--- a/src/AccelerometerThread.h
+++ /dev/null
@@ -1,297 +0,0 @@
-#pragma once
-#include "configuration.h"
-
-#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
-
-#include "PowerFSM.h"
-#include "concurrency/OSThread.h"
-#include "main.h"
-#include "power.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#ifdef RAK_4631
-#include "Fusion/Fusion.h"
-#include "graphics/Screen.h"
-#include "graphics/ScreenFonts.h"
-#include
-#endif
-
-#define ACCELEROMETER_CHECK_INTERVAL_MS 100
-#define ACCELEROMETER_CLICK_THRESHOLD 40
-
-static inline int readRegister(uint8_t address, uint8_t reg, uint8_t *data, uint8_t len)
-{
- Wire.beginTransmission(address);
- Wire.write(reg);
- Wire.endTransmission();
- Wire.requestFrom((uint8_t)address, (uint8_t)len);
- uint8_t i = 0;
- while (Wire.available()) {
- data[i++] = Wire.read();
- }
- return 0; // Pass
-}
-
-static inline int writeRegister(uint8_t address, uint8_t reg, uint8_t *data, uint8_t len)
-{
- Wire.beginTransmission(address);
- Wire.write(reg);
- Wire.write(data, len);
- return (0 != Wire.endTransmission());
-}
-
-class AccelerometerThread : public concurrency::OSThread
-{
- public:
- explicit AccelerometerThread(ScanI2C::DeviceType type) : OSThread("AccelerometerThread")
- {
- if (accelerometer_found.port == ScanI2C::I2CPort::NO_I2C) {
- LOG_DEBUG("AccelerometerThread disabling due to no sensors found\n");
- disable();
- return;
- }
- acceleremoter_type = type;
-#ifndef RAK_4631
- if (!config.display.wake_on_tap_or_motion && !config.device.double_tap_as_button_press) {
- LOG_DEBUG("AccelerometerThread disabling due to no interested configurations\n");
- disable();
- return;
- }
-#endif
- init();
- }
-
- void start()
- {
- init();
- setIntervalFromNow(0);
- };
-
- protected:
- int32_t runOnce() override
- {
- canSleep = true; // Assume we should not keep the board awake
-
- if (acceleremoter_type == ScanI2C::DeviceType::MPU6050 && mpu.getMotionInterruptStatus()) {
- wakeScreen();
- } else if (acceleremoter_type == ScanI2C::DeviceType::LIS3DH && lis.getClick() > 0) {
- uint8_t click = lis.getClick();
- if (!config.device.double_tap_as_button_press) {
- wakeScreen();
- }
-
- if (config.device.double_tap_as_button_press && (click & 0x20)) {
- buttonPress();
- return 500;
- }
- } else if (acceleremoter_type == ScanI2C::DeviceType::BMA423 && bmaSensor.readIrqStatus() != DEV_WIRE_NONE) {
- if (bmaSensor.isTilt() || bmaSensor.isDoubleTap()) {
- wakeScreen();
- return 500;
- }
-#ifdef RAK_4631
- } else if (acceleremoter_type == ScanI2C::DeviceType::BMX160) {
- sBmx160SensorData_t magAccel;
- sBmx160SensorData_t gAccel;
-
- /* Get a new sensor event */
- bmx160.getAllData(&magAccel, NULL, &gAccel);
-
- // expirimental calibrate routine. Limited to between 10 and 30 seconds after boot
- if (millis() > 12 * 1000 && millis() < 30 * 1000) {
- if (!showingScreen) {
- showingScreen = true;
- screen->startAlert((FrameCallback)drawFrameCalibration);
- }
- if (magAccel.x > highestX)
- highestX = magAccel.x;
- if (magAccel.x < lowestX)
- lowestX = magAccel.x;
- if (magAccel.y > highestY)
- highestY = magAccel.y;
- if (magAccel.y < lowestY)
- lowestY = magAccel.y;
- if (magAccel.z > highestZ)
- highestZ = magAccel.z;
- if (magAccel.z < lowestZ)
- lowestZ = magAccel.z;
- } else if (showingScreen && millis() >= 30 * 1000) {
- showingScreen = false;
- screen->endAlert();
- }
-
- int highestRealX = highestX - (highestX + lowestX) / 2;
-
- magAccel.x -= (highestX + lowestX) / 2;
- magAccel.y -= (highestY + lowestY) / 2;
- magAccel.z -= (highestZ + lowestZ) / 2;
- FusionVector ga, ma;
- ga.axis.x = -gAccel.x; // default location for the BMX160 is on the rear of the board
- ga.axis.y = -gAccel.y;
- ga.axis.z = gAccel.z;
- ma.axis.x = -magAccel.x;
- ma.axis.y = -magAccel.y;
- ma.axis.z = magAccel.z * 3;
-
- // If we're set to one of the inverted positions
- if (config.display.compass_orientation > meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270) {
- ma = FusionAxesSwap(ma, FusionAxesAlignmentNXNYPZ);
- ga = FusionAxesSwap(ga, FusionAxesAlignmentNXNYPZ);
- }
-
- float heading = FusionCompassCalculateHeading(FusionConventionNed, ga, ma);
-
- switch (config.display.compass_orientation) {
- case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_0_INVERTED:
- case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_0:
- break;
- case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_90:
- case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_90_INVERTED:
- heading += 90;
- break;
- case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_180:
- case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_180_INVERTED:
- heading += 180;
- break;
- case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270:
- case meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270_INVERTED:
- heading += 270;
- break;
- }
-
- screen->setHeading(heading);
-
-#endif
- } else if (acceleremoter_type == ScanI2C::DeviceType::LSM6DS3 && lsm.shake()) {
- wakeScreen();
- return 500;
- }
-
- return ACCELEROMETER_CHECK_INTERVAL_MS;
- }
-
- private:
- void init()
- {
- LOG_DEBUG("AccelerometerThread initializing\n");
-
- if (acceleremoter_type == ScanI2C::DeviceType::MPU6050 && mpu.begin(accelerometer_found.address)) {
- LOG_DEBUG("MPU6050 initializing\n");
- // setup motion detection
- mpu.setHighPassFilter(MPU6050_HIGHPASS_0_63_HZ);
- mpu.setMotionDetectionThreshold(1);
- mpu.setMotionDetectionDuration(20);
- mpu.setInterruptPinLatch(true); // Keep it latched. Will turn off when reinitialized.
- mpu.setInterruptPinPolarity(true);
- } else if (acceleremoter_type == ScanI2C::DeviceType::LIS3DH && lis.begin(accelerometer_found.address)) {
- LOG_DEBUG("LIS3DH initializing\n");
- lis.setRange(LIS3DH_RANGE_2_G);
- // Adjust threshold, higher numbers are less sensitive
- lis.setClick(config.device.double_tap_as_button_press ? 2 : 1, ACCELEROMETER_CLICK_THRESHOLD);
- } else if (acceleremoter_type == ScanI2C::DeviceType::BMA423 &&
- bmaSensor.begin(accelerometer_found.address, &readRegister, &writeRegister)) {
- LOG_DEBUG("BMA423 initializing\n");
- bmaSensor.configAccelerometer(bmaSensor.RANGE_2G, bmaSensor.ODR_100HZ, bmaSensor.BW_NORMAL_AVG4,
- bmaSensor.PERF_CONTINUOUS_MODE);
- bmaSensor.enableAccelerometer();
- bmaSensor.configInterrupt(BMA4_LEVEL_TRIGGER, BMA4_ACTIVE_HIGH, BMA4_PUSH_PULL, BMA4_OUTPUT_ENABLE,
- BMA4_INPUT_DISABLE);
-
-#ifdef BMA423_INT
- pinMode(BMA4XX_INT, INPUT);
- attachInterrupt(
- BMA4XX_INT,
- [] {
- // Set interrupt to set irq value to true
- BMA_IRQ = true;
- },
- RISING); // Select the interrupt mode according to the actual circuit
-#endif
-
-#ifdef T_WATCH_S3
- // Need to raise the wrist function, need to set the correct axis
- bmaSensor.setReampAxes(bmaSensor.REMAP_TOP_LAYER_RIGHT_CORNER);
-#else
- bmaSensor.setReampAxes(bmaSensor.REMAP_BOTTOM_LAYER_BOTTOM_LEFT_CORNER);
-#endif
- // bmaSensor.enableFeature(bmaSensor.FEATURE_STEP_CNTR, true);
- bmaSensor.enableFeature(bmaSensor.FEATURE_TILT, true);
- bmaSensor.enableFeature(bmaSensor.FEATURE_WAKEUP, true);
- // bmaSensor.resetPedometer();
-
- // Turn on feature interrupt
- bmaSensor.enablePedometerIRQ();
- bmaSensor.enableTiltIRQ();
- // It corresponds to isDoubleClick interrupt
- bmaSensor.enableWakeupIRQ();
-#ifdef RAK_4631
- } else if (acceleremoter_type == ScanI2C::DeviceType::BMX160 && bmx160.begin()) {
- bmx160.ODR_Config(BMX160_ACCEL_ODR_100HZ, BMX160_GYRO_ODR_100HZ); // set output data rate
-
-#endif
- } else if (acceleremoter_type == ScanI2C::DeviceType::LSM6DS3 && lsm.begin_I2C(accelerometer_found.address)) {
- LOG_DEBUG("LSM6DS3 initializing\n");
- // Default threshold of 2G, less sensitive options are 4, 8 or 16G
- lsm.setAccelRange(LSM6DS_ACCEL_RANGE_2_G);
-#ifndef LSM6DS3_WAKE_THRESH
-#define LSM6DS3_WAKE_THRESH 20
-#endif
- lsm.enableWakeup(config.display.wake_on_tap_or_motion, 1, LSM6DS3_WAKE_THRESH);
- // Duration is number of occurances needed to trigger, higher threshold is less sensitive
- }
- }
- void wakeScreen()
- {
- if (powerFSM.getState() == &stateDARK) {
- LOG_INFO("Tap or motion detected. Turning on screen\n");
- powerFSM.trigger(EVENT_INPUT);
- }
- }
-
- void buttonPress()
- {
- LOG_DEBUG("Double-tap detected. Firing button press\n");
- powerFSM.trigger(EVENT_PRESS);
- }
-
- ScanI2C::DeviceType acceleremoter_type;
- Adafruit_MPU6050 mpu;
- Adafruit_LIS3DH lis;
- Adafruit_LSM6DS3TRC lsm;
- SensorBMA423 bmaSensor;
- bool BMA_IRQ = false;
-#ifdef RAK_4631
- bool showingScreen = false;
- RAK_BMX160 bmx160;
- float highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0;
-
- static void drawFrameCalibration(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
- {
- int x_offset = display->width() / 2;
- int y_offset = display->height() <= 80 ? 0 : 32;
- display->setTextAlignment(TEXT_ALIGN_LEFT);
- display->setFont(FONT_MEDIUM);
- display->drawString(x, y, "Calibrating\nCompass");
- int16_t compassX = 0, compassY = 0;
- uint16_t compassDiam = graphics::Screen::getCompassDiam(display->getWidth(), display->getHeight());
-
- // coordinates for the center of the compass/circle
- if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
- compassX = x + display->getWidth() - compassDiam / 2 - 5;
- compassY = y + display->getHeight() / 2;
- } else {
- compassX = x + display->getWidth() - compassDiam / 2 - 5;
- compassY = y + FONT_HEIGHT_SMALL + (display->getHeight() - FONT_HEIGHT_SMALL) / 2;
- }
- display->drawCircle(compassX, compassY, compassDiam / 2);
- screen->drawCompassNorth(display, compassX, compassY, screen->getHeading() * PI / 180);
- }
-#endif
-};
-
-#endif
\ No newline at end of file
diff --git a/src/ButtonThread.cpp b/src/ButtonThread.cpp
index 5351fa049..9e6ef55f5 100644
--- a/src/ButtonThread.cpp
+++ b/src/ButtonThread.cpp
@@ -124,6 +124,11 @@ int32_t ButtonThread::runOnce()
switch (btnEvent) {
case BUTTON_EVENT_PRESSED: {
LOG_BUTTON("press!\n");
+ // If a nag notification is running, stop it and prevent other actions
+ if (moduleConfig.external_notification.enabled && (externalNotificationModule->nagCycleCutoff != UINT32_MAX)) {
+ externalNotificationModule->stopNow();
+ return 50;
+ }
#ifdef BUTTON_PIN
if (((config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN) !=
moduleConfig.canned_message.inputbroker_pin_press) ||
diff --git a/src/DebugConfiguration.cpp b/src/DebugConfiguration.cpp
index 23b140daf..1c081ae29 100644
--- a/src/DebugConfiguration.cpp
+++ b/src/DebugConfiguration.cpp
@@ -97,12 +97,14 @@ Syslog &Syslog::logMask(uint8_t priMask)
void Syslog::enable()
{
+ this->_client->begin(this->_port);
this->_enabled = true;
}
void Syslog::disable()
{
this->_enabled = false;
+ this->_client->stop();
}
bool Syslog::isEnabled()
@@ -193,4 +195,4 @@ inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *mess
return true;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/src/FSCommon.cpp b/src/FSCommon.cpp
index 0f78f3e8b..bfe04fa49 100644
--- a/src/FSCommon.cpp
+++ b/src/FSCommon.cpp
@@ -375,8 +375,8 @@ void setupSDCard()
}
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
- LOG_DEBUG("SD Card Size: %lluMB\n", cardSize);
- LOG_DEBUG("Total space: %llu MB\n", SD.totalBytes() / (1024 * 1024));
- LOG_DEBUG("Used space: %llu MB\n", SD.usedBytes() / (1024 * 1024));
+ LOG_DEBUG("SD Card Size: %lu MB\n", (uint32_t)cardSize);
+ LOG_DEBUG("Total space: %lu MB\n", (uint32_t)(SD.totalBytes() / (1024 * 1024)));
+ LOG_DEBUG("Used space: %lu MB\n", (uint32_t)(SD.usedBytes() / (1024 * 1024)));
#endif
}
\ No newline at end of file
diff --git a/src/Power.cpp b/src/Power.cpp
index 61a6c987d..2fe28633a 100644
--- a/src/Power.cpp
+++ b/src/Power.cpp
@@ -13,6 +13,7 @@
#include "power.h"
#include "NodeDB.h"
#include "PowerFSM.h"
+#include "Throttle.h"
#include "buzz/buzz.h"
#include "configuration.h"
#include "main.h"
@@ -30,6 +31,7 @@
#if HAS_WIFI
#include
#endif
+
#endif
#ifndef DELAY_FOREVER
@@ -75,6 +77,15 @@ INA219Sensor ina219Sensor;
INA3221Sensor ina3221Sensor;
#endif
+#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
+#include "modules/Telemetry/Sensor/MAX17048Sensor.h"
+#include
+extern std::pair nodeTelemetrySensorsMap[_meshtastic_TelemetrySensorType_MAX + 1];
+#if HAS_TELEMETRY && (!MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR || !MESHTASTIC_EXCLUDE_POWER_TELEMETRY)
+MAX17048Sensor max17048Sensor;
+#endif
+#endif
+
#if HAS_RAKPROT && !defined(ARCH_PORTDUINO)
RAK9154Sensor rak9154Sensor;
#endif
@@ -136,6 +147,8 @@ using namespace meshtastic;
*/
static HasBatteryLevel *batteryLevel; // Default to NULL for no battery level sensor
+#ifdef BATTERY_PIN
+
static void adcEnable()
{
#ifdef ADC_CTRL // enable adc voltage divider when we need to read
@@ -160,11 +173,14 @@ static void adcDisable()
#endif
}
+#endif
+
/**
* A simple battery level sensor that assumes the battery voltage is attached via a voltage-divider to an analog input
*/
class AnalogBatteryLevel : public HasBatteryLevel
{
+ public:
/**
* Battery state of charge, from 0 to 100 or -1 for unknown
*/
@@ -244,7 +260,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
config.power.adc_multiplier_override > 0 ? config.power.adc_multiplier_override : ADC_MULTIPLIER;
// Do not call analogRead() often.
const uint32_t min_read_interval = 5000;
- if (millis() - last_read_time_ms > min_read_interval) {
+ if (!Throttle::isWithinTimespanMs(last_read_time_ms, min_read_interval)) {
last_read_time_ms = millis();
uint32_t raw = 0;
@@ -551,7 +567,12 @@ bool Power::analogInit()
*/
bool Power::setup()
{
- bool found = axpChipInit() || analogInit();
+ // initialise one power sensor (only)
+ bool found = axpChipInit();
+ if (!found)
+ found = lipoInit();
+ if (!found)
+ found = analogInit();
#ifdef NRF_APM
found = true;
@@ -567,7 +588,7 @@ void Power::shutdown()
{
LOG_INFO("Shutting down\n");
-#if defined(ARCH_NRF52) || defined(ARCH_ESP32)
+#if defined(ARCH_NRF52) || defined(ARCH_ESP32) || defined(ARCH_RP2040)
#ifdef PIN_LED1
ledOff(PIN_LED1);
#endif
@@ -950,7 +971,6 @@ bool Power::axpChipInit()
PMU->enableVbusVoltageMeasure();
PMU->enableBattVoltageMeasure();
- LOG_DEBUG("=======================================================================\n");
if (PMU->isChannelAvailable(XPOWERS_DCDC1)) {
LOG_DEBUG("DC1 : %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC1) ? "+" : "-",
PMU->getPowerChannelVoltage(XPOWERS_DCDC1));
@@ -999,7 +1019,6 @@ bool Power::axpChipInit()
LOG_DEBUG("BLDO2: %s Voltage:%u mV \n", PMU->isPowerChannelEnable(XPOWERS_BLDO2) ? "+" : "-",
PMU->getPowerChannelVoltage(XPOWERS_BLDO2));
}
- LOG_DEBUG("=======================================================================\n");
// We can safely ignore this approach for most (or all) boards because MCU turned off
// earlier than battery discharged to 2.6V.
@@ -1042,4 +1061,82 @@ bool Power::axpChipInit()
#else
return false;
#endif
-}
\ No newline at end of file
+}
+
+#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
+
+/**
+ * Wrapper class for an I2C MAX17048 Lipo battery sensor.
+ */
+class LipoBatteryLevel : public HasBatteryLevel
+{
+ private:
+ MAX17048Singleton *max17048 = nullptr;
+
+ public:
+ /**
+ * Init the I2C MAX17048 Lipo battery level sensor
+ */
+ bool runOnce()
+ {
+ if (max17048 == nullptr) {
+ max17048 = MAX17048Singleton::GetInstance();
+ }
+
+ // try to start if the sensor has been detected
+ if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MAX17048].first != 0) {
+ return max17048->runOnce(nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MAX17048].second);
+ }
+ return false;
+ }
+
+ /**
+ * Battery state of charge, from 0 to 100 or -1 for unknown
+ */
+ virtual int getBatteryPercent() override { return max17048->getBusBatteryPercent(); }
+
+ /**
+ * The raw voltage of the battery in millivolts, or NAN if unknown
+ */
+ virtual uint16_t getBattVoltage() override { return max17048->getBusVoltageMv(); }
+
+ /**
+ * return true if there is a battery installed in this unit
+ */
+ virtual bool isBatteryConnect() override { return max17048->isBatteryConnected(); }
+
+ /**
+ * return true if there is an external power source detected
+ */
+ virtual bool isVbusIn() override { return max17048->isExternallyPowered(); }
+
+ /**
+ * return true if the battery is currently charging
+ */
+ virtual bool isCharging() override { return max17048->isBatteryCharging(); }
+};
+
+LipoBatteryLevel lipoLevel;
+
+/**
+ * Init the Lipo battery level sensor
+ */
+bool Power::lipoInit()
+{
+ bool result = lipoLevel.runOnce();
+ LOG_DEBUG("Power::lipoInit lipo sensor is %s\n", result ? "ready" : "not ready yet");
+ if (!result)
+ return false;
+ batteryLevel = &lipoLevel;
+ return true;
+}
+
+#else
+/**
+ * The Lipo battery level sensor is unavailable - default to AnalogBatteryLevel
+ */
+bool Power::lipoInit()
+{
+ return false;
+}
+#endif
\ No newline at end of file
diff --git a/src/SafeFile.h b/src/SafeFile.h
index 7088074cd..61361d312 100644
--- a/src/SafeFile.h
+++ b/src/SafeFile.h
@@ -24,7 +24,7 @@
class SafeFile : public Print
{
public:
- SafeFile(char const *filepath, bool fullAtomic = false);
+ explicit SafeFile(char const *filepath, bool fullAtomic = false);
virtual size_t write(uint8_t);
virtual size_t write(const uint8_t *buffer, size_t size);
diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp
index b911e15da..fe6ccdefe 100644
--- a/src/SerialConsole.cpp
+++ b/src/SerialConsole.cpp
@@ -1,6 +1,8 @@
#include "SerialConsole.h"
+#include "Default.h"
#include "NodeDB.h"
#include "PowerFSM.h"
+#include "Throttle.h"
#include "configuration.h"
#include "time.h"
@@ -44,10 +46,11 @@ SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), con
Port.setRX(SERIAL2_RX);
#endif
Port.begin(SERIAL_BAUD);
-#if defined(ARCH_NRF52) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(ARCH_RP2040)
+#if defined(ARCH_NRF52) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(ARCH_RP2040) || \
+ defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6)
time_t timeout = millis();
while (!Port) {
- if ((millis() - timeout) < 5000) {
+ if (Throttle::isWithinTimespanMs(timeout, FIVE_SECONDS_MS)) {
delay(100);
} else {
break;
@@ -72,8 +75,7 @@ void SerialConsole::flush()
// For the serial port we can't really detect if any client is on the other side, so instead just look for recent messages
bool SerialConsole::checkIsConnected()
{
- uint32_t now = millis();
- return (now - lastContactMsec) < SERIAL_CONNECTION_TIMEOUT;
+ return Throttle::isWithinTimespanMs(lastContactMsec, SERIAL_CONNECTION_TIMEOUT);
}
/**
@@ -120,4 +122,4 @@ void SerialConsole::log_to_serial(const char *logLevel, const char *format, va_l
emitLogRecord(ll, thread ? thread->ThreadName.c_str() : "", format, arg);
} else
RedirectablePrint::log_to_serial(logLevel, format, arg);
-}
+}
\ No newline at end of file
diff --git a/src/airtime.cpp b/src/airtime.cpp
index 2702ee2bf..fcda05468 100644
--- a/src/airtime.cpp
+++ b/src/airtime.cpp
@@ -13,17 +13,17 @@ void AirTime::logAirtime(reportTypes reportType, uint32_t airtime_ms)
{
if (reportType == TX_LOG) {
- LOG_DEBUG("AirTime - Packet transmitted : %ums\n", airtime_ms);
+ LOG_DEBUG("Packet transmitted : %ums\n", airtime_ms);
this->airtimes.periodTX[0] = this->airtimes.periodTX[0] + airtime_ms;
air_period_tx[0] = air_period_tx[0] + airtime_ms;
this->utilizationTX[this->getPeriodUtilHour()] = this->utilizationTX[this->getPeriodUtilHour()] + airtime_ms;
} else if (reportType == RX_LOG) {
- LOG_DEBUG("AirTime - Packet received : %ums\n", airtime_ms);
+ LOG_DEBUG("Packet received : %ums\n", airtime_ms);
this->airtimes.periodRX[0] = this->airtimes.periodRX[0] + airtime_ms;
air_period_rx[0] = air_period_rx[0] + airtime_ms;
} else if (reportType == RX_ALL_LOG) {
- LOG_DEBUG("AirTime - Packet received (noise?) : %ums\n", airtime_ms);
+ LOG_DEBUG("Packet received (noise?) : %ums\n", airtime_ms);
this->airtimes.periodRX_ALL[0] = this->airtimes.periodRX_ALL[0] + airtime_ms;
}
diff --git a/src/configuration.h b/src/configuration.h
index 047dbd727..729d6b046 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -69,6 +69,9 @@ along with this program. If not, see .
#ifndef RTC_DATA_ATTR
#define RTC_DATA_ATTR
#endif
+#ifndef EXT_RAM_BSS_ATTR
+#define EXT_RAM_BSS_ATTR EXT_RAM_ATTR
+#endif
// -----------------------------------------------------------------------------
// Regulatory overrides
@@ -122,9 +125,11 @@ along with this program. If not, see .
#define INA_ADDR_ALTERNATE 0x41
#define INA_ADDR_WAVESHARE_UPS 0x43
#define INA3221_ADDR 0x42
+#define MAX1704X_ADDR 0x36
#define QMC6310_ADDR 0x1C
#define QMI8658_ADDR 0x6B
-#define QMC5883L_ADDR 0x1E
+#define QMC5883L_ADDR 0x0D
+#define HMC5883L_ADDR 0x1E
#define SHTC3_ADDR 0x70
#define LPS22HB_ADDR 0x5C
#define LPS22HB_ADDR_ALT 0x5D
@@ -139,15 +144,20 @@ along with this program. If not, see .
#define MLX90632_ADDR 0x3A
#define DFROBOT_LARK_ADDR 0x42
#define NAU7802_ADDR 0x2A
+#define MAX30102_ADDR 0x57
+#define MLX90614_ADDR_DEF 0x5A
// -----------------------------------------------------------------------------
// ACCELEROMETER
// -----------------------------------------------------------------------------
#define MPU6050_ADDR 0x68
+#define STK8BXX_ADR 0x18
#define LIS3DH_ADR 0x18
#define BMA423_ADDR 0x19
#define LSM6DS3_ADDR 0x6A
#define BMX160_ADDR 0x69
+#define ICM20948_ADDR 0x69
+#define ICM20948_ADDR_ALT 0x68
// -----------------------------------------------------------------------------
// LED
@@ -162,6 +172,7 @@ along with this program. If not, see .
// -----------------------------------------------------------------------------
// IO Expander
// -----------------------------------------------------------------------------
+#define TCA9535_ADDR 0x20
#define TCA9555_ADDR 0x26
// -----------------------------------------------------------------------------
@@ -171,6 +182,11 @@ along with this program. If not, see .
#define GPS_THREAD_INTERVAL 200
#endif
+// -----------------------------------------------------------------------------
+// Touchscreen
+// -----------------------------------------------------------------------------
+#define FT6336U_ADDR 0x48
+
// convert 24-bit color to 16-bit (56K)
#define COLOR565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3))
@@ -202,6 +218,16 @@ along with this program. If not, see .
#define MINIMUM_SAFE_FREE_HEAP 1500
#endif
+#ifndef WIRE_INTERFACES_COUNT
+// Officially an NRF52 macro
+// Repurposed cross-platform to identify devices using Wire1
+#if defined(I2C_SDA1) || defined(PIN_WIRE1_SDA)
+#define WIRE_INTERFACES_COUNT 2
+#elif HAS_WIRE
+#define WIRE_INTERFACES_COUNT 1
+#endif
+#endif
+
/* Step #3: mop up with disabled values for HAS_ options not handled by the above two */
#ifndef HAS_WIFI
@@ -324,4 +350,4 @@ along with this program. If not, see .
#endif
#include "DebugConfiguration.h"
-#include "RF95Configuration.h"
\ No newline at end of file
+#include "RF95Configuration.h"
diff --git a/src/detect/LoRaRadioType.h b/src/detect/LoRaRadioType.h
index 3975153b5..a059a3668 100644
--- a/src/detect/LoRaRadioType.h
+++ b/src/detect/LoRaRadioType.h
@@ -10,7 +10,8 @@ enum LoRaRadioType {
LLCC68_RADIO,
SX1280_RADIO,
LR1110_RADIO,
- LR1120_RADIO
+ LR1120_RADIO,
+ LR1121_RADIO
};
extern LoRaRadioType radioType;
\ No newline at end of file
diff --git a/src/detect/ScanI2C.cpp b/src/detect/ScanI2C.cpp
index 73bdf973b..a9d70edaa 100644
--- a/src/detect/ScanI2C.cpp
+++ b/src/detect/ScanI2C.cpp
@@ -37,8 +37,8 @@ ScanI2C::FoundDevice ScanI2C::firstKeyboard() const
ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const
{
- ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160};
- return firstOfOrNONE(5, types);
+ ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX, ICM20948};
+ return firstOfOrNONE(7, types);
}
ScanI2C::FoundDevice ScanI2C::find(ScanI2C::DeviceType) const
diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h
index 0a5b360de..920af06c7 100644
--- a/src/detect/ScanI2C.h
+++ b/src/detect/ScanI2C.h
@@ -28,6 +28,7 @@ class ScanI2C
INA260,
INA219,
INA3221,
+ MAX17048,
MCP9808,
SHT31,
SHT4X,
@@ -36,12 +37,14 @@ class ScanI2C
QMC6310,
QMI8658,
QMC5883L,
+ HMC5883L,
PMSA0031,
MPU6050,
LIS3DH,
BMA423,
BQ24295,
LSM6DS3,
+ TCA9535,
TCA9555,
VEML7700,
RCWL9620,
@@ -49,10 +52,15 @@ class ScanI2C
TSL2591,
OPT3001,
MLX90632,
+ MLX90614,
AHT10,
BMX160,
DFROBOT_LARK,
- NAU7802
+ NAU7802,
+ FT6336U,
+ STK8BAXX,
+ ICM20948,
+ MAX30102
} DeviceType;
// typedef uint8_t DeviceAddress;
@@ -63,8 +71,9 @@ class ScanI2C
} I2CPort;
typedef struct DeviceAddress {
- I2CPort port;
- uint8_t address;
+ // set default values for ADDRESS_NONE
+ I2CPort port = I2CPort::NO_I2C;
+ uint8_t address = 0;
explicit DeviceAddress(I2CPort port, uint8_t address);
DeviceAddress();
diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp
index 326eb4165..0fec2d63f 100644
--- a/src/detect/ScanI2CTwoWire.cpp
+++ b/src/detect/ScanI2CTwoWire.cpp
@@ -162,13 +162,13 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
Melopero_RV3028 rtc;
#endif
-#ifdef I2C_SDA1
+#if WIRE_INTERFACES_COUNT == 2
if (port == I2CPort::WIRE1) {
i2cBus = &Wire1;
} else {
#endif
i2cBus = &Wire;
-#ifdef I2C_SDA1
+#if WIRE_INTERFACES_COUNT == 2
}
#endif
@@ -313,17 +313,33 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
}
break;
case MCP9808_ADDR:
- registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x07), 2);
- if (registerValue == 0x0400) {
- type = MCP9808;
- LOG_INFO("MCP9808 sensor found\n");
- } else {
- type = LIS3DH;
- LOG_INFO("LIS3DH accelerometer found\n");
+ // We need to check for STK8BAXX first, since register 0x07 is new data flag for the z-axis and can produce some
+ // weird result. and register 0x00 doesn't seems to be colliding with MCP9808 and LIS3DH chips.
+ {
+ // Check register 0x00 for 0x8700 response to ID STK8BA53 chip.
+ registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 2);
+ if (registerValue == 0x8700) {
+ type = STK8BAXX;
+ LOG_INFO("STK8BAXX accelerometer found\n");
+ break;
+ }
+
+ // Check register 0x07 for 0x0400 response to ID MCP9808 chip.
+ registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x07), 2);
+ if (registerValue == 0x0400) {
+ type = MCP9808;
+ LOG_INFO("MCP9808 sensor found\n");
+ break;
+ }
+
+ // Check register 0x0F for 0x3300 response to ID LIS3DH chip.
+ registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0F), 2);
+ if (registerValue == 0x3300 || registerValue == 0x3333) { // RAK4631 WisBlock has LIS3DH register at 0x3333
+ type = LIS3DH;
+ LOG_INFO("LIS3DH accelerometer found\n");
+ }
+ break;
}
-
- break;
-
case SHT31_4x_ADDR:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2);
if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0xe9c) {
@@ -340,7 +356,18 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
break;
SCAN_SIMPLE_CASE(SHTC3_ADDR, SHTC3, "SHTC3 sensor found\n")
- SCAN_SIMPLE_CASE(RCWL9620_ADDR, RCWL9620, "RCWL9620 sensor found\n")
+ case RCWL9620_ADDR:
+ // get MAX30102 PARTID
+ registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFF), 1);
+ if (registerValue == 0x15) {
+ type = MAX30102;
+ LOG_INFO("MAX30102 Health sensor found\n");
+ break;
+ } else {
+ type = RCWL9620;
+ LOG_INFO("RCWL9620 sensor found\n");
+ }
+ break;
case LPS22HB_ADDR_ALT:
SCAN_SIMPLE_CASE(LPS22HB_ADDR, LPS22HB, "LPS22HB sensor found\n")
@@ -365,18 +392,38 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
break;
SCAN_SIMPLE_CASE(QMC5883L_ADDR, QMC5883L, "QMC5883L Highrate 3-Axis magnetic sensor found\n")
-
+ SCAN_SIMPLE_CASE(HMC5883L_ADDR, HMC5883L, "HMC5883L 3-Axis digital compass found\n")
SCAN_SIMPLE_CASE(PMSA0031_ADDR, PMSA0031, "PMSA0031 air quality sensor found\n")
- SCAN_SIMPLE_CASE(MPU6050_ADDR, MPU6050, "MPU6050 accelerometer found\n");
- SCAN_SIMPLE_CASE(BMX160_ADDR, BMX160, "BMX160 accelerometer found\n");
SCAN_SIMPLE_CASE(BMA423_ADDR, BMA423, "BMA423 accelerometer found\n");
SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3 accelerometer found at address 0x%x\n", (uint8_t)addr.address);
+ SCAN_SIMPLE_CASE(TCA9535_ADDR, TCA9535, "TCA9535 I2C expander found\n");
SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555 I2C expander found\n");
SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700 light sensor found\n");
SCAN_SIMPLE_CASE(TSL25911_ADDR, TSL2591, "TSL2591 light sensor found\n");
SCAN_SIMPLE_CASE(OPT3001_ADDR, OPT3001, "OPT3001 light sensor found\n");
SCAN_SIMPLE_CASE(MLX90632_ADDR, MLX90632, "MLX90632 IR temp sensor found\n");
SCAN_SIMPLE_CASE(NAU7802_ADDR, NAU7802, "NAU7802 based scale found\n");
+ SCAN_SIMPLE_CASE(FT6336U_ADDR, FT6336U, "FT6336U touchscreen found\n");
+ SCAN_SIMPLE_CASE(MAX1704X_ADDR, MAX17048, "MAX17048 lipo fuel gauge found\n");
+ SCAN_SIMPLE_CASE(MLX90614_ADDR_DEF, MLX90614, "MLX90614 IR temp sensor found\n");
+
+ case ICM20948_ADDR: // same as BMX160_ADDR
+ case ICM20948_ADDR_ALT: // same as MPU6050_ADDR
+ registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1);
+ if (registerValue == 0xEA) {
+ type = ICM20948;
+ LOG_INFO("ICM20948 9-dof motion processor found\n");
+ break;
+ } else if (addr.address == BMX160_ADDR) {
+ type = BMX160;
+ LOG_INFO("BMX160 accelerometer found\n");
+ break;
+ } else {
+ type = MPU6050;
+ LOG_INFO("MPU6050 accelerometer found\n");
+ break;
+ }
+ break;
default:
LOG_INFO("Device found at address 0x%x was not able to be enumerated\n", addr.address);
@@ -403,7 +450,7 @@ TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address) const
if (address.port == ScanI2C::I2CPort::WIRE) {
return &Wire;
} else {
-#ifdef I2C_SDA1
+#if WIRE_INTERFACES_COUNT == 2
return &Wire1;
#else
return &Wire;
@@ -415,4 +462,4 @@ size_t ScanI2CTwoWire::countDevices() const
{
return foundDevices.size();
}
-#endif
+#endif
\ No newline at end of file
diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp
index 066c12f5b..7296610ad 100644
--- a/src/gps/GPS.cpp
+++ b/src/gps/GPS.cpp
@@ -6,6 +6,8 @@
#include "NodeDB.h"
#include "PowerMon.h"
#include "RTC.h"
+#include "Throttle.h"
+#include "meshUtils.h"
#include "main.h" // pmu_found
#include "sleep.h"
@@ -26,6 +28,8 @@
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
HardwareSerial *GPS::_serial_gps = &Serial1;
+#elif defined(ARCH_RP2040)
+SerialUART *GPS::_serial_gps = &Serial1;
#else
HardwareSerial *GPS::_serial_gps = NULL;
#endif
@@ -58,7 +62,8 @@ const char *getGPSPowerStateString(GPSPowerState state)
case GPS_OFF:
return "OFF";
default:
- assert(false); // Unhandled enum value..
+ assert(false); // Unhandled enum value..
+ return "FALSE"; // to make new ESP-IDF happy
}
}
@@ -86,9 +91,9 @@ void GPS::CASChecksum(uint8_t *message, size_t length)
// Iterate over the payload as a series of uint32_t's and
// accumulate the cksum
- uint32_t const *payload = (uint32_t *)(message + 6);
for (size_t i = 0; i < (length - 10) / 4; i++) {
- uint32_t pl = payload[i];
+ uint32_t pl = 0;
+ memcpy(&pl, (message + 6) + (i * sizeof(uint32_t)), sizeof(uint32_t)); // avoid pointer dereference
cksum += pl;
}
@@ -166,18 +171,21 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
b = _serial_gps->read();
#ifdef GPS_DEBUG
- LOG_DEBUG("%02X", (char *)buffer);
+ LOG_DEBUG("%c", (b >= 32 && b <= 126) ? b : '.');
#endif
buffer[bytesRead] = b;
bytesRead++;
if ((bytesRead == 767) || (b == '\r')) {
if (strnstr((char *)buffer, message, bytesRead) != nullptr) {
#ifdef GPS_DEBUG
- LOG_DEBUG("\r");
+ LOG_DEBUG("\r\nFound: %s\r\n", message); // Log the found message
#endif
return GNSS_RESPONSE_OK;
} else {
bytesRead = 0;
+#ifdef GPS_DEBUG
+ LOG_DEBUG("\r\n");
+#endif
}
}
}
@@ -201,7 +209,7 @@ GPS_RESPONSE GPS::getACKCas(uint8_t class_id, uint8_t msg_id, uint32_t waitMilli
// ACK-NACK| 0xBA | 0xCE | 0x04 | 0x00 | 0x05 | 0x00 | 0xXX | 0xXX | 0x00 | 0x00 | 0xXX | 0xXX | 0xXX | 0xXX |
// ACK-ACK | 0xBA | 0xCE | 0x04 | 0x00 | 0x05 | 0x01 | 0xXX | 0xXX | 0x00 | 0x00 | 0xXX | 0xXX | 0xXX | 0xXX |
- while (millis() - startTime < waitMillis) {
+ while (Throttle::isWithinTimespanMs(startTime, waitMillis)) {
if (_serial_gps->available()) {
buffer[bufferPos++] = _serial_gps->read();
@@ -270,7 +278,7 @@ GPS_RESPONSE GPS::getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis)
buf[9] += buf[8];
}
- while (millis() - startTime < waitMillis) {
+ while (Throttle::isWithinTimespanMs(startTime, waitMillis)) {
if (ack > 9) {
#ifdef GPS_DEBUG
LOG_DEBUG("\n");
@@ -325,9 +333,9 @@ int GPS::getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
{
uint16_t ubxFrameCounter = 0;
uint32_t startTime = millis();
- uint16_t needRead;
+ uint16_t needRead = 0;
- while (millis() - startTime < waitMillis) {
+ while (Throttle::isWithinTimespanMs(startTime, waitMillis)) {
if (_serial_gps->available()) {
int c = _serial_gps->read();
switch (ubxFrameCounter) {
@@ -398,9 +406,9 @@ int GPS::getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
bool GPS::setup()
{
- int msglen = 0;
if (!didSerialInit) {
+ int msglen = 0;
if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) {
// if GPS_BAUDRATE is specified in variant (i.e. not 9600), skip to the specified rate.
@@ -505,283 +513,110 @@ bool GPS::setup()
delay(250);
_serial_gps->write("$CFGMSG,6,1,0\r\n");
delay(250);
- } else if (gnssModel == GNSS_MODEL_AG3335) {
+ } else if (IS_ONE_OF(gnssModel, GNSS_MODEL_AG3335, GNSS_MODEL_AG3352)) {
- _serial_gps->write("$PAIR066,1,0,1,0,0,1*3B"); // Enable GPS+GALILEO+NAVIC
+ _serial_gps->write("$PAIR066,1,0,1,0,0,1*3B\r\n"); // Enable GPS+GALILEO+NAVIC
// Configure NMEA (sentences will output once per fix)
- _serial_gps->write("$PAIR062,0,0*3F"); // GGA ON
- _serial_gps->write("$PAIR062,1,0*3F"); // GLL OFF
- _serial_gps->write("$PAIR062,2,1*3D"); // GSA ON
- _serial_gps->write("$PAIR062,3,0*3D"); // GSV OFF
- _serial_gps->write("$PAIR062,4,0*3B"); // RMC ON
- _serial_gps->write("$PAIR062,5,0*3B"); // VTG OFF
- _serial_gps->write("$PAIR062,6,1*39"); // ZDA ON
+ _serial_gps->write("$PAIR062,0,1*3F\r\n"); // GGA ON
+ _serial_gps->write("$PAIR062,1,0*3F\r\n"); // GLL OFF
+ _serial_gps->write("$PAIR062,2,0*3C\r\n"); // GSA OFF
+ _serial_gps->write("$PAIR062,3,0*3D\r\n"); // GSV OFF
+ _serial_gps->write("$PAIR062,4,1*3B\r\n"); // RMC ON
+ _serial_gps->write("$PAIR062,5,0*3B\r\n"); // VTG OFF
+ _serial_gps->write("$PAIR062,6,0*38\r\n"); // ZDA ON
delay(250);
- _serial_gps->write("$PAIR513*3D"); // save configuration
+ _serial_gps->write("$PAIR513*3D\r\n"); // save configuration
+ } else if (gnssModel == GNSS_MODEL_UBLOX6) {
+ clearBuffer();
+ SEND_UBX_PACKET(0x06, 0x02, _message_DISABLE_TXT_INFO, "disable text info messages", 500);
+ SEND_UBX_PACKET(0x06, 0x39, _message_JAM_6_7, "enable interference resistance", 500);
+ SEND_UBX_PACKET(0x06, 0x23, _message_NAVX5, "configure NAVX5 settings", 500);
- } else if (gnssModel == GNSS_MODEL_UBLOX) {
- // Configure GNSS system to GPS+SBAS+GLONASS (Module may restart after this command)
- // We need set it because by default it is GPS only, and we want to use GLONASS too
- // Also we need SBAS for better accuracy and extra features
- // ToDo: Dynamic configure GNSS systems depending of LoRa region
+ // Turn off unwanted NMEA messages, set update rate
+ SEND_UBX_PACKET(0x06, 0x08, _message_1HZ, "set GPS update rate", 500);
+ SEND_UBX_PACKET(0x06, 0x01, _message_GLL, "disable NMEA GLL", 500);
+ SEND_UBX_PACKET(0x06, 0x01, _message_GSA, "enable NMEA GSA", 500);
+ SEND_UBX_PACKET(0x06, 0x01, _message_GSV, "disable NMEA GSV", 500);
+ SEND_UBX_PACKET(0x06, 0x01, _message_VTG, "disable NMEA VTG", 500);
+ SEND_UBX_PACKET(0x06, 0x01, _message_RMC, "enable NMEA RMC", 500);
+ SEND_UBX_PACKET(0x06, 0x01, _message_GGA, "enable NMEA GGA", 500);
- if (strncmp(info.hwVersion, "000A0000", 8) != 0) {
- if (strncmp(info.hwVersion, "00040007", 8) != 0) {
- // The original ublox Neo-6 is GPS only and doesn't support the UBX-CFG-GNSS message
- // Max7 seems to only support GPS *or* GLONASS
- // Neo-7 is supposed to support GPS *and* GLONASS but NAKs the CFG-GNSS command to do it
- // So treat all the u-blox 7 series as GPS only
- // M8 can support 3 constallations at once so turn on GPS, GLONASS and Galileo (or BeiDou)
+ clearBuffer();
+ SEND_UBX_PACKET(0x06, 0x11, _message_CFG_RXM_ECO, "enable powersaving ECO mode for Neo-6", 500);
+ SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "enable powersaving details for GPS", 500);
+ SEND_UBX_PACKET(0x06, 0x01, _message_AID, "disable UBX-AID", 500);
- if (strncmp(info.hwVersion, "00070000", 8) == 0) {
- LOG_DEBUG("Setting GPS+SBAS\n");
- msglen = makeUBXPacket(0x06, 0x3e, sizeof(_message_GNSS_7), _message_GNSS_7);
- _serial_gps->write(UBXscratch, msglen);
- } else {
- msglen = makeUBXPacket(0x06, 0x3e, sizeof(_message_GNSS_8), _message_GNSS_8);
- _serial_gps->write(UBXscratch, msglen);
- }
+ msglen = makeUBXPacket(0x06, 0x09, sizeof(_message_SAVE), _message_SAVE);
+ _serial_gps->write(UBXscratch, msglen);
+ if (getACK(0x06, 0x09, 2000) != GNSS_RESPONSE_OK) {
+ LOG_WARN("Unable to save GNSS module configuration.\n");
+ } else {
+ LOG_INFO("GNSS module configuration saved!\n");
+ }
+ } else if (IS_ONE_OF(gnssModel, GNSS_MODEL_UBLOX7, GNSS_MODEL_UBLOX8, GNSS_MODEL_UBLOX9)) {
+ if (gnssModel == GNSS_MODEL_UBLOX7) {
+ LOG_DEBUG("Setting GPS+SBAS\n");
+ msglen = makeUBXPacket(0x06, 0x3e, sizeof(_message_GNSS_7), _message_GNSS_7);
+ _serial_gps->write(UBXscratch, msglen);
+ } else { // 8,9
+ msglen = makeUBXPacket(0x06, 0x3e, sizeof(_message_GNSS_8), _message_GNSS_8);
+ _serial_gps->write(UBXscratch, msglen);
+ }
- if (getACK(0x06, 0x3e, 800) == GNSS_RESPONSE_NAK) {
- // It's not critical if the module doesn't acknowledge this configuration.
- LOG_INFO("Unable to reconfigure GNSS - defaults maintained. Is this module GPS-only?\n");
- } else {
- if (strncmp(info.hwVersion, "00070000", 8) == 0) {
- LOG_INFO("GNSS configured for GPS+SBAS. Pause for 0.75s before sending next command.\n");
- } else {
- LOG_INFO(
- "GNSS configured for GPS+SBAS+GLONASS+Galileo. Pause for 0.75s before sending next command.\n");
- }
- // Documentation say, we need wait atleast 0.5s after reconfiguration of GNSS module, before sending next
- // commands for the M8 it tends to be more... 1 sec should be enough ;>)
- delay(1000);
- }
+ if (getACK(0x06, 0x3e, 800) == GNSS_RESPONSE_NAK) {
+ // It's not critical if the module doesn't acknowledge this configuration.
+ LOG_INFO("reconfigure GNSS - defaults maintained. Is this module GPS-only?\n");
+ } else {
+ if (gnssModel == GNSS_MODEL_UBLOX7) {
+ LOG_INFO("GNSS configured for GPS+SBAS.\n");
+ } else { // 8,9
+ LOG_INFO("GNSS configured for GPS+SBAS+GLONASS+Galileo.\n");
}
- // Disable Text Info messages
- msglen = makeUBXPacket(0x06, 0x02, sizeof(_message_DISABLE_TXT_INFO), _message_DISABLE_TXT_INFO);
+ // Documentation say, we need wait atleast 0.5s after reconfiguration of GNSS module, before sending next
+ // commands for the M8 it tends to be more... 1 sec should be enough ;>)
+ delay(1000);
+ }
+
+ // Disable Text Info messages //6,7,8,9
+ clearBuffer();
+ SEND_UBX_PACKET(0x06, 0x02, _message_DISABLE_TXT_INFO, "disable text info messages", 500);
+
+ if (gnssModel == GNSS_MODEL_UBLOX8) { // 8
clearBuffer();
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x02, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to disable text info messages.\n");
- }
- // ToDo add M10 tests for below
- if (strncmp(info.hwVersion, "00080000", 8) == 0) {
- msglen = makeUBXPacket(0x06, 0x39, sizeof(_message_JAM_8), _message_JAM_8);
+ SEND_UBX_PACKET(0x06, 0x39, _message_JAM_8, "enable interference resistance", 500);
+
+ clearBuffer();
+ SEND_UBX_PACKET(0x06, 0x23, _message_NAVX5_8, "configure NAVX5_8 settings", 500);
+ } else { // 6,7,9
+ SEND_UBX_PACKET(0x06, 0x39, _message_JAM_6_7, "enable interference resistance", 500);
+ SEND_UBX_PACKET(0x06, 0x23, _message_NAVX5, "configure NAVX5 settings", 500);
+ }
+ // Turn off unwanted NMEA messages, set update rate
+ SEND_UBX_PACKET(0x06, 0x08, _message_1HZ, "set GPS update rate", 500);
+ SEND_UBX_PACKET(0x06, 0x01, _message_GLL, "disable NMEA GLL", 500);
+ SEND_UBX_PACKET(0x06, 0x01, _message_GSA, "enable NMEA GSA", 500);
+ SEND_UBX_PACKET(0x06, 0x01, _message_GSV, "disable NMEA GSV", 500);
+ SEND_UBX_PACKET(0x06, 0x01, _message_VTG, "disable NMEA VTG", 500);
+ SEND_UBX_PACKET(0x06, 0x01, _message_RMC, "enable NMEA RMC", 500);
+ SEND_UBX_PACKET(0x06, 0x01, _message_GGA, "enable NMEA GGA", 500);
+
+ if (uBloxProtocolVersion >= 18) {
+ clearBuffer();
+ SEND_UBX_PACKET(0x06, 0x86, _message_PMS, "enable powersaving for GPS", 500);
+ SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "enable powersaving details for GPS", 500);
+
+ // For M8 we want to enable NMEA vserion 4.10 so we can see the additional sats.
+ if (gnssModel == GNSS_MODEL_UBLOX8) {
clearBuffer();
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x39, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to enable interference resistance.\n");
- }
-
- msglen = makeUBXPacket(0x06, 0x23, sizeof(_message_NAVX5_8), _message_NAVX5_8);
- clearBuffer();
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x23, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to configure NAVX5_8 settings.\n");
- }
- } else {
- msglen = makeUBXPacket(0x06, 0x39, sizeof(_message_JAM_6_7), _message_JAM_6_7);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x39, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to enable interference resistance.\n");
- }
-
- msglen = makeUBXPacket(0x06, 0x23, sizeof(_message_NAVX5), _message_NAVX5);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x23, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to configure NAVX5 settings.\n");
- }
- }
- // Turn off unwanted NMEA messages, set update rate
-
- msglen = makeUBXPacket(0x06, 0x08, sizeof(_message_1HZ), _message_1HZ);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x08, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to set GPS update rate.\n");
- }
-
- msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_GLL), _message_GLL);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to disable NMEA GLL.\n");
- }
-
- msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_GSA), _message_GSA);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to Enable NMEA GSA.\n");
- }
-
- msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_GSV), _message_GSV);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to disable NMEA GSV.\n");
- }
-
- msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_VTG), _message_VTG);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to disable NMEA VTG.\n");
- }
-
- msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_RMC), _message_RMC);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to enable NMEA RMC.\n");
- }
-
- msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_GGA), _message_GGA);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to enable NMEA GGA.\n");
- }
-
- if (uBloxProtocolVersion >= 18) {
- msglen = makeUBXPacket(0x06, 0x86, sizeof(_message_PMS), _message_PMS);
- clearBuffer();
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x86, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to enable powersaving for GPS.\n");
- }
- msglen = makeUBXPacket(0x06, 0x3B, sizeof(_message_CFG_PM2), _message_CFG_PM2);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x3B, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to enable powersaving details for GPS.\n");
- }
- // For M8 we want to enable NMEA vserion 4.10 so we can see the additional sats.
- if (strncmp(info.hwVersion, "00080000", 8) == 0) {
- msglen = makeUBXPacket(0x06, 0x17, sizeof(_message_NMEA), _message_NMEA);
- clearBuffer();
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x17, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to enable NMEA 4.10.\n");
- }
- }
- } else {
- if (strncmp(info.hwVersion, "00040007", 8) == 0) { // This PSM mode is only for Neo-6
- msglen = makeUBXPacket(0x06, 0x11, 0x2, _message_CFG_RXM_ECO);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x11, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to enable powersaving ECO mode for Neo-6.\n");
- }
- msglen = makeUBXPacket(0x06, 0x3B, sizeof(_message_CFG_PM2), _message_CFG_PM2);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x3B, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to enable powersaving details for GPS.\n");
- }
- msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_AID), _message_AID);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x01, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to disable UBX-AID.\n");
- }
- } else {
- msglen = makeUBXPacket(0x06, 0x11, 0x2, _message_CFG_RXM_PSM);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x11, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to enable powersaving mode for GPS.\n");
- }
-
- msglen = makeUBXPacket(0x06, 0x3B, sizeof(_message_CFG_PM2), _message_CFG_PM2);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x3B, 500) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to enable powersaving details for GPS.\n");
- }
- }
+ SEND_UBX_PACKET(0x06, 0x17, _message_NMEA, "enable NMEA 4.10", 500);
}
} else {
- // LOG_INFO("u-blox M10 hardware found.\n");
- delay(1000);
- // First disable all NMEA messages in RAM layer
- msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_DISABLE_NMEA_RAM), _message_VALSET_DISABLE_NMEA_RAM);
- clearBuffer();
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to disable NMEA messages for M10 GPS RAM.\n");
- }
- delay(250);
- // Next disable unwanted NMEA messages in BBR layer
- msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_DISABLE_NMEA_BBR), _message_VALSET_DISABLE_NMEA_BBR);
- clearBuffer();
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to disable NMEA messages for M10 GPS BBR.\n");
- }
- delay(250);
- // Disable Info txt messages in RAM layer
- msglen =
- makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_DISABLE_TXT_INFO_RAM), _message_VALSET_DISABLE_TXT_INFO_RAM);
- clearBuffer();
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to disable Info messages for M10 GPS RAM.\n");
- }
- delay(250);
- // Next disable Info txt messages in BBR layer
- msglen =
- makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_DISABLE_TXT_INFO_BBR), _message_VALSET_DISABLE_TXT_INFO_BBR);
- clearBuffer();
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to disable Info messages for M10 GPS BBR.\n");
- }
- // Do M10 configuration for Power Management.
-
- msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_PM_RAM), _message_VALSET_PM_RAM);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to enable powersaving for M10 GPS RAM.\n");
- }
- msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_PM_BBR), _message_VALSET_PM_BBR);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to enable powersaving for M10 GPS BBR.\n");
- }
-
- delay(250);
- msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_ITFM_RAM), _message_VALSET_ITFM_RAM);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to enable Jamming detection M10 GPS RAM.\n");
- }
- msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_ITFM_BBR), _message_VALSET_ITFM_BBR);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to enable Jamming detection M10 GPS BBR.\n");
- }
-
- // Here is where the init commands should go to do further M10 initialization.
- delay(250);
- msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_DISABLE_SBAS_RAM), _message_VALSET_DISABLE_SBAS_RAM);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to disable SBAS M10 GPS RAM.\n");
- }
- delay(750); // will cause a receiver restart so wait a bit
- msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_DISABLE_SBAS_BBR), _message_VALSET_DISABLE_SBAS_BBR);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to disable SBAS M10 GPS BBR.\n");
- }
- delay(750); // will cause a receiver restart so wait a bit
- // Done with initialization, Now enable wanted NMEA messages in BBR layer so they will survive a periodic sleep.
- msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_ENABLE_NMEA_BBR), _message_VALSET_ENABLE_NMEA_BBR);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to enable messages for M10 GPS BBR.\n");
- }
- delay(250);
- // Next enable wanted NMEA messages in RAM layer
- msglen = makeUBXPacket(0x06, 0x8A, sizeof(_message_VALSET_ENABLE_NMEA_RAM), _message_VALSET_ENABLE_NMEA_RAM);
- _serial_gps->write(UBXscratch, msglen);
- if (getACK(0x06, 0x8A, 300) != GNSS_RESPONSE_OK) {
- LOG_WARN("Unable to enable messages for M10 GPS RAM.\n");
- }
- // As the M10 has no flash, the best we can do to preserve the config is to set it in RAM and BBR.
- // BBR will survive a restart, and power off for a while, but modules with small backup
- // batteries or super caps will not retain the config for a long power off time.
+ SEND_UBX_PACKET(0x06, 0x11, _message_CFG_RXM_PSM, "enable powersaving mode for GPS", 500);
+ SEND_UBX_PACKET(0x06, 0x3B, _message_CFG_PM2, "enable powersaving details for GPS", 500);
}
+
msglen = makeUBXPacket(0x06, 0x09, sizeof(_message_SAVE), _message_SAVE);
_serial_gps->write(UBXscratch, msglen);
if (getACK(0x06, 0x09, 2000) != GNSS_RESPONSE_OK) {
@@ -789,6 +624,53 @@ bool GPS::setup()
} else {
LOG_INFO("GNSS module configuration saved!\n");
}
+ } else if (gnssModel == GNSS_MODEL_UBLOX10) {
+ delay(1000);
+ clearBuffer();
+ SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_NMEA_RAM, "disable NMEA messages in M10 RAM", 300);
+ delay(750);
+ clearBuffer();
+ SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_NMEA_BBR, "disable NMEA messages in M10 BBR", 300);
+ delay(750);
+ clearBuffer();
+ SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_TXT_INFO_RAM, "disable Info messages for M10 GPS RAM", 300);
+ delay(750);
+ // Next disable Info txt messages in BBR layer
+ clearBuffer();
+ SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_TXT_INFO_BBR, "disable Info messages for M10 GPS BBR", 300);
+ delay(750);
+ // Do M10 configuration for Power Management.
+ SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_PM_RAM, "enable powersaving for M10 GPS RAM", 300);
+ delay(750);
+ SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_PM_BBR, "enable powersaving for M10 GPS BBR", 300);
+ delay(750);
+ SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ITFM_RAM, "enable Jamming detection M10 GPS RAM", 300);
+ delay(750);
+ SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ITFM_BBR, "enable Jamming detection M10 GPS BBR", 300);
+ delay(750);
+ // Here is where the init commands should go to do further M10 initialization.
+ SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_SBAS_RAM, "disable SBAS M10 GPS RAM", 300);
+ delay(750); // will cause a receiver restart so wait a bit
+ SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_SBAS_BBR, "disable SBAS M10 GPS BBR", 300);
+ delay(750); // will cause a receiver restart so wait a bit
+
+ // Done with initialization, Now enable wanted NMEA messages in BBR layer so they will survive a periodic sleep.
+ SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ENABLE_NMEA_BBR, "enable messages for M10 GPS BBR", 300);
+ delay(750);
+ // Next enable wanted NMEA messages in RAM layer
+ SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ENABLE_NMEA_RAM, "enable messages for M10 GPS RAM", 500);
+ delay(750);
+
+ // As the M10 has no flash, the best we can do to preserve the config is to set it in RAM and BBR.
+ // BBR will survive a restart, and power off for a while, but modules with small backup
+ // batteries or super caps will not retain the config for a long power off time.
+ msglen = makeUBXPacket(0x06, 0x09, sizeof(_message_SAVE_10), _message_SAVE_10);
+ _serial_gps->write(UBXscratch, msglen);
+ if (getACK(0x06, 0x09, 2000) != GNSS_RESPONSE_OK) {
+ LOG_WARN("Unable to save GNSS module configuration.\n");
+ } else {
+ LOG_INFO("GNSS module configuration saved!\n");
+ }
}
didSerialInit = true;
}
@@ -944,7 +826,7 @@ void GPS::setPowerPMU(bool on)
void GPS::setPowerUBLOX(bool on, uint32_t sleepMs)
{
// Abort: if not UBLOX hardware
- if (gnssModel != GNSS_MODEL_UBLOX)
+ if (!IS_ONE_OF(gnssModel, GNSS_MODEL_UBLOX6, GNSS_MODEL_UBLOX7, GNSS_MODEL_UBLOX8, GNSS_MODEL_UBLOX9, GNSS_MODEL_UBLOX10))
return;
// If waking
@@ -967,7 +849,7 @@ void GPS::setPowerUBLOX(bool on, uint32_t sleepMs)
}
// Determine hardware version
- if (strncmp(info.hwVersion, "000A0000", 8) != 0) {
+ if (gnssModel == GNSS_MODEL_UBLOX10) {
// Encode the sleep time in millis into the packet
for (int i = 0; i < 4; i++)
gps->_message_PMREQ[0 + i] = sleepMs >> (i * 8);
@@ -1019,33 +901,36 @@ void GPS::down()
LOG_DEBUG("%us until next search\n", sleepTime / 1000);
// If update interval less than 10 seconds, no attempt to sleep
- if (updateInterval <= 10 * 1000UL)
+ if (updateInterval <= 10 * 1000UL || sleepTime == 0)
setPowerState(GPS_IDLE);
else {
// Check whether the GPS hardware is capable of GPS_SOFTSLEEP
// If not, fallback to GPS_HARDSLEEP instead
bool softsleepSupported = false;
- if (gnssModel == GNSS_MODEL_UBLOX) // U-blox is supported via PMREQ
+ // U-blox is supported via PMREQ
+ if (IS_ONE_OF(gnssModel, GNSS_MODEL_UBLOX6, GNSS_MODEL_UBLOX7, GNSS_MODEL_UBLOX8, GNSS_MODEL_UBLOX9, GNSS_MODEL_UBLOX10))
softsleepSupported = true;
#ifdef PIN_GPS_STANDBY // L76B, L76K and clones have a standby pin
softsleepSupported = true;
#endif
- // How long does gps_update_interval need to be, for GPS_HARDSLEEP to become more efficient than GPS_SOFTSLEEP?
- // Heuristic equation. A compromise manually fitted to power observations from U-blox NEO-6M and M10050
- // https://www.desmos.com/calculator/6gvjghoumr
- // This is not particularly accurate, but probably an impromevement over a single, fixed threshold
- uint32_t hardsleepThreshold = (2750 * pow(predictedSearchDuration / 1000, 1.22));
- LOG_DEBUG("gps_update_interval >= %us needed to justify hardsleep\n", hardsleepThreshold / 1000);
-
- // If update interval too short: softsleep (if supported by hardware)
- if (softsleepSupported && updateInterval < hardsleepThreshold)
- setPowerState(GPS_SOFTSLEEP, sleepTime);
+ if (softsleepSupported) {
+ // How long does gps_update_interval need to be, for GPS_HARDSLEEP to become more efficient than GPS_SOFTSLEEP?
+ // Heuristic equation. A compromise manually fitted to power observations from U-blox NEO-6M and M10050
+ // https://www.desmos.com/calculator/6gvjghoumr
+ // This is not particularly accurate, but probably an impromevement over a single, fixed threshold
+ uint32_t hardsleepThreshold = (2750 * pow(predictedSearchDuration / 1000, 1.22));
+ LOG_DEBUG("gps_update_interval >= %us needed to justify hardsleep\n", hardsleepThreshold / 1000);
+ // If update interval too short: softsleep (if supported by hardware)
+ if (updateInterval < hardsleepThreshold) {
+ setPowerState(GPS_SOFTSLEEP, sleepTime);
+ return;
+ }
+ }
// If update interval long enough (or softsleep unsupported): hardsleep instead
- else
- setPowerState(GPS_HARDSLEEP, sleepTime);
+ setPowerState(GPS_HARDSLEEP, sleepTime);
}
}
@@ -1101,7 +986,9 @@ int32_t GPS::runOnce()
// if we have received valid NMEA claim we are connected
setConnected();
} else {
- if ((config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) && (gnssModel == GNSS_MODEL_UBLOX)) {
+ if ((config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) &&
+ IS_ONE_OF(gnssModel, GNSS_MODEL_UBLOX6, GNSS_MODEL_UBLOX7, GNSS_MODEL_UBLOX8, GNSS_MODEL_UBLOX9,
+ GNSS_MODEL_UBLOX10)) {
// reset the GPS on next bootup
if (devicestate.did_gps_reset && scheduling.elapsedSearchMs() > 60 * 1000UL && !hasFlow()) {
LOG_DEBUG("GPS is not communicating, trying factory reset on next bootup.\n");
@@ -1184,25 +1071,34 @@ int GPS::prepareDeepSleep(void *unused)
return 0;
}
+const char *PROBE_MESSAGE = "Trying %s (%s)...\n";
+const char *DETECTED_MESSAGE = "%s detected, using %s Module\n";
+
+#define PROBE_SIMPLE(CHIP, TOWRITE, RESPONSE, DRIVER, TIMEOUT, ...) \
+ LOG_DEBUG(PROBE_MESSAGE, TOWRITE, CHIP); \
+ clearBuffer(); \
+ _serial_gps->write(TOWRITE "\r\n"); \
+ if (getACK(RESPONSE, TIMEOUT) == GNSS_RESPONSE_OK) { \
+ LOG_INFO(DETECTED_MESSAGE, CHIP, #DRIVER); \
+ return DRIVER; \
+ }
+
GnssModel_t GPS::probe(int serialSpeed)
{
-#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_RP2040) || defined(ARCH_STM32WL) || defined(ARCH_APOLLO3)
+#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL) || defined(ARCH_APOLLO3)
_serial_gps->end();
_serial_gps->begin(serialSpeed);
+#elif defined(ARCH_RP2040)
+ _serial_gps->end();
+ _serial_gps->setFIFOSize(256);
+ _serial_gps->begin(serialSpeed);
#else
if (_serial_gps->baudRate() != serialSpeed) {
LOG_DEBUG("Setting Baud to %i\n", serialSpeed);
_serial_gps->updateBaudRate(serialSpeed);
}
#endif
-#ifdef GNSS_AIROHA
- return GNSS_MODEL_AG3335;
-#endif
-#ifdef GPS_DEBUG
- for (int i = 0; i < 20; i++) {
- getACK("$GP", 200);
- }
-#endif
+
memset(&info, 0, sizeof(struct uBloxGnssModelInfo));
uint8_t buffer[768] = {0};
delay(100);
@@ -1211,70 +1107,29 @@ GnssModel_t GPS::probe(int serialSpeed)
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
delay(20);
- // get version information from Unicore UFirebirdII Series
- // Works for: UC6580, UM620, UM621, UM670A, UM680A, or UM681A
- _serial_gps->write("$PDTINFO\r\n");
- delay(750);
- if (getACK("UC6580", 500) == GNSS_RESPONSE_OK) {
- LOG_INFO("UC6580 detected, using UC6580 Module\n");
- return GNSS_MODEL_UC6580;
- }
-
- clearBuffer();
- _serial_gps->write("$PDTINFO\r\n");
- delay(750);
- if (getACK("UM600", 500) == GNSS_RESPONSE_OK) {
- LOG_INFO("UM600 detected, using UC6580 Module\n");
- return GNSS_MODEL_UC6580;
- }
-
- // Get version information for ATGM336H
- clearBuffer();
- _serial_gps->write("$PCAS06,1*1A\r\n");
- if (getACK("$GPTXT,01,01,02,HW=ATGM336H", 500) == GNSS_RESPONSE_OK) {
- LOG_INFO("ATGM336H GNSS init succeeded, using ATGM336H Module\n");
- return GNSS_MODEL_ATGM336H;
- }
-
+ // Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A
+ PROBE_SIMPLE("UC6580", "$PDTINFO", "UC6580", GNSS_MODEL_UC6580, 500);
+ PROBE_SIMPLE("UM600", "$PDTINFO", "UM600", GNSS_MODEL_UC6580, 500);
+ PROBE_SIMPLE("ATGM336H", "$PCAS06,1*1A", "$GPTXT,01,01,02,HW=ATGM336H", GNSS_MODEL_ATGM336H, 500);
/* ATGM332D series (-11(GPS), -21(BDS), -31(GPS+BDS), -51(GPS+GLONASS), -71-0(GPS+BDS+GLONASS))
based on AT6558 */
- clearBuffer();
- _serial_gps->write("$PCAS06,1*1A\r\n");
- if (getACK("$GPTXT,01,01,02,HW=ATGM332D", 500) == GNSS_RESPONSE_OK) {
- LOG_INFO("ATGM332D detected, using ATGM336H Module\n");
- return GNSS_MODEL_ATGM336H;
- }
+ PROBE_SIMPLE("ATGM332D", "$PCAS06,1*1A", "$GPTXT,01,01,02,HW=ATGM332D", GNSS_MODEL_ATGM336H, 500);
/* Airoha (Mediatek) AG3335A/M/S, A3352Q, Quectel L89 2.0, SimCom SIM65M */
- clearBuffer();
- _serial_gps->write("PAIR020*38\r\n");
- if (getACK("$PAIR020,AG3335", 500) == GNSS_RESPONSE_OK) {
- LOG_INFO("Aioha AG3335 detected, using AG3335 Module\n");
- return GNSS_MODEL_AG3335;
- }
- // Get version information for Airoha AG3335
- clearBuffer();
- _serial_gps->write("$PMTK605*31\r\n");
+ _serial_gps->write("$PAIR062,2,0*3C\r\n"); // GSA OFF to reduce volume
+ _serial_gps->write("$PAIR062,3,0*3D\r\n"); // GSV OFF to reduce volume
+ _serial_gps->write("$PAIR513*3D\r\n"); // save configuration
+ PROBE_SIMPLE("AG3335", "$PAIR021*39", "$PAIR021,AG3335", GNSS_MODEL_AG3335, 500);
+ PROBE_SIMPLE("AG3352", "$PAIR021*39", "$PAIR021,AG3352", GNSS_MODEL_AG3352, 500);
+ PROBE_SIMPLE("LC86", "$PQTMVERNO*58", "$PQTMVERNO,LC86", GNSS_MODEL_AG3352, 500);
- // Get version information
- clearBuffer();
- _serial_gps->write("$PCAS06,0*1B\r\n");
- if (getACK("$GPTXT,01,01,02,SW=", 500) == GNSS_RESPONSE_OK) {
- LOG_INFO("L76K GNSS init succeeded, using L76K GNSS Module\n");
- return GNSS_MODEL_MTK;
- }
+ PROBE_SIMPLE("L76K", "$PCAS06,0*1B", "$GPTXT,01,01,02,SW=", GNSS_MODEL_MTK, 500);
// Close all NMEA sentences, valid for L76B MTK platform (Waveshare Pico GPS)
_serial_gps->write("$PMTK514,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*2E\r\n");
delay(20);
- // Get version information
- clearBuffer();
- _serial_gps->write("$PMTK605*31\r\n");
- if (getACK("Quectel-L76B", 500) == GNSS_RESPONSE_OK) {
- LOG_INFO("L76B GNSS init succeeded, using L76B GNSS Module\n");
- return GNSS_MODEL_MTK_L76B;
- }
+ PROBE_SIMPLE("L76B", "$PMTK605*31", "Quectel-L76B", GNSS_MODEL_MTK_L76B, 500);
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00};
UBXChecksum(cfg_rate, sizeof(cfg_rate));
@@ -1301,9 +1156,13 @@ GnssModel_t GPS::probe(int serialSpeed)
_serial_gps->write(_message_prt, sizeof(_message_prt));
delay(500);
serialSpeed = 9600;
-#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_RP2040) || defined(ARCH_STM32WL) || defined(ARCH_APOLLO3)
+#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL) || defined(ARCH_APOLLO3)
_serial_gps->end();
_serial_gps->begin(serialSpeed);
+#elif defined(ARCH_RP2040)
+ _serial_gps->end();
+ _serial_gps->setFIFOSize(256);
+ _serial_gps->begin(serialSpeed);
#else
_serial_gps->updateBaudRate(serialSpeed);
#endif
@@ -1361,9 +1220,9 @@ GnssModel_t GPS::probe(int serialSpeed)
strncpy((char *)buffer, &(info.extension[i][4]), sizeof(buffer));
// LOG_DEBUG("GetModel:%s\n", (char *)buffer);
if (strlen((char *)buffer)) {
- LOG_INFO("UBlox GNSS probe succeeded, using UBlox %s GNSS Module\n", (char *)buffer);
+ LOG_INFO("%s detected, using GNSS_MODEL_UBLOX\n", (char *)buffer);
} else {
- LOG_INFO("UBlox GNSS probe succeeded, using UBlox GNSS Module\n");
+ LOG_INFO("Generic Ublox detected, using GNSS_MODEL_UBLOX\n");
}
} else if (!strncmp(info.extension[i], "PROTVER", 7)) {
char *ptr = nullptr;
@@ -1378,9 +1237,20 @@ GnssModel_t GPS::probe(int serialSpeed)
}
}
}
+ if (strncmp(info.hwVersion, "00040007", 8) == 0) {
+ return GNSS_MODEL_UBLOX6;
+ } else if (strncmp(info.hwVersion, "00070000", 8) == 0) {
+ return GNSS_MODEL_UBLOX7;
+ } else if (strncmp(info.hwVersion, "00080000", 8) == 0) {
+ return GNSS_MODEL_UBLOX8;
+ } else if (strncmp(info.hwVersion, "00190000", 8) == 0) {
+ return GNSS_MODEL_UBLOX9;
+ } else if (strncmp(info.hwVersion, "000A0000", 8) == 0) {
+ return GNSS_MODEL_UBLOX10;
+ }
}
- return GNSS_MODEL_UBLOX;
+ return GNSS_MODEL_UNKNOWN;
}
GPS *GPS::createGps()
@@ -1464,6 +1334,9 @@ GPS *GPS::createGps()
LOG_DEBUG("Using GPIO%d for GPS RX\n", new_gps->rx_gpio);
LOG_DEBUG("Using GPIO%d for GPS TX\n", new_gps->tx_gpio);
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, new_gps->rx_gpio, new_gps->tx_gpio);
+#elif defined(ARCH_RP2040)
+ _serial_gps->setFIFOSize(256);
+ _serial_gps->begin(GPS_BAUDRATE);
#else
_serial_gps->begin(GPS_BAUDRATE);
#endif
@@ -1495,21 +1368,21 @@ bool GPS::factoryReset()
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1C, 0xA2};
_serial_gps->write(_message_reset1, sizeof(_message_reset1));
if (getACK(0x05, 0x01, 10000)) {
- LOG_INFO("Get ack success!\n");
+ LOG_INFO(ACK_SUCCESS_MESSAGE);
}
delay(100);
byte _message_reset2[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1B, 0xA1};
_serial_gps->write(_message_reset2, sizeof(_message_reset2));
if (getACK(0x05, 0x01, 10000)) {
- LOG_INFO("Get ack success!\n");
+ LOG_INFO(ACK_SUCCESS_MESSAGE);
}
delay(100);
byte _message_reset3[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x03, 0x1D, 0xB3};
_serial_gps->write(_message_reset3, sizeof(_message_reset3));
if (getACK(0x05, 0x01, 10000)) {
- LOG_INFO("Get ack success!\n");
+ LOG_INFO(ACK_SUCCESS_MESSAGE);
}
// Reset device ram to COLDSTART state
// byte _message_CFG_RST_COLDSTART[] = {0xB5, 0x62, 0x06, 0x04, 0x04, 0x00, 0xFF, 0xB9, 0x00, 0x00, 0xC6, 0x8B};
@@ -1551,16 +1424,15 @@ bool GPS::lookForTime()
#ifdef GNSS_AIROHA
uint8_t fix = reader.fixQuality();
- uint32_t now = millis();
if (fix > 0) {
if (lastFixStartMsec > 0) {
- if ((now - lastFixStartMsec) < GPS_FIX_HOLD_TIME) {
+ if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
return false;
} else {
clearBuffer();
}
} else {
- lastFixStartMsec = now;
+ lastFixStartMsec = millis();
return false;
}
} else {
@@ -1575,7 +1447,7 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
*/
struct tm t;
- t.tm_sec = ti.second();
+ t.tm_sec = ti.second() + round(ti.age() / 1000);
t.tm_min = ti.minute();
t.tm_hour = ti.hour();
t.tm_mday = d.day();
@@ -1583,8 +1455,8 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
t.tm_year = d.year() - 1900;
t.tm_isdst = false;
if (t.tm_mon > -1) {
- LOG_DEBUG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d\n", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min,
- t.tm_sec);
+ LOG_DEBUG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d age %d\n", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min,
+ t.tm_sec, ti.age());
perhapsSetRTC(RTCQualityGPS, t);
return true;
} else
@@ -1604,16 +1476,15 @@ bool GPS::lookForLocation()
#ifdef GNSS_AIROHA
if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) {
uint8_t fix = reader.fixQuality();
- uint32_t now = millis();
if (fix > 0) {
if (lastFixStartMsec > 0) {
- if ((now - lastFixStartMsec) < GPS_FIX_HOLD_TIME) {
+ if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
return false;
} else {
clearBuffer();
}
} else {
- lastFixStartMsec = now;
+ lastFixStartMsec = millis();
return false;
}
} else {
diff --git a/src/gps/GPS.h b/src/gps/GPS.h
index c0e9fb8b6..6222881bc 100644
--- a/src/gps/GPS.h
+++ b/src/gps/GPS.h
@@ -26,11 +26,16 @@ struct uBloxGnssModelInfo {
typedef enum {
GNSS_MODEL_ATGM336H,
GNSS_MODEL_MTK,
- GNSS_MODEL_UBLOX,
+ GNSS_MODEL_UBLOX6,
+ GNSS_MODEL_UBLOX7,
+ GNSS_MODEL_UBLOX8,
+ GNSS_MODEL_UBLOX9,
+ GNSS_MODEL_UBLOX10,
GNSS_MODEL_UC6580,
GNSS_MODEL_UNKNOWN,
GNSS_MODEL_MTK_L76B,
- GNSS_MODEL_AG3335
+ GNSS_MODEL_AG3335,
+ GNSS_MODEL_AG3352
} GnssModel_t;
typedef enum {
@@ -101,7 +106,7 @@ class GPS : private concurrency::OSThread
public:
/** If !NULL we will use this serial port to construct our GPS */
-#if defined(RPI_PICO_WAVESHARE)
+#if defined(ARCH_RP2040)
static SerialUART *_serial_gps;
#else
static HardwareSerial *_serial_gps;
@@ -129,6 +134,7 @@ class GPS : private concurrency::OSThread
static const uint8_t _message_GGA[];
static const uint8_t _message_PMS[];
static const uint8_t _message_SAVE[];
+ static const uint8_t _message_SAVE_10[];
// VALSET Commands for M10
static const uint8_t _message_VALSET_PM[];
@@ -150,6 +156,8 @@ class GPS : private concurrency::OSThread
static const uint8_t _message_CAS_CFG_NAVX_CONF[];
static const uint8_t _message_CAS_CFG_RATE_1HZ[];
+ const char *ACK_SUCCESS_MESSAGE = "Get ack success!\n";
+
meshtastic_Position p = meshtastic_Position_init_default;
/** This is normally bound to config.position.gps_en_gpio but some rare boards (like heltec tracker) need more advanced
@@ -297,7 +305,6 @@ class GPS : private concurrency::OSThread
virtual int32_t runOnce() override;
// Get GNSS model
- String getNMEA();
GnssModel_t probe(int serialSpeed);
// delay counter to allow more sats before fixed position stops GPS thread
diff --git a/src/gps/NMEAWPL.cpp b/src/gps/NMEAWPL.cpp
index 71943b76c..f528c4607 100644
--- a/src/gps/NMEAWPL.cpp
+++ b/src/gps/NMEAWPL.cpp
@@ -75,10 +75,13 @@ uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_Position &pos, const
uint32_t printGGA(char *buf, size_t bufsz, const meshtastic_Position &pos)
{
GeoCoord geoCoord(pos.latitude_i, pos.longitude_i, pos.altitude);
- tm *t = gmtime((time_t *)&pos.timestamp);
+ time_t timestamp = pos.timestamp;
+
+ tm *t = gmtime(×tamp);
if (getRTCQuality() > 0) { // use the device clock if we got time from somewhere. If not, use the GPS timestamp.
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice);
- t = gmtime((time_t *)&rtc_sec);
+ timestamp = rtc_sec;
+ t = gmtime(×tamp);
}
uint32_t len = snprintf(
diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp
index 070038672..d9ac56b74 100644
--- a/src/gps/RTC.cpp
+++ b/src/gps/RTC.cpp
@@ -2,6 +2,7 @@
#include "configuration.h"
#include "detect/ScanI2C.h"
#include "main.h"
+#include
#include
#include
@@ -29,7 +30,7 @@ void readFromRTC()
if (rtc_found.address == RV3028_RTC) {
uint32_t now = millis();
Melopero_RV3028 rtc;
-#ifdef I2C_SDA1
+#if WIRE_INTERFACES_COUNT == 2
rtc.initI2C(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire);
#else
rtc.initI2C();
@@ -43,7 +44,10 @@ void readFromRTC()
t.tm_sec = rtc.getSecond();
tv.tv_sec = gm_mktime(&t);
tv.tv_usec = 0;
- LOG_DEBUG("Read RTC time from RV3028 as %ld\n", tv.tv_sec);
+
+ uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
+ LOG_DEBUG("Read RTC time from RV3028 getTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)\n", t.tm_year + 1900, t.tm_mon + 1,
+ t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
if (currentQuality == RTCQualityNone) {
@@ -55,7 +59,7 @@ void readFromRTC()
uint32_t now = millis();
PCF8563_Class rtc;
-#ifdef I2C_SDA1
+#if WIRE_INTERFACES_COUNT == 2
rtc.begin(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire);
#else
rtc.begin();
@@ -71,7 +75,10 @@ void readFromRTC()
t.tm_sec = tc.second;
tv.tv_sec = gm_mktime(&t);
tv.tv_usec = 0;
- LOG_DEBUG("Read RTC time from PCF8563 as %ld\n", tv.tv_sec);
+
+ uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
+ LOG_DEBUG("Read RTC time from PCF8563 getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)\n", t.tm_year + 1900,
+ t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
if (currentQuality == RTCQualityNone) {
@@ -81,7 +88,8 @@ void readFromRTC()
#else
if (!gettimeofday(&tv, NULL)) {
uint32_t now = millis();
- LOG_DEBUG("Read RTC time as %ld\n", tv.tv_sec);
+ uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
+ LOG_DEBUG("Read RTC time as %ld\n", printableEpoch);
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
}
@@ -101,6 +109,13 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate)
{
static uint32_t lastSetMsec = 0;
uint32_t now = millis();
+ uint32_t printableEpoch = tv->tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
+#ifdef BUILD_EPOCH
+ if (tv->tv_sec < BUILD_EPOCH) {
+ LOG_WARN("Ignoring time (%ld) before build epoch (%ld)!\n", printableEpoch, BUILD_EPOCH);
+ return false;
+ }
+#endif
bool shouldSet;
if (forceUpdate) {
@@ -110,10 +125,13 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate)
} else if (q > currentQuality) {
shouldSet = true;
LOG_DEBUG("Upgrading time to quality %s\n", RtcName(q));
- } else if (q >= RTCQualityNTP && (now - lastSetMsec) > (12 * 60 * 60 * 1000UL)) {
- // Every 12 hrs we will slam in a new GPS or Phone GPS / NTP time, to correct for local RTC clock drift
+ } else if (q == RTCQualityGPS) {
shouldSet = true;
- LOG_DEBUG("Reapplying external time to correct clock drift %ld secs\n", tv->tv_sec);
+ LOG_DEBUG("Reapplying GPS time: %ld secs\n", printableEpoch);
+ } else if (q == RTCQualityNTP && !Throttle::isWithinTimespanMs(lastSetMsec, (12 * 60 * 60 * 1000UL))) {
+ // Every 12 hrs we will slam in a new NTP or Phone GPS / NTP time, to correct for local RTC clock drift
+ shouldSet = true;
+ LOG_DEBUG("Reapplying external time to correct clock drift %ld secs\n", printableEpoch);
} else {
shouldSet = false;
LOG_DEBUG("Current RTC quality: %s. Ignoring time of RTC quality of %s\n", RtcName(currentQuality), RtcName(q));
@@ -133,29 +151,29 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate)
#ifdef RV3028_RTC
if (rtc_found.address == RV3028_RTC) {
Melopero_RV3028 rtc;
-#ifdef I2C_SDA1
+#if WIRE_INTERFACES_COUNT == 2
rtc.initI2C(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire);
#else
rtc.initI2C();
#endif
tm *t = gmtime(&tv->tv_sec);
rtc.setTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_wday, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
- LOG_DEBUG("RV3028_RTC setTime %02d-%02d-%02d %02d:%02d:%02d %ld\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
- t->tm_hour, t->tm_min, t->tm_sec, tv->tv_sec);
+ LOG_DEBUG("RV3028_RTC setTime %02d-%02d-%02d %02d:%02d:%02d (%ld)\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
+ t->tm_hour, t->tm_min, t->tm_sec, printableEpoch);
}
#elif defined(PCF8563_RTC)
if (rtc_found.address == PCF8563_RTC) {
PCF8563_Class rtc;
-#ifdef I2C_SDA1
+#if WIRE_INTERFACES_COUNT == 2
rtc.begin(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire);
#else
rtc.begin();
#endif
tm *t = gmtime(&tv->tv_sec);
rtc.setDateTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
- LOG_DEBUG("PCF8563_RTC setDateTime %02d-%02d-%02d %02d:%02d:%02d %ld\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
- t->tm_hour, t->tm_min, t->tm_sec, tv->tv_sec);
+ LOG_DEBUG("PCF8563_RTC setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)\n", t->tm_year + 1900, t->tm_mon + 1,
+ t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, printableEpoch);
}
#elif defined(ARCH_ESP32)
settimeofday(tv, NULL);
@@ -272,4 +290,4 @@ time_t gm_mktime(struct tm *tm)
#else
return mktime(tm);
#endif
-}
\ No newline at end of file
+}
diff --git a/src/gps/ubx.h b/src/gps/ubx.h
index 0852c331d..b137d3349 100644
--- a/src/gps/ubx.h
+++ b/src/gps/ubx.h
@@ -1,3 +1,12 @@
+const char *failMessage = "Unable to %s\n";
+
+#define SEND_UBX_PACKET(TYPE, ID, DATA, ERRMSG, TIMEOUT) \
+ msglen = makeUBXPacket(TYPE, ID, sizeof(DATA), DATA); \
+ _serial_gps->write(UBXscratch, msglen); \
+ if (getACK(TYPE, ID, TIMEOUT) != GNSS_RESPONSE_OK) { \
+ LOG_WARN(failMessage, #ERRMSG); \
+ }
+
// Power Management
uint8_t GPS::_message_PMREQ[] PROGMEM = {
@@ -316,6 +325,13 @@ const uint8_t GPS::_message_SAVE[] = {
0x17 // deviceMask: BBR, Flash, EEPROM, and SPI Flash
};
+const uint8_t GPS::_message_SAVE_10[] = {
+ 0x00, 0x00, 0x00, 0x00, // clearMask: no sections cleared
+ 0xFF, 0xFF, 0x00, 0x00, // saveMask: save all sections
+ 0x00, 0x00, 0x00, 0x00, // loadMask: no sections loaded
+ 0x01 // deviceMask: only save to BBR
+};
+
// As the M10 has no flash, the best we can do to preserve the config is to set it in RAM and BBR.
// BBR will survive a restart, and power off for a while, but modules with small backup
// batteries or super caps will not retain the config for a long power off time.
@@ -335,36 +351,36 @@ const uint8_t GPS::_message_SAVE[] = {
// has details on low-power modes
/*
-CFG-PM2 has been replaced by many CFG-PM commands
-CFG-PMS has been removed
-
-CFG-PM-OPERATEMODE E1 (0 | 1 | 2) -> 1 (PSMOO), because sporadic position updates are required instead of continous tracking <10s
-(PSMCT) CFG-PM-POSUPDATEPERIOD U4 -> 0ms, no self-timed wakup because receiver power mode is controlled via "software standby
-mode" by legacy UBX-RXM-PMREQ request CFG-PM-ACQPERIOD U4 -> 0ms, because receiver power mode is controlled via "software standby
-mode" by legacy UBX-RXM-PMREQ request CFG-PM-ONTIME U4 -> 0ms, optional I guess CFG-PM-EXTINTBACKUP L -> 1, force receiver into
-BACKUP mode when EXTINT (should be connected to GPS_EN_PIN) pin is "low"
-
-This is required because the receiver never enters low power mode if microcontroller is in deep-sleep.
-Maybe the changing UART_RX levels trigger a wakeup but even with UBX-RXM-PMREQ[12] = 0x00 (all external wakeup sources disabled)
-the receivcer remains in aquisition state -> potentially a bug
-
-Workaround: Control the EXTINT pin by the GPS_EN_PIN signal
-
-As mentioned in the M10 operational issues down below, power save won't allow the use of BDS B1C.
-CFG-SIGNAL-BDS_B1C_ENA L -> 0
+OPERATEMODE E1 2 (0 | 1 | 2)
+POSUPDATEPERIOD U4 5
+ACQPERIOD U4 10
+GRIDOFFSET U4 0
+ONTIME U2 1
+MINACQTIME U1 0
+MAXACQTIME U1 0
+DONOTENTEROFF L 1
+WAITTIMEFIX L 1
+UPDATEEPH L 1
+EXTINTWAKE L 0 no ext ints
+EXTINTBACKUP L 0 no ext ints
+EXTINTINACTIVE L 0 no ext ints
+EXTINTACTIVITY U4 0 no ext ints
+LIMITPEAKCURRENT L 1
// Ram layer config message:
-// 01 01 00 00 01 00 D0 20 01 02 00 D0 40 00 00 00 00 03 00 D0 40 00 00 00 00 05 00 D0 30 00 00 0D 00 D0 10 01
+// b5 62 06 8a 26 00 00 01 00 00 01 00 d0 20 02 02 00 d0 40 05 00 00 00 05 00 d0 30 01 00 08 00 d0 10 01 09 00 d0 10 01 10 00 d0
+// 10 01 8b de
// BBR layer config message:
-// 01 02 00 00 01 00 D0 20 01 02 00 D0 40 00 00 00 00 03 00 D0 40 00 00 00 00 05 00 D0 30 00 00 0D 00 D0 10 01
+// b5 62 06 8a 26 00 00 02 00 00 01 00 d0 20 02 02 00 d0 40 05 00 00 00 05 00 d0 30 01 00 08 00 d0 10 01 09 00 d0 10 01 10 00 d0
+// 10 01 8c 03
*/
-const uint8_t GPS::_message_VALSET_PM_RAM[] = {0x01, 0x01, 0x00, 0x00, 0x0F, 0x00, 0x31, 0x10, 0x00, 0x01, 0x00, 0xD0, 0x20, 0x01,
- 0x02, 0x00, 0xD0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xD0, 0x40, 0x00, 0x00,
- 0x00, 0x00, 0x05, 0x00, 0xD0, 0x30, 0x00, 0x00, 0x0D, 0x00, 0xD0, 0x10, 0x01};
-const uint8_t GPS::_message_VALSET_PM_BBR[] = {0x01, 0x02, 0x00, 0x00, 0x0F, 0x00, 0x31, 0x10, 0x00, 0x01, 0x00, 0xD0, 0x20, 0x01,
- 0x02, 0x00, 0xD0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xD0, 0x40, 0x00, 0x00,
- 0x00, 0x00, 0x05, 0x00, 0xD0, 0x30, 0x00, 0x00, 0x0D, 0x00, 0xD0, 0x10, 0x01};
+const uint8_t GPS::_message_VALSET_PM_RAM[] = {0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0xd0, 0x20, 0x02, 0x02, 0x00, 0xd0, 0x40,
+ 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x30, 0x01, 0x00, 0x08, 0x00, 0xd0,
+ 0x10, 0x01, 0x09, 0x00, 0xd0, 0x10, 0x01, 0x10, 0x00, 0xd0, 0x10, 0x01};
+const uint8_t GPS::_message_VALSET_PM_BBR[] = {0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0xd0, 0x20, 0x02, 0x02, 0x00, 0xd0, 0x40,
+ 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0xd0, 0x30, 0x01, 0x00, 0x08, 0x00, 0xd0,
+ 0x10, 0x01, 0x09, 0x00, 0xd0, 0x10, 0x01, 0x10, 0x00, 0xd0, 0x10, 0x01};
/*
CFG-ITFM replaced by 5 valset messages which can be combined into one for RAM and one for BBR
diff --git a/src/graphics/EInkDynamicDisplay.cpp b/src/graphics/EInkDynamicDisplay.cpp
index c31941a60..ca994b2c9 100644
--- a/src/graphics/EInkDynamicDisplay.cpp
+++ b/src/graphics/EInkDynamicDisplay.cpp
@@ -1,3 +1,4 @@
+#include "Throttle.h"
#include "configuration.h"
#if defined(USE_EINK) && defined(USE_EINK_DYNAMICDISPLAY)
@@ -231,15 +232,13 @@ void EInkDynamicDisplay::checkForPromotion()
// Is it too soon for another frame of this type?
void EInkDynamicDisplay::checkRateLimiting()
{
- uint32_t now = millis();
-
// Sanity check: millis() overflow - just let the update run..
- if (previousRunMs > now)
+ if (previousRunMs > millis())
return;
// Skip update: too soon for BACKGROUND
if (frameFlags == BACKGROUND) {
- if (now - previousRunMs < EINK_LIMIT_RATE_BACKGROUND_SEC * 1000) {
+ if (Throttle::isWithinTimespanMs(previousRunMs, EINK_LIMIT_RATE_BACKGROUND_SEC * 1000)) {
refresh = SKIPPED;
reason = EXCEEDED_RATELIMIT_FULL;
return;
@@ -252,7 +251,7 @@ void EInkDynamicDisplay::checkRateLimiting()
// Skip update: too soon for RESPONSIVE
if (frameFlags & RESPONSIVE) {
- if (now - previousRunMs < EINK_LIMIT_RATE_RESPONSIVE_SEC * 1000) {
+ if (Throttle::isWithinTimespanMs(previousRunMs, EINK_LIMIT_RATE_RESPONSIVE_SEC * 1000)) {
refresh = SKIPPED;
reason = EXCEEDED_RATELIMIT_FAST;
LOG_DEBUG("refresh=SKIPPED, reason=EXCEEDED_RATELIMIT_FAST, frameFlags=0x%x\n", frameFlags);
diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp
index 04fe73e44..ad42fa979 100644
--- a/src/graphics/Screen.cpp
+++ b/src/graphics/Screen.cpp
@@ -22,6 +22,7 @@ along with this program. If not, see .
#include "Screen.h"
#include "../userPrefs.h"
#include "PowerMon.h"
+#include "Throttle.h"
#include "configuration.h"
#if HAS_SCREEN
#include
@@ -47,6 +48,7 @@ along with this program. If not, see .
#include "modules/AdminModule.h"
#include "modules/ExternalNotificationModule.h"
#include "modules/TextMessageModule.h"
+#include "modules/WaypointModule.h"
#include "sleep.h"
#include "target_specific.h"
@@ -56,10 +58,11 @@ along with this program. If not, see .
#ifdef ARCH_ESP32
#include "esp_task_wdt.h"
-#include "modules/esp32/StoreForwardModule.h"
+#include "modules/StoreForwardModule.h"
#endif
#if ARCH_PORTDUINO
+#include "modules/StoreForwardModule.h"
#include "platform/portduino/PortduinoGlue.h"
#endif
@@ -117,6 +120,7 @@ static bool heartbeat = false;
#define SCREEN_HEIGHT display->getHeight()
#include "graphics/ScreenFonts.h"
+#include
#define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2)
@@ -159,8 +163,8 @@ static void drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDispl
display->setFont(FONT_MEDIUM);
display->setTextAlignment(TEXT_ALIGN_LEFT);
-#ifdef SPLASH_TITLE_USERPREFS
- const char *title = SPLASH_TITLE_USERPREFS;
+#ifdef USERPREFS_SPLASH_TITLE
+ const char *title = USERPREFS_SPLASH_TITLE;
#else
const char *title = "meshtastic.org";
#endif
@@ -1004,55 +1008,55 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
display->setColor(WHITE);
#ifndef EXCLUDE_EMOJI
- if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\U0001F44D") == 0) {
+ if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F44D") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - thumbs_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - thumbs_height) / 2 + 2 + 5, thumbs_width, thumbs_height,
thumbup);
- } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\U0001F44E") == 0) {
+ } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F44E") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - thumbs_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - thumbs_height) / 2 + 2 + 5, thumbs_width, thumbs_height,
thumbdown);
- } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"❓") == 0) {
+ } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "❓") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - question_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - question_height) / 2 + 2 + 5, question_width, question_height,
question);
- } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"‼️") == 0) {
+ } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "‼️") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - bang_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - bang_height) / 2 + 2 + 5,
bang_width, bang_height, bang);
- } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\U0001F4A9") == 0) {
+ } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F4A9") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - poo_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - poo_height) / 2 + 2 + 5,
poo_width, poo_height, poo);
} else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\xf0\x9f\xa4\xa3") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - haha_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - haha_height) / 2 + 2 + 5,
haha_width, haha_height, haha);
- } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\U0001F44B") == 0) {
+ } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F44B") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - wave_icon_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - wave_icon_height) / 2 + 2 + 5, wave_icon_width,
wave_icon_height, wave_icon);
- } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\U0001F920") == 0) {
+ } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F920") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - cowboy_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - cowboy_height) / 2 + 2 + 5, cowboy_width, cowboy_height,
cowboy);
- } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\U0001F42D") == 0) {
+ } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\U0001F42D") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - deadmau5_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - deadmau5_height) / 2 + 2 + 5, deadmau5_width, deadmau5_height,
deadmau5);
- } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\xE2\x98\x80\xEF\xB8\x8F") == 0) {
+ } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\xE2\x98\x80\xEF\xB8\x8F") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - sun_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - sun_height) / 2 + 2 + 5,
sun_width, sun_height, sun);
- } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\u2614") == 0) {
+ } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\u2614") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - rain_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - rain_height) / 2 + 2 + 10,
rain_width, rain_height, rain);
- } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"☁️") == 0) {
+ } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "☁️") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - cloud_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - cloud_height) / 2 + 2 + 5, cloud_width, cloud_height, cloud);
- } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"🌫️") == 0) {
+ } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "🌫️") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - fog_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - fog_height) / 2 + 2 + 5,
fog_width, fog_height, fog);
- } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"\xf0\x9f\x98\x88") == 0) {
+ } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "\xf0\x9f\x98\x88") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - devil_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - devil_height) / 2 + 2 + 5, devil_width, devil_height, devil);
- } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), u8"♥️") == 0) {
+ } else if (strcmp(reinterpret_cast(mp.decoded.payload.bytes), "♥️") == 0) {
display->drawXbm(x + (SCREEN_WIDTH - heart_width) / 2,
y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - heart_height) / 2 + 2 + 5, heart_width, heart_height, heart);
} else {
@@ -1093,8 +1097,8 @@ static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const NodeStat
{
char usersString[20];
snprintf(usersString, sizeof(usersString), "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal());
-#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \
- defined(HX8357_CS)) && \
+#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
+ defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x, y + 3, 8, 8, imgUser);
#else
@@ -1285,8 +1289,8 @@ static int8_t prevFrame = -1;
// Draw the arrow pointing to a node's location
void Screen::drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, uint16_t compassDiam, float headingRadian)
{
- Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially
- float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
+ Point tip(0.0f, 0.5f), tail(0.0f, -0.35f); // pointing up initially
+ float arrowOffsetX = 0.14f, arrowOffsetY = 1.0f;
Point leftArrow(tip.x - arrowOffsetX, tip.y - arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y - arrowOffsetY);
Point *arrowPoints[] = {&tip, &tail, &leftArrow, &rightArrow};
@@ -1296,9 +1300,19 @@ void Screen::drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t com
arrowPoints[i]->scale(compassDiam * 0.6);
arrowPoints[i]->translate(compassX, compassY);
}
+ /* Old arrow
display->drawLine(tip.x, tip.y, tail.x, tail.y);
display->drawLine(leftArrow.x, leftArrow.y, tip.x, tip.y);
display->drawLine(rightArrow.x, rightArrow.y, tip.x, tip.y);
+ display->drawLine(leftArrow.x, leftArrow.y, tail.x, tail.y);
+ display->drawLine(rightArrow.x, rightArrow.y, tail.x, tail.y);
+ */
+#ifdef USE_EINK
+ display->drawTriangle(tip.x, tip.y, rightArrow.x, rightArrow.y, tail.x, tail.y);
+#else
+ display->fillTriangle(tip.x, tip.y, rightArrow.x, rightArrow.y, tail.x, tail.y);
+#endif
+ display->drawTriangle(tip.x, tip.y, leftArrow.x, leftArrow.y, tail.x, tail.y);
}
// Get a string representation of the time passed since something happened
@@ -1336,22 +1350,27 @@ void Screen::drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t co
// If north is supposed to be at the top of the compass we want rotation to be +0
if (config.display.compass_north_top)
myHeading = -0;
-
- Point N1(-0.04f, 0.65f), N2(0.04f, 0.65f);
+ /* N sign points currently not deleted*/
+ Point N1(-0.04f, 0.65f), N2(0.04f, 0.65f); // N sign points (N1-N4)
Point N3(-0.04f, 0.55f), N4(0.04f, 0.55f);
- Point *rosePoints[] = {&N1, &N2, &N3, &N4};
+ Point NC1(0.00f, 0.50f); // north circle center point
+ Point *rosePoints[] = {&N1, &N2, &N3, &N4, &NC1};
uint16_t compassDiam = Screen::getCompassDiam(SCREEN_WIDTH, SCREEN_HEIGHT);
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < 5; i++) {
// North on compass will be negative of heading
rosePoints[i]->rotate(-myHeading);
rosePoints[i]->scale(compassDiam);
rosePoints[i]->translate(compassX, compassY);
}
+
+ /* changed the N sign to a small circle on the compass circle.
display->drawLine(N1.x, N1.y, N3.x, N3.y);
display->drawLine(N2.x, N2.y, N4.x, N4.y);
display->drawLine(N1.x, N1.y, N4.x, N4.y);
+ */
+ display->drawCircle(NC1.x, NC1.y, 4); // North sign circle, 4px radius is sufficient for all displays.
}
uint16_t Screen::getCompassDiam(uint32_t displayWidth, uint32_t displayHeight)
@@ -1515,7 +1534,8 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
#elif defined(USE_SSD1306)
dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
-#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(RAK14014) || defined(HX8357_CS)
+#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7789_CS) || \
+ defined(RAK14014) || defined(HX8357_CS)
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif defined(USE_EINK) && !defined(USE_EINK_DYNAMICDISPLAY)
@@ -1654,6 +1674,11 @@ void Screen::setup()
static_cast(dispdev)->setSubtype(7);
#endif
+#if defined(USE_ST7789) && defined(TFT_MESH)
+ // Heltec T114 and T190: honor a custom text color, if defined in variant.h
+ static_cast(dispdev)->setRGB(TFT_MESH);
+#endif
+
// Initialising the UI will init the display too.
ui->init();
@@ -1707,8 +1732,11 @@ void Screen::setup()
// Standard behaviour is to FLIP the screen (needed on T-Beam). If this config item is set, unflip it, and thereby logically
// flip it. If you have a headache now, you're welcome.
if (!config.display.flip_screen) {
-#if defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(RAK14014) || defined(HX8357_CS)
+#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || \
+ defined(ST7789_CS) || defined(RAK14014) || defined(HX8357_CS)
static_cast(dispdev)->flipScreenVertically();
+#elif defined(USE_ST7789)
+ static_cast(dispdev)->flipScreenVertically();
#else
dispdev->flipScreenVertically();
#endif
@@ -1766,6 +1794,11 @@ void Screen::forceDisplay(bool forceUiUpdate)
#ifdef USE_EINK
// If requested, make sure queued commands are run, and UI has rendered a new frame
if (forceUiUpdate) {
+ // Force a display refresh, in addition to the UI update
+ // Changing the GPS status bar icon apparently doesn't register as a change in image
+ // (False negative of the image hashing algorithm used to skip identical frames)
+ EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST);
+
// No delay between UI frame rendering
setFastFramerate();
@@ -1851,13 +1884,7 @@ int32_t Screen::runOnce()
handleSetOn(false);
break;
case Cmd::ON_PRESS:
- // If a nag notification is running, stop it
- if (moduleConfig.external_notification.enabled && (externalNotificationModule->nagCycleCutoff != UINT32_MAX)) {
- externalNotificationModule->stopNow();
- } else {
- // Don't advance the screen if we just wanted to switch off the nag notification
- handleOnPress();
- }
+ handleOnPress();
break;
case Cmd::SHOW_PREV_FRAME:
handleShowPrevFrame();
@@ -1920,7 +1947,7 @@ int32_t Screen::runOnce()
if (showingNormalScreen) {
// standard screen loop handling here
if (config.display.auto_screen_carousel_secs > 0 &&
- (millis() - lastScreenTransition) > (config.display.auto_screen_carousel_secs * 1000)) {
+ !Throttle::isWithinTimespanMs(lastScreenTransition, config.display.auto_screen_carousel_secs * 1000)) {
// If an E-Ink display struggles with fast refresh, force carousel to use full refresh instead
// Carousel is potentially a major source of E-Ink display wear
@@ -2081,8 +2108,13 @@ void Screen::setFrames(FrameFocus focus)
// Check if the module being drawn has requested focus
// We will honor this request later, if setFrames was triggered by a UIFrameEvent
MeshModule *m = *i;
- if (m->isRequestingFocus())
+ if (m->isRequestingFocus()) {
fsi.positions.focusedModule = numframes;
+ }
+
+ // Identify the position of specific modules, if we need to know this later
+ if (m == waypointModule)
+ fsi.positions.waypoint = numframes;
numframes++;
}
@@ -2101,8 +2133,8 @@ void Screen::setFrames(FrameFocus focus)
#endif
// If we have a text message - show it next, unless it's a phone message and we aren't using any special modules
- fsi.positions.textMessage = numframes;
if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) {
+ fsi.positions.textMessage = numframes;
normalFrames[numframes++] = drawTextMessageFrame;
}
@@ -2164,7 +2196,7 @@ void Screen::setFrames(FrameFocus focus)
case FOCUS_PRESERVE:
// If we can identify which type of frame "originalPosition" was, can move directly to it in the new frameset
- FramesetInfo &oldFsi = this->framesetInfo;
+ const FramesetInfo &oldFsi = this->framesetInfo;
if (originalPosition == oldFsi.positions.log)
ui->switchToFrame(fsi.positions.log);
else if (originalPosition == oldFsi.positions.settings)
@@ -2204,6 +2236,31 @@ void Screen::setFrameImmediateDraw(FrameCallback *drawFrames)
setFastFramerate();
}
+// Dismisses the currently displayed screen frame, if possible
+// Relevant for text message, waypoint, others in future?
+// Triggered with a CardKB keycombo
+void Screen::dismissCurrentFrame()
+{
+ uint8_t currentFrame = ui->getUiState()->currentFrame;
+ bool dismissed = false;
+
+ if (currentFrame == framesetInfo.positions.textMessage && devicestate.has_rx_text_message) {
+ LOG_INFO("Dismissing Text Message\n");
+ devicestate.has_rx_text_message = false;
+ dismissed = true;
+ }
+
+ else if (currentFrame == framesetInfo.positions.waypoint && devicestate.has_rx_waypoint) {
+ LOG_DEBUG("Dismissing Waypoint\n");
+ devicestate.has_rx_waypoint = false;
+ dismissed = true;
+ }
+
+ // If we did make changes to dismiss, we now need to regenerate the frameset
+ if (dismissed)
+ setFrames();
+}
+
void Screen::handleStartFirmwareUpdateScreen()
{
LOG_DEBUG("showing firmware screen\n");
@@ -2413,10 +2470,10 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
// Draw our hardware ID to assist with bluetooth pairing. Either prefix with Info or S&F Logo
if (moduleConfig.store_forward.enabled) {
#ifdef ARCH_ESP32
- if (millis() - storeForwardModule->lastHeartbeat >
- (storeForwardModule->heartbeatInterval * 1200)) { // no heartbeat, overlap a bit
-#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \
- defined(HX8357_CS)) && \
+ if (!Throttle::isWithinTimespanMs(storeForwardModule->lastHeartbeat,
+ (storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit
+#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
+ defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || ARCH_PORTDUINO) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
imgQuestionL1);
@@ -2427,8 +2484,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
imgQuestion);
#endif
} else {
-#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \
- defined(HX8357_CS)) && \
+#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
+ defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 16, 8,
imgSFL1);
@@ -2442,8 +2499,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
#endif
} else {
// TODO: Raspberry Pi supports more than just the one screen size
-#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \
- defined(HX8357_CS) || ARCH_PORTDUINO) && \
+#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
+ defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || ARCH_PORTDUINO) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
imgInfoL1);
@@ -2716,12 +2773,23 @@ int Screen::handleInputEvent(const InputEvent *event)
}
#endif
- if (showingNormalScreen && moduleFrames.size() == 0) {
- // LOG_DEBUG("Screen::handleInputEvent from %s\n", event->source);
- if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) {
- showPrevFrame();
- } else if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) {
- showNextFrame();
+ // Use left or right input from a keyboard to move between frames,
+ // so long as a mesh module isn't using these events for some other purpose
+ if (showingNormalScreen) {
+
+ // Ask any MeshModules if they're handling keyboard input right now
+ bool inputIntercepted = false;
+ for (MeshModule *module : moduleFrames) {
+ if (module->interceptingKeyboardInput())
+ inputIntercepted = true;
+ }
+
+ // If no modules are using the input, move between frames
+ if (!inputIntercepted) {
+ if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT))
+ showPrevFrame();
+ else if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT))
+ showNextFrame();
}
}
diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h
index 021b11bda..b2e6e905b 100644
--- a/src/graphics/Screen.h
+++ b/src/graphics/Screen.h
@@ -454,6 +454,9 @@ class Screen : public concurrency::OSThread
void setWelcomeFrames();
+ // Dismiss the currently focussed frame, if possible (e.g. text message, waypoint)
+ void dismissCurrentFrame();
+
#ifdef USE_EINK
/// Draw an image to remain on E-Ink display after screen off
void setScreensaverFrames(FrameCallback einkScreensaver = NULL);
@@ -503,11 +506,14 @@ class Screen : public concurrency::OSThread
void handleStartFirmwareUpdateScreen();
// Info collected by setFrames method.
- // Index location of specific frames. Used to apply the FrameFocus parameter of setFrames
+ // Index location of specific frames.
+ // - Used to apply the FrameFocus parameter of setFrames
+ // - Used to dismiss the currently shown frame (txt; waypoint) by CardKB combo
struct FramesetInfo {
struct FramePositions {
uint8_t fault = 0;
uint8_t textMessage = 0;
+ uint8_t waypoint = 0;
uint8_t focusedModule = 0;
uint8_t log = 0;
uint8_t settings = 0;
diff --git a/src/graphics/ScreenFonts.h b/src/graphics/ScreenFonts.h
index 267f6e0a8..c9ce961fa 100644
--- a/src/graphics/ScreenFonts.h
+++ b/src/graphics/ScreenFonts.h
@@ -12,8 +12,8 @@
#include "graphics/fonts/OLEDDisplayFontsUA.h"
#endif
-#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \
- defined(HX8357_CS)) && \
+#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
+ defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
// The screen is bigger so use bigger fonts
#define FONT_SMALL ArialMT_Plain_16 // Height: 19
@@ -41,4 +41,4 @@
#define FONT_HEIGHT_SMALL _fontHeight(FONT_SMALL)
#define FONT_HEIGHT_MEDIUM _fontHeight(FONT_MEDIUM)
-#define FONT_HEIGHT_LARGE _fontHeight(FONT_LARGE)
\ No newline at end of file
+#define FONT_HEIGHT_LARGE _fontHeight(FONT_LARGE)
diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp
index 2849dd9a9..0c32a7c32 100644
--- a/src/graphics/TFTDisplay.cpp
+++ b/src/graphics/TFTDisplay.cpp
@@ -244,9 +244,9 @@ class LGFX : public lgfx::LGFX_Device
static LGFX *tft = nullptr;
-#elif defined(ILI9341_DRIVER)
+#elif defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER)
-#include // Graphics and font library for ILI9341 driver chip
+#include // Graphics and font library for ILI9341/ILI9342 driver chip
#if defined(ILI9341_BACKLIGHT_EN) && !defined(TFT_BL)
#define TFT_BL ILI9341_BACKLIGHT_EN
@@ -254,7 +254,11 @@ static LGFX *tft = nullptr;
class LGFX : public lgfx::LGFX_Device
{
+#if defined(ILI9341_DRIVER)
lgfx::Panel_ILI9341 _panel_instance;
+#elif defined(ILI9342_DRIVER)
+ lgfx::Panel_ILI9342 _panel_instance;
+#endif
lgfx::Bus_SPI _bus_instance;
lgfx::Light_PWM _light_instance;
@@ -265,7 +269,11 @@ class LGFX : public lgfx::LGFX_Device
auto cfg = _bus_instance.config();
// configure SPI
+#if defined(ILI9341_DRIVER)
cfg.spi_host = ILI9341_SPI_HOST; // ESP32-S2,S3,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST
+#elif defined(ILI9342_DRIVER)
+ cfg.spi_host = ILI9342_SPI_HOST; // ESP32-S2,S3,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST
+#endif
cfg.spi_mode = 0;
cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by dividing
// 80MHz by an integer)
@@ -336,7 +344,7 @@ class LGFX : public lgfx::LGFX_Device
static LGFX *tft = nullptr;
#elif defined(ST7735_CS)
-#include // Graphics and font library for ILI9341 driver chip
+#include // Graphics and font library for ILI9342 driver chip
static TFT_eSPI *tft = nullptr; // Invoke library, pins defined in User_Setup.h
#elif ARCH_PORTDUINO && HAS_SCREEN != 0
@@ -360,6 +368,8 @@ class LGFX : public lgfx::LGFX_Device
_panel_instance = new lgfx::Panel_ST7735S;
else if (settingsMap[displayPanel] == ili9341)
_panel_instance = new lgfx::Panel_ILI9341;
+ else if (settingsMap[displayPanel] == ili9342)
+ _panel_instance = new lgfx::Panel_ILI9342;
auto buscfg = _bus_instance.config();
buscfg.spi_mode = 0;
buscfg.spi_host = settingsMap[displayspidev];
@@ -500,10 +510,126 @@ class LGFX : public lgfx::LGFX_Device
static LGFX *tft = nullptr;
+#elif defined(ST7701_CS)
+#include // Graphics and font library for ST7701 driver chip
+#include
+#include
+
+class LGFX : public lgfx::LGFX_Device
+{
+ lgfx::Panel_ST7701 _panel_instance;
+ lgfx::Bus_RGB _bus_instance;
+ lgfx::Light_PWM _light_instance;
+ lgfx::Touch_FT5x06 _touch_instance;
+
+ public:
+ LGFX(void)
+ {
+ {
+ auto cfg = _panel_instance.config();
+ cfg.memory_width = 800;
+ cfg.memory_height = 480;
+ cfg.panel_width = TFT_WIDTH;
+ cfg.panel_height = TFT_HEIGHT;
+ cfg.offset_x = TFT_OFFSET_X;
+ cfg.offset_y = TFT_OFFSET_Y;
+ _panel_instance.config(cfg);
+ }
+
+ {
+ auto cfg = _panel_instance.config_detail();
+ cfg.pin_cs = ST7701_CS;
+ cfg.pin_sclk = ST7701_SCK;
+ cfg.pin_mosi = ST7701_SDA;
+ // cfg.use_psram = 1;
+ _panel_instance.config_detail(cfg);
+ }
+
+ {
+ auto cfg = _bus_instance.config();
+ cfg.panel = &_panel_instance;
+#ifdef SENSECAP_INDICATOR
+ cfg.pin_d0 = GPIO_NUM_15; // B0
+ cfg.pin_d1 = GPIO_NUM_14; // B1
+ cfg.pin_d2 = GPIO_NUM_13; // B2
+ cfg.pin_d3 = GPIO_NUM_12; // B3
+ cfg.pin_d4 = GPIO_NUM_11; // B4
+
+ cfg.pin_d5 = GPIO_NUM_10; // G0
+ cfg.pin_d6 = GPIO_NUM_9; // G1
+ cfg.pin_d7 = GPIO_NUM_8; // G2
+ cfg.pin_d8 = GPIO_NUM_7; // G3
+ cfg.pin_d9 = GPIO_NUM_6; // G4
+ cfg.pin_d10 = GPIO_NUM_5; // G5
+
+ cfg.pin_d11 = GPIO_NUM_4; // R0
+ cfg.pin_d12 = GPIO_NUM_3; // R1
+ cfg.pin_d13 = GPIO_NUM_2; // R2
+ cfg.pin_d14 = GPIO_NUM_1; // R3
+ cfg.pin_d15 = GPIO_NUM_0; // R4
+
+ cfg.pin_henable = GPIO_NUM_18;
+ cfg.pin_vsync = GPIO_NUM_17;
+ cfg.pin_hsync = GPIO_NUM_16;
+ cfg.pin_pclk = GPIO_NUM_21;
+ cfg.freq_write = 12000000;
+
+ cfg.hsync_polarity = 0;
+ cfg.hsync_front_porch = 10;
+ cfg.hsync_pulse_width = 8;
+ cfg.hsync_back_porch = 50;
+
+ cfg.vsync_polarity = 0;
+ cfg.vsync_front_porch = 10;
+ cfg.vsync_pulse_width = 8;
+ cfg.vsync_back_porch = 20;
+
+ cfg.pclk_active_neg = 0;
+ cfg.de_idle_high = 1;
+ cfg.pclk_idle_high = 0;
+#endif
+ _bus_instance.config(cfg);
+ }
+ _panel_instance.setBus(&_bus_instance);
+
+ {
+ auto cfg = _light_instance.config();
+ cfg.pin_bl = ST7701_BL;
+ _light_instance.config(cfg);
+ }
+ _panel_instance.light(&_light_instance);
+
+ {
+ auto cfg = _touch_instance.config();
+ cfg.pin_cs = -1;
+ cfg.x_min = 0;
+ cfg.x_max = 479;
+ cfg.y_min = 0;
+ cfg.y_max = 479;
+ cfg.pin_int = -1; // don't use SCREEN_TOUCH_INT;
+ cfg.pin_rst = SCREEN_TOUCH_RST;
+ cfg.bus_shared = true;
+ cfg.offset_rotation = TFT_OFFSET_ROTATION;
+
+ cfg.i2c_port = TOUCH_I2C_PORT;
+ cfg.i2c_addr = TOUCH_SLAVE_ADDRESS;
+ cfg.pin_sda = I2C_SDA;
+ cfg.pin_scl = I2C_SCL;
+ cfg.freq = 400000;
+ _touch_instance.config(cfg);
+ _panel_instance.setTouch(&_touch_instance);
+ }
+
+ setPanel(&_panel_instance);
+ }
+};
+
+static LGFX *tft = nullptr;
+
#endif
-#if defined(ST7735_CS) || defined(ST7789_CS) || defined(ILI9341_DRIVER) || defined(RAK14014) || defined(HX8357_CS) || \
- (ARCH_PORTDUINO && HAS_SCREEN != 0)
+#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ST7789_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
+ defined(RAK14014) || defined(HX8357_CS) || (ARCH_PORTDUINO && HAS_SCREEN != 0)
#include "SPILock.h"
#include "TFTDisplay.h"
#include
@@ -709,7 +835,6 @@ bool TFTDisplay::connect()
#ifdef UNPHONE
unphone.backlight(true); // using unPhone library
- LOG_INFO("Power to TFT Backlight\n");
#endif
tft->init();
@@ -725,7 +850,7 @@ bool TFTDisplay::connect()
attachInterrupt(digitalPinToInterrupt(SCREEN_TOUCH_INT), rak14014_tpIntHandle, FALLING);
#elif defined(T_DECK) || defined(PICOMPUTER_S3) || defined(CHATTER_2)
tft->setRotation(1); // T-Deck has the TFT in landscape
-#elif defined(T_WATCH_S3)
+#elif defined(T_WATCH_S3) || defined(SENSECAP_INDICATOR)
tft->setRotation(2); // T-Watch S3 left-handed orientation
#else
tft->setRotation(3); // Orient horizontal and wide underneath the silkscreen name label
@@ -735,4 +860,4 @@ bool TFTDisplay::connect()
return true;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/src/graphics/images.h b/src/graphics/images.h
index ab3767a89..2b0854a33 100644
--- a/src/graphics/images.h
+++ b/src/graphics/images.h
@@ -20,8 +20,8 @@ const uint8_t bluetoothConnectedIcon[36] PROGMEM = {0xfe, 0x01, 0xff, 0x03, 0x03
0xfe, 0x31, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0x3f, 0xe0, 0x1f};
#endif
-#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS) || defined(USE_ST7789) || \
- defined(HX8357_CS) || ARCH_PORTDUINO) && \
+#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
+ defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || ARCH_PORTDUINO) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
const uint8_t imgQuestionL1[] PROGMEM = {0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff};
const uint8_t imgQuestionL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f};
diff --git a/src/graphics/img/icon.xbm b/src/graphics/img/icon.xbm
index f90cf4946..4e78ae8a1 100644
--- a/src/graphics/img/icon.xbm
+++ b/src/graphics/img/icon.xbm
@@ -1,4 +1,4 @@
-#ifndef HAS_USERPREFS_SPLASH
+#ifndef USERPREFS_HAS_SPLASH
#define icon_width 50
#define icon_height 28
static uint8_t icon_bits[] = {
diff --git a/src/input/ExpressLRSFiveWay.cpp b/src/input/ExpressLRSFiveWay.cpp
index c444800ba..af4433dae 100644
--- a/src/input/ExpressLRSFiveWay.cpp
+++ b/src/input/ExpressLRSFiveWay.cpp
@@ -1,5 +1,5 @@
-
#include "ExpressLRSFiveWay.h"
+#include "Throttle.h"
#ifdef INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE
@@ -76,11 +76,10 @@ void ExpressLRSFiveWay::update(int *keyValue, bool *keyLongPressed)
*keyValue = NO_PRESS;
int newKey = readKey();
- uint32_t now = millis();
if (keyInProcess == NO_PRESS) {
// New key down
if (newKey != NO_PRESS) {
- keyDownStart = now;
+ keyDownStart = millis();
// DBGLN("down=%u", newKey);
}
} else {
@@ -88,7 +87,7 @@ void ExpressLRSFiveWay::update(int *keyValue, bool *keyLongPressed)
if (newKey == NO_PRESS) {
// DBGLN("up=%u", keyInProcess);
if (!isLongPressed) {
- if ((now - keyDownStart) > KEY_DEBOUNCE_MS) {
+ if (!Throttle::isWithinTimespanMs(keyDownStart, KEY_DEBOUNCE_MS)) {
*keyValue = keyInProcess;
*keyLongPressed = false;
}
@@ -101,7 +100,7 @@ void ExpressLRSFiveWay::update(int *keyValue, bool *keyLongPressed)
}
// else still pressing, waiting for long if not already signaled
else if (!isLongPressed) {
- if ((now - keyDownStart) > KEY_LONG_PRESS_MS) {
+ if (!Throttle::isWithinTimespanMs(keyDownStart, KEY_LONG_PRESS_MS)) {
*keyValue = keyInProcess;
*keyLongPressed = true;
isLongPressed = true;
diff --git a/src/input/InputBroker.h b/src/input/InputBroker.h
index 57c25af4b..db7524bb0 100644
--- a/src/input/InputBroker.h
+++ b/src/input/InputBroker.h
@@ -4,6 +4,22 @@
#define ANYKEY 0xFF
#define MATRIXKEY 0xFE
+#define INPUT_BROKER_MSG_BRIGHTNESS_UP 0x11
+#define INPUT_BROKER_MSG_BRIGHTNESS_DOWN 0x12
+#define INPUT_BROKER_MSG_REBOOT 0x90
+#define INPUT_BROKER_MSG_SHUTDOWN 0x9b
+#define INPUT_BROKER_MSG_GPS_TOGGLE 0x9e
+#define INPUT_BROKER_MSG_MUTE_TOGGLE 0xac
+#define INPUT_BROKER_MSG_SEND_PING 0xaf
+#define INPUT_BROKER_MSG_DISMISS_FRAME 0x8b
+#define INPUT_BROKER_MSG_LEFT 0xb4
+#define INPUT_BROKER_MSG_UP 0xb5
+#define INPUT_BROKER_MSG_DOWN 0xb6
+#define INPUT_BROKER_MSG_RIGHT 0xb7
+#define INPUT_BROKER_MSG_FN_SYMBOL_ON 0xf1
+#define INPUT_BROKER_MSG_FN_SYMBOL_OFF 0xf2
+#define INPUT_BROKER_MSG_BLUETOOTH_TOGGLE 0xAA
+
typedef struct _InputEvent {
const char *source;
char inputEvent;
diff --git a/src/input/LinuxInput.cpp b/src/input/LinuxInput.cpp
index 6194195ed..57a87b0ef 100644
--- a/src/input/LinuxInput.cpp
+++ b/src/input/LinuxInput.cpp
@@ -147,11 +147,11 @@ int32_t LinuxInput::runOnce()
case KEY_LEFT: // Left
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT;
break;
- e.kbchar = 0xb4;
+ e.kbchar = INPUT_BROKER_MSG_LEFT;
case KEY_RIGHT: // Right
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
break;
- e.kbchar = 0xb7;
+ e.kbchar = INPUT_BROKER_MSG_RIGHT;
case KEY_ENTER: // Enter
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
break;
diff --git a/src/input/ScanAndSelect.cpp b/src/input/ScanAndSelect.cpp
index d693d768c..65ca7e332 100644
--- a/src/input/ScanAndSelect.cpp
+++ b/src/input/ScanAndSelect.cpp
@@ -6,6 +6,7 @@
#include "ScanAndSelect.h"
#include "modules/CannedMessageModule.h"
+#include
// Config
static const char name[] = "scanAndSelect"; // should match "allow input source" string
@@ -75,7 +76,7 @@ int32_t ScanAndSelectInput::runOnce()
else {
// Duration enough for long press
// Long press not yet fired (prevent repeat firing while held)
- if (!longPressFired && now - downSinceMs > durationLongMs) {
+ if (!longPressFired && Throttle::isWithinTimespanMs(downSinceMs, durationLongMs)) {
longPressFired = true;
longPress();
}
@@ -91,7 +92,7 @@ int32_t ScanAndSelectInput::runOnce()
// Long press event didn't already fire
if (held && !longPressFired) {
// Duration enough for short press
- if (now - downSinceMs > durationShortMs) {
+ if (!Throttle::isWithinTimespanMs(downSinceMs, durationShortMs)) {
shortPress();
}
}
diff --git a/src/input/SerialKeyboard.cpp b/src/input/SerialKeyboard.cpp
index fa3eb2528..4827e8995 100644
--- a/src/input/SerialKeyboard.cpp
+++ b/src/input/SerialKeyboard.cpp
@@ -1,5 +1,6 @@
#include "SerialKeyboard.h"
#include "configuration.h"
+#include
#ifdef INPUTBROKER_SERIAL_TYPE
#define CANNED_MESSAGE_MODULE_ENABLE 1 // in case it's not set in the variant file
@@ -73,7 +74,7 @@ int32_t SerialKeyboard::runOnce()
// Serial.print ("X");
// Serial.println (shiftRegister2, BIN);
- if (millis() - lastPressTime > 500) {
+ if (!Throttle::isWithinTimespanMs(lastPressTime, 500)) {
quickPress = 0;
}
@@ -87,7 +88,7 @@ int32_t SerialKeyboard::runOnce()
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
} else if (!(shiftRegister2 & (1 << 2))) {
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
- e.kbchar = 0xb7;
+ e.kbchar = INPUT_BROKER_MSG_RIGHT;
} else if (!(shiftRegister2 & (1 << 1))) {
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
} else if (!(shiftRegister2 & (1 << 0))) {
diff --git a/src/input/cardKbI2cImpl.cpp b/src/input/cardKbI2cImpl.cpp
index 1bff49475..f1df6b137 100644
--- a/src/input/cardKbI2cImpl.cpp
+++ b/src/input/cardKbI2cImpl.cpp
@@ -9,14 +9,14 @@ CardKbI2cImpl::CardKbI2cImpl() : KbI2cBase("cardKB") {}
void CardKbI2cImpl::init()
{
-#ifndef ARCH_PORTDUINO
+#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO)
if (cardkb_found.address == 0x00) {
LOG_DEBUG("Rescanning for I2C keyboard\n");
uint8_t i2caddr_scan[] = {CARDKB_ADDR, TDECK_KB_ADDR, BBQ10_KB_ADDR};
uint8_t i2caddr_asize = 3;
auto i2cScanner = std::unique_ptr(new ScanI2CTwoWire());
-#if defined(I2C_SDA1)
+#if WIRE_INTERFACES_COUNT == 2
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1, i2caddr_scan, i2caddr_asize);
#endif
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE, i2caddr_scan, i2caddr_asize);
@@ -57,4 +57,4 @@ void CardKbI2cImpl::init()
}
#endif
inputBroker->registerSource(this);
-}
+}
\ No newline at end of file
diff --git a/src/input/kbI2cBase.cpp b/src/input/kbI2cBase.cpp
index 024b16b9e..8b201cd22 100644
--- a/src/input/kbI2cBase.cpp
+++ b/src/input/kbI2cBase.cpp
@@ -33,7 +33,7 @@ int32_t KbI2cBase::runOnce()
if (!i2cBus) {
switch (cardkb_found.port) {
case ScanI2C::WIRE1:
-#ifdef I2C_SDA1
+#if WIRE_INTERFACES_COUNT == 2
LOG_DEBUG("Using I2C Bus 1 (the second one)\n");
i2cBus = &Wire1;
if (cardkb_found.address == BBQ10_KB_ADDR) {
@@ -94,7 +94,7 @@ int32_t KbI2cBase::runOnce()
case 'e': // sym e
if (is_sym) {
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
- e.kbchar = 0xb5;
+ e.kbchar = INPUT_BROKER_MSG_UP;
is_sym = false; // reset sym state after second keypress
} else {
e.inputEvent = ANYKEY;
@@ -104,7 +104,7 @@ int32_t KbI2cBase::runOnce()
case 'x': // sym x
if (is_sym) {
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN;
- e.kbchar = 0xb6;
+ e.kbchar = INPUT_BROKER_MSG_DOWN;
is_sym = false; // reset sym state after second keypress
} else {
e.inputEvent = ANYKEY;
@@ -134,8 +134,8 @@ int32_t KbI2cBase::runOnce()
case 0x13: // Code scanner says the SYM key is 0x13
is_sym = !is_sym;
e.inputEvent = ANYKEY;
- e.kbchar =
- is_sym ? 0xf1 : 0xf2; // send 0xf1 to tell CannedMessages to display that the modifier key is active
+ e.kbchar = is_sym ? INPUT_BROKER_MSG_FN_SYMBOL_ON // send 0xf1 to tell CannedMessages to display that
+ : INPUT_BROKER_MSG_FN_SYMBOL_OFF; // the modifier key is active
break;
case 0x0a: // apparently Enter on Q10 is a line feed instead of carriage return
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
@@ -214,7 +214,7 @@ int32_t KbI2cBase::runOnce()
if (is_sym) {
is_sym = false;
e.inputEvent = ANYKEY;
- e.kbchar = 0xac; // mute notifications
+ e.kbchar = INPUT_BROKER_MSG_MUTE_TOGGLE; // mute notifications
} else {
e.inputEvent = ANYKEY;
e.kbchar = c;
@@ -224,7 +224,7 @@ int32_t KbI2cBase::runOnce()
if (is_sym) {
is_sym = false;
e.inputEvent = ANYKEY;
- e.kbchar = 0x11; // Increase Brightness code
+ e.kbchar = INPUT_BROKER_MSG_BRIGHTNESS_UP; // Increase Brightness code
} else {
e.inputEvent = ANYKEY;
e.kbchar = c;
@@ -234,7 +234,7 @@ int32_t KbI2cBase::runOnce()
if (is_sym) {
is_sym = false;
e.inputEvent = ANYKEY;
- e.kbchar = 0x12; // Decrease Brightness code
+ e.kbchar = INPUT_BROKER_MSG_BRIGHTNESS_DOWN; // Decrease Brightness code
} else {
e.inputEvent = ANYKEY;
e.kbchar = c;
@@ -244,7 +244,7 @@ int32_t KbI2cBase::runOnce()
if (is_sym) {
is_sym = false;
e.inputEvent = ANYKEY;
- e.kbchar = 0xaf; // (fn + space)
+ e.kbchar = INPUT_BROKER_MSG_SEND_PING; // (fn + space)
} else {
e.inputEvent = ANYKEY;
e.kbchar = c;
@@ -254,7 +254,7 @@ int32_t KbI2cBase::runOnce()
if (is_sym) {
is_sym = false;
e.inputEvent = ANYKEY;
- e.kbchar = 0x9e;
+ e.kbchar = INPUT_BROKER_MSG_GPS_TOGGLE;
} else {
e.inputEvent = ANYKEY;
e.kbchar = c;
@@ -269,32 +269,35 @@ int32_t KbI2cBase::runOnce()
break;
case 0xb5: // Up
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
- e.kbchar = 0xb5;
+ e.kbchar = INPUT_BROKER_MSG_UP;
break;
case 0xb6: // Down
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN;
- e.kbchar = 0xb6;
+ e.kbchar = INPUT_BROKER_MSG_DOWN;
break;
case 0xb4: // Left
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT;
- e.kbchar = 0xb4;
+ e.kbchar = INPUT_BROKER_MSG_LEFT;
break;
case 0xb7: // Right
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
- e.kbchar = 0xb7;
+ e.kbchar = INPUT_BROKER_MSG_RIGHT;
break;
case 0xc: // Modifier key: 0xc is alt+c (Other options could be: 0xea = shift+mic button or 0x4 shift+$(speaker))
// toggle moddifiers button.
is_sym = !is_sym;
e.inputEvent = ANYKEY;
- e.kbchar = is_sym ? 0xf1 : 0xf2; // send 0xf1 to tell CannedMessages to display that the modifier key is active
+ e.kbchar = is_sym ? INPUT_BROKER_MSG_FN_SYMBOL_ON // send 0xf1 to tell CannedMessages to display that the
+ : INPUT_BROKER_MSG_FN_SYMBOL_OFF; // modifier key is active
break;
- case 0x90: // fn+r
+ case 0x90: // fn+r INPUT_BROKER_MSG_REBOOT
case 0x91: // fn+t
- case 0x9b: // fn+s
- case 0xac: // fn+m
- case 0x9e: // fn+g
- case 0xaf: // fn+space
+ case 0x9b: // fn+s INPUT_BROKER_MSG_SHUTDOWN
+ case 0xac: // fn+m INPUT_BROKER_MSG_MUTE_TOGGLE
+ case 0x9e: // fn+g INPUT_BROKER_MSG_GPS_TOGGLE
+ case 0xaf: // fn+space INPUT_BROKER_MSG_SEND_PING
+ case 0x8b: // fn+del INPUT_BROKEN_MSG_DISMISS_FRAME
+ case 0xAA: // fn+b INPUT_BROKER_MSG_BLUETOOTH_TOGGLE
// just pass those unmodified
e.inputEvent = ANYKEY;
e.kbchar = c;
diff --git a/src/main.cpp b/src/main.cpp
index c8345eaa1..a792f2e47 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,3 +1,4 @@
+#include "../userPrefs.h"
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_GPS
#include "GPS.h"
@@ -11,16 +12,19 @@
#include "airtime.h"
#include "buzz.h"
-#include "error.h"
-#include "power.h"
-// #include "debug.h"
#include "FSCommon.h"
#include "Led.h"
#include "SPILock.h"
+#include "Throttle.h"
#include "concurrency/OSThread.h"
#include "concurrency/Periodic.h"
#include "detect/ScanI2C.h"
+<<<<<<< HEAD
#include "gps/RTC.h"
+=======
+#include "error.h"
+#include "power.h"
+>>>>>>> master
#if !MESHTASTIC_EXCLUDE_I2C
#include "detect/ScanI2CTwoWire.h"
@@ -33,13 +37,13 @@
#include "graphics/Screen.h"
#include "main.h"
#include "mesh/generated/meshtastic/config.pb.h"
+#include "meshUtils.h"
#include "modules/Modules.h"
#include "shutdown.h"
#include "sleep.h"
#include "target_specific.h"
#include
#include
-// #include
#ifdef ARCH_ESP32
#if !MESHTASTIC_EXCLUDE_WEBSERVER
@@ -73,6 +77,7 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr;
#include "LLCC68Interface.h"
#include "LR1110Interface.h"
#include "LR1120Interface.h"
+#include "LR1121Interface.h"
#include "RF95Interface.h"
#include "SX1262Interface.h"
#include "SX1268Interface.h"
@@ -103,8 +108,13 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr;
#include "AmbientLightingThread.h"
#include "PowerFSMThread.h"
+<<<<<<< HEAD
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(ARCH_APOLLO3) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#include "AccelerometerThread.h"
+=======
+#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C
+#include "motion/AccelerometerThread.h"
+>>>>>>> master
AccelerometerThread *accelerometerThread = nullptr;
#endif
@@ -119,6 +129,8 @@ float tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE; // if TCXO is optional, put this h
using namespace concurrency;
+volatile static const char slipstreamTZString[] = USERPREFS_TZ_STRING;
+
// We always create a screen object, but we only init it if we find the hardware
graphics::Screen *screen = nullptr;
@@ -323,15 +335,19 @@ void setup()
#ifdef BUTTON_PIN
#ifdef ARCH_ESP32
- // If the button is connected to GPIO 12, don't enable the ability to use
- // meshtasticAdmin on the device.
- pinMode(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, INPUT);
-
+#if ESP_ARDUINO_VERSION_MAJOR >= 3
+#ifdef BUTTON_NEED_PULLUP
+ pinMode(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, INPUT_PULLUP);
+#else
+ pinMode(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, INPUT); // default to BUTTON_PIN
+#endif
+#else
+ pinMode(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN, INPUT); // default to BUTTON_PIN
#ifdef BUTTON_NEED_PULLUP
gpio_pullup_en((gpio_num_t)(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN));
delay(10);
#endif
-
+#endif
#endif
#endif
@@ -361,6 +377,8 @@ void setup()
Wire1.begin();
#elif defined(I2C_SDA1) && !defined(ARCH_RP2040)
Wire1.begin(I2C_SDA1, I2C_SCL1);
+#elif WIRE_INTERFACES_COUNT == 2
+ Wire1.begin();
#endif
#if defined(I2C_SDA) && defined(ARCH_RP2040)
@@ -391,7 +409,7 @@ void setup()
#endif
#ifdef AQ_SET_PIN
- // RAK-12039 set pin for Air quality sensor
+ // RAK-12039 set pin for Air quality sensor. Detectable on I2C after ~3 seconds, so we need to rescan later
pinMode(AQ_SET_PIN, OUTPUT);
digitalWrite(AQ_SET_PIN, HIGH);
#endif
@@ -428,6 +446,8 @@ void setup()
#elif defined(I2C_SDA1) && !defined(ARCH_RP2040)
Wire1.begin(I2C_SDA1, I2C_SCL1);
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1);
+#elif defined(NRF52840_XXAA) && (WIRE_INTERFACES_COUNT == 2)
+ i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1);
#endif
#if defined(I2C_SDA) && defined(ARCH_RP2040)
@@ -553,6 +573,7 @@ void setup()
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA260, meshtastic_TelemetrySensorType_INA260)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA219, meshtastic_TelemetrySensorType_INA219)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221)
+ SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MAX17048, meshtastic_TelemetrySensorType_MAX17048)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MCP9808, meshtastic_TelemetrySensorType_MCP9808)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MCP9808, meshtastic_TelemetrySensorType_MCP9808)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::SHT31, meshtastic_TelemetrySensorType_SHT31)
@@ -561,15 +582,19 @@ void setup()
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::QMC6310, meshtastic_TelemetrySensorType_QMC6310)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::QMI8658, meshtastic_TelemetrySensorType_QMI8658)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::QMC5883L, meshtastic_TelemetrySensorType_QMC5883L)
+ SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::HMC5883L, meshtastic_TelemetrySensorType_QMC5883L)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::PMSA0031, meshtastic_TelemetrySensorType_PMSA003I)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::RCWL9620, meshtastic_TelemetrySensorType_RCWL9620)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::VEML7700, meshtastic_TelemetrySensorType_VEML7700)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::TSL2591, meshtastic_TelemetrySensorType_TSL25911FN)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::OPT3001, meshtastic_TelemetrySensorType_OPT3001)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MLX90632, meshtastic_TelemetrySensorType_MLX90632)
+ SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MLX90614, meshtastic_TelemetrySensorType_MLX90614)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::SHT4X, meshtastic_TelemetrySensorType_SHT4X)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::AHT10, meshtastic_TelemetrySensorType_AHT10)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::DFROBOT_LARK, meshtastic_TelemetrySensorType_DFROBOT_LARK)
+ SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::ICM20948, meshtastic_TelemetrySensorType_ICM20948)
+ SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MAX30102, meshtastic_TelemetrySensorType_MAX30102)
i2cScanner.reset();
#endif
@@ -587,6 +612,9 @@ void setup()
// Hello
printInfo();
+#ifdef BUILD_EPOCH
+ LOG_INFO("Build timestamp: %ld\n", BUILD_EPOCH);
+#endif
#ifdef ARCH_ESP32
esp32Setup();
@@ -618,7 +646,13 @@ void setup()
buttonThread = new ButtonThread();
#endif
- playStartMelody();
+ // only play start melody when role is not tracker or sensor
+ if (config.power.is_power_saving == true &&
+ IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_TRACKER,
+ meshtastic_Config_DeviceConfig_Role_TAK_TRACKER, meshtastic_Config_DeviceConfig_Role_SENSOR))
+ LOG_DEBUG("Tracker/Sensor: Skipping start melody\n");
+ else
+ playStartMelody();
// fixed screen override?
if (config.display.oled != meshtastic_Config_DisplayConfig_OledType_OLED_AUTO)
@@ -634,10 +668,8 @@ void setup()
#endif
#if !MESHTASTIC_EXCLUDE_I2C
-#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(ARCH_APOLLO3) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(ARCH_APOLLO3)
if (acc_info.type != ScanI2C::DeviceType::NONE) {
- config.display.wake_on_tap_or_motion = true;
- moduleConfig.external_notification.enabled = true;
accelerometerThread = new AccelerometerThread(acc_info.type);
}
#endif
@@ -688,10 +720,17 @@ void setup()
// setup TZ prior to time actions.
#if !MESHTASTIC_EXCLUDE_TZ
- if (*config.device.tzdef) {
+ LOG_DEBUG("Using compiled/slipstreamed %s\n", slipstreamTZString); // important, removing this clobbers our magic string
+ if (*config.device.tzdef && config.device.tzdef[0] != 0) {
+ LOG_DEBUG("Saved TZ: %s \n", config.device.tzdef);
setenv("TZ", config.device.tzdef, 1);
} else {
- setenv("TZ", "GMT0", 1);
+ if (strncmp((const char *)slipstreamTZString, "tzpl", 4) == 0) {
+ setenv("TZ", "GMT0", 1);
+ } else {
+ setenv("TZ", (const char *)slipstreamTZString, 1);
+ strcpy(config.device.tzdef, (const char *)slipstreamTZString);
+ }
}
tzset();
LOG_DEBUG("Set Timezone to %s\n", getenv("TZ"));
@@ -748,8 +787,8 @@ void setup()
#if !MESHTASTIC_EXCLUDE_I2C
// Don't call screen setup until after nodedb is setup (because we need
// the current region name)
-#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(HX8357_CS) || \
- defined(USE_ST7789)
+#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
+ defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789)
screen->setup();
#elif defined(ARCH_PORTDUINO)
if (screen_found.port != ScanI2C::I2CPort::NO_I2C || settingsMap[displayPanel]) {
@@ -763,12 +802,6 @@ void setup()
screen->print("Started...\n");
-#ifdef SX126X_ANT_SW
- // make analog PA vs not PA switch on SX126x eval board work properly
- pinMode(SX126X_ANT_SW, OUTPUT);
- digitalWrite(SX126X_ANT_SW, 1);
-#endif
-
#ifdef PIN_PWR_DELAY_MS
// This may be required to give the peripherals time to power up.
delay(PIN_PWR_DELAY_MS);
@@ -873,8 +906,8 @@ void setup()
}
#endif
-#if defined(RF95_IRQ)
- if (!rIf) {
+#if defined(RF95_IRQ) && RADIOLIB_EXCLUDE_SX127X != 1
+ if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) {
rIf = new RF95Interface(RadioLibHAL, LORA_CS, RF95_IRQ, RF95_RESET, RF95_DIO1);
if (!rIf->init()) {
LOG_WARN("Failed to find RF95 radio\n");
@@ -887,8 +920,8 @@ void setup()
}
#endif
-#if defined(USE_SX1262) && !defined(ARCH_PORTDUINO) && !defined(TCXO_OPTIONAL)
- if (!rIf) {
+#if defined(USE_SX1262) && !defined(ARCH_PORTDUINO) && !defined(TCXO_OPTIONAL) && RADIOLIB_EXCLUDE_SX126X != 1
+ if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) {
rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY);
if (!rIf->init()) {
LOG_WARN("Failed to find SX1262 radio\n");
@@ -902,7 +935,7 @@ void setup()
#endif
#if defined(USE_SX1262) && !defined(ARCH_PORTDUINO) && defined(TCXO_OPTIONAL)
- if (!rIf) {
+ if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) {
// Try using the specified TCXO voltage
rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY);
if (!rIf->init()) {
@@ -918,7 +951,7 @@ void setup()
}
}
- if (!rIf) {
+ if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) {
// If specified TCXO voltage fails, attempt to use DIO3 as a reference instea
rIf = new SX1262Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY);
if (!rIf->init()) {
@@ -936,7 +969,7 @@ void setup()
#endif
#if defined(USE_SX1268)
- if (!rIf) {
+ if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) {
rIf = new SX1268Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY);
if (!rIf->init()) {
LOG_WARN("Failed to find SX1268 radio\n");
@@ -950,7 +983,7 @@ void setup()
#endif
#if defined(USE_LLCC68)
- if (!rIf) {
+ if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) {
rIf = new LLCC68Interface(RadioLibHAL, SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY);
if (!rIf->init()) {
LOG_WARN("Failed to find LLCC68 radio\n");
@@ -963,33 +996,49 @@ void setup()
}
#endif
-#if defined(USE_LR1110)
- if (!rIf) {
- rIf = new LR1110Interface(RadioLibHAL, LR1110_SPI_NSS_PIN, LR1110_IRQ_PIN, LR1110_NRESER_PIN, LR1110_BUSY_PIN);
+#if defined(USE_LR1110) && RADIOLIB_EXCLUDE_LR11X0 != 1
+ if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) {
+ rIf = new LR1110Interface(RadioLibHAL, LR1110_SPI_NSS_PIN, LR1110_IRQ_PIN, LR1110_NRESET_PIN, LR1110_BUSY_PIN);
if (!rIf->init()) {
LOG_WARN("Failed to find LR1110 radio\n");
delete rIf;
rIf = NULL;
} else {
LOG_INFO("LR1110 Radio init succeeded, using LR1110 radio\n");
+ radioType = LR1110_RADIO;
}
}
#endif
-#if defined(USE_LR1120)
+#if defined(USE_LR1120) && RADIOLIB_EXCLUDE_LR11X0 != 1
if (!rIf) {
- rIf = new LR1120Interface(RadioLibHAL, LR1120_SPI_NSS_PIN, LR1120_IRQ_PIN, LR1120_NRESER_PIN, LR1120_BUSY_PIN);
+ rIf = new LR1120Interface(RadioLibHAL, LR1120_SPI_NSS_PIN, LR1120_IRQ_PIN, LR1120_NRESET_PIN, LR1120_BUSY_PIN);
if (!rIf->init()) {
LOG_WARN("Failed to find LR1120 radio\n");
delete rIf;
rIf = NULL;
} else {
LOG_INFO("LR1120 Radio init succeeded, using LR1120 radio\n");
+ radioType = LR1120_RADIO;
}
}
#endif
-#if defined(USE_SX1280)
+#if defined(USE_LR1121) && RADIOLIB_EXCLUDE_LR11X0 != 1
+ if (!rIf) {
+ rIf = new LR1121Interface(RadioLibHAL, LR1121_SPI_NSS_PIN, LR1121_IRQ_PIN, LR1121_NRESET_PIN, LR1121_BUSY_PIN);
+ if (!rIf->init()) {
+ LOG_WARN("Failed to find LR1121 radio\n");
+ delete rIf;
+ rIf = NULL;
+ } else {
+ LOG_INFO("LR1121 Radio init succeeded, using LR1121 radio\n");
+ radioType = LR1121_RADIO;
+ }
+ }
+#endif
+
+#if defined(USE_SX1280) && RADIOLIB_EXCLUDE_SX128X != 1
if (!rIf) {
rIf = new SX1280Interface(RadioLibHAL, SX128X_CS, SX128X_DIO1, SX128X_RESET, SX128X_BUSY);
if (!rIf->init()) {
@@ -1004,7 +1053,6 @@ void setup()
#endif
// check if the radio chip matches the selected region
-
if ((config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24) && (!rIf->wideLora())) {
LOG_WARN("Radio chip does not support 2.4GHz LoRa. Reverting to unset.\n");
config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_UNSET;
@@ -1096,6 +1144,9 @@ extern meshtastic_DeviceMetadata getDeviceMetadata()
deviceMetadata.position_flags = config.position.position_flags;
deviceMetadata.hw_model = HW_VENDOR;
deviceMetadata.hasRemoteHardware = moduleConfig.remote_hardware.enabled;
+#if !(MESHTASTIC_EXCLUDE_PKI)
+ deviceMetadata.hasPKC = true;
+#endif
return deviceMetadata;
}
#ifndef PIO_UNIT_TESTING
@@ -1103,10 +1154,6 @@ void loop()
{
runASAP = false;
- // axpDebugOutput.loop();
-
- // heap_caps_check_integrity_all(true); // FIXME - disable this expensive check
-
#ifdef ARCH_ESP32
esp32Loop();
#endif
@@ -1115,33 +1162,21 @@ void loop()
#endif
powerCommandsCheck();
- // For debugging
- // if (rIf) ((RadioLibInterface *)rIf)->isActivelyReceiving();
-
#ifdef DEBUG_STACK
static uint32_t lastPrint = 0;
- if (millis() - lastPrint > 10 * 1000L) {
+ if (!Throttle::isWithinTimespanMs(lastPrint, 10 * 1000L)) {
lastPrint = millis();
meshtastic::printThreadInfo("main");
}
#endif
- // TODO: This should go into a thread handled by FreeRTOS.
- // handleWebResponse();
-
service->loop();
long delayMsec = mainController.runOrDelay();
- /* if (mainController.nextThread && delayMsec)
- LOG_DEBUG("Next %s in %ld\n", mainController.nextThread->ThreadName.c_str(),
- mainController.nextThread->tillRun(millis())); */
-
// We want to sleep as long as possible here - because it saves power
if (!runASAP && loopCanSleep()) {
- // if(delayMsec > 100) LOG_DEBUG("sleeping %ld\n", delayMsec);
mainDelay.delay(delayMsec);
}
- // if (didWake) LOG_DEBUG("wake!\n");
}
#endif
\ No newline at end of file
diff --git a/src/main.h b/src/main.h
index 896015b68..d3b6ade47 100644
--- a/src/main.h
+++ b/src/main.h
@@ -56,8 +56,8 @@ extern AudioThread *audioThread;
// Global Screen singleton.
extern graphics::Screen *screen;
-#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
-#include "AccelerometerThread.h"
+#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C
+#include "motion/AccelerometerThread.h"
extern AccelerometerThread *accelerometerThread;
#endif
@@ -86,4 +86,4 @@ void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), rp2040Setup(), clearB
meshtastic_DeviceMetadata getDeviceMetadata();
// We default to 4MHz SPI, SPI mode 0
-extern SPISettings spiSettings;
+extern SPISettings spiSettings;
\ No newline at end of file
diff --git a/src/memGet.cpp b/src/memGet.cpp
index e982ef7ee..ef1102f1e 100644
--- a/src/memGet.cpp
+++ b/src/memGet.cpp
@@ -57,6 +57,8 @@ uint32_t MemGet::getFreePsram()
{
#ifdef ARCH_ESP32
return ESP.getFreePsram();
+#elif defined(ARCH_PORTDUINO)
+ return 4194252;
#else
return 0;
#endif
@@ -71,6 +73,8 @@ uint32_t MemGet::getPsramSize()
{
#ifdef ARCH_ESP32
return ESP.getPsramSize();
+#elif defined(ARCH_PORTDUINO)
+ return 4194252;
#else
return 0;
#endif
diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp
index bba7571d2..bb30e501d 100644
--- a/src/mesh/Channels.cpp
+++ b/src/mesh/Channels.cpp
@@ -13,10 +13,6 @@
#include "mqtt/MQTT.h"
#endif
-/// 16 bytes of random PSK for our _public_ default channel that all devices power up on (AES128)
-static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
- 0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0x01};
-
Channels channels;
const char *Channels::adminChannel = "admin";
@@ -80,6 +76,23 @@ meshtastic_Channel &Channels::fixupChannel(ChannelIndex chIndex)
return ch;
}
+void Channels::initDefaultLoraConfig()
+{
+ meshtastic_Config_LoRaConfig &loraConfig = config.lora;
+
+ loraConfig.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST; // Default to Long Range & Fast
+ loraConfig.use_preset = true;
+ loraConfig.tx_power = 0; // default
+ loraConfig.channel_num = 0;
+
+#ifdef USERPREFS_LORACONFIG_MODEM_PRESET
+ loraConfig.modem_preset = USERPREFS_LORACONFIG_MODEM_PRESET;
+#endif
+#ifdef USERPREFS_LORACONFIG_CHANNEL_NUM
+ loraConfig.channel_num = USERPREFS_LORACONFIG_CHANNEL_NUM;
+#endif
+}
+
/**
* Write a default channel to the specified channel index
*/
@@ -87,12 +100,7 @@ void Channels::initDefaultChannel(ChannelIndex chIndex)
{
meshtastic_Channel &ch = getByIndex(chIndex);
meshtastic_ChannelSettings &channelSettings = ch.settings;
- meshtastic_Config_LoRaConfig &loraConfig = config.lora;
- loraConfig.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST; // Default to Long Range & Fast
- loraConfig.use_preset = true;
- loraConfig.tx_power = 0; // default
- loraConfig.channel_num = 0;
uint8_t defaultpskIndex = 1;
channelSettings.psk.bytes[0] = defaultpskIndex;
channelSettings.psk.size = 1;
@@ -101,29 +109,53 @@ void Channels::initDefaultChannel(ChannelIndex chIndex)
channelSettings.has_module_settings = true;
ch.has_settings = true;
- ch.role = meshtastic_Channel_Role_PRIMARY;
+ ch.role = chIndex == 0 ? meshtastic_Channel_Role_PRIMARY : meshtastic_Channel_Role_SECONDARY;
-#ifdef LORACONFIG_MODEM_PRESET_USERPREFS
- loraConfig.modem_preset = LORACONFIG_MODEM_PRESET_USERPREFS;
-#endif
-#ifdef LORACONFIG_CHANNEL_NUM_USERPREFS
- loraConfig.channel_num = LORACONFIG_CHANNEL_NUM_USERPREFS;
-#endif
-
- // Install custom defaults. Will eventually support setting multiple default channels
- if (chIndex == 0) {
-#ifdef CHANNEL_0_PSK_USERPREFS
- static const uint8_t defaultpsk[] = CHANNEL_0_PSK_USERPREFS;
- memcpy(channelSettings.psk.bytes, defaultpsk, sizeof(defaultpsk));
- channelSettings.psk.size = sizeof(defaultpsk);
+ switch (chIndex) {
+ case 0:
+#ifdef USERPREFS_CHANNEL_0_PSK
+ static const uint8_t defaultpsk0[] = USERPREFS_CHANNEL_0_PSK;
+ memcpy(channelSettings.psk.bytes, defaultpsk0, sizeof(defaultpsk0));
+ channelSettings.psk.size = sizeof(defaultpsk0);
#endif
-#ifdef CHANNEL_0_NAME_USERPREFS
- strcpy(channelSettings.name, CHANNEL_0_NAME_USERPREFS);
+#ifdef USERPREFS_CHANNEL_0_NAME
+ strcpy(channelSettings.name, USERPREFS_CHANNEL_0_NAME);
#endif
-#ifdef CHANNEL_0_PRECISION_USERPREFS
- channelSettings.module_settings.position_precision = CHANNEL_0_PRECISION_USERPREFS;
+#ifdef USERPREFS_CHANNEL_0_PRECISION
+ channelSettings.module_settings.position_precision = USERPREFS_CHANNEL_0_PRECISION;
#endif
+ break;
+ case 1:
+#ifdef USERPREFS_CHANNEL_1_PSK
+ static const uint8_t defaultpsk1[] = USERPREFS_CHANNEL_1_PSK;
+ memcpy(channelSettings.psk.bytes, defaultpsk1, sizeof(defaultpsk1));
+ channelSettings.psk.size = sizeof(defaultpsk1);
+
+#endif
+#ifdef USERPREFS_CHANNEL_1_NAME
+ strcpy(channelSettings.name, USERPREFS_CHANNEL_1_NAME);
+#endif
+#ifdef USERPREFS_CHANNEL_1_PRECISION
+ channelSettings.module_settings.position_precision = USERPREFS_CHANNEL_1_PRECISION;
+#endif
+ break;
+ case 2:
+#ifdef USERPREFS_CHANNEL_2_PSK
+ static const uint8_t defaultpsk2[] = USERPREFS_CHANNEL_2_PSK;
+ memcpy(channelSettings.psk.bytes, defaultpsk2, sizeof(defaultpsk2));
+ channelSettings.psk.size = sizeof(defaultpsk2);
+
+#endif
+#ifdef USERPREFS_CHANNEL_2_NAME
+ strcpy(channelSettings.name, USERPREFS_CHANNEL_2_NAME);
+#endif
+#ifdef USERPREFS_CHANNEL_2_PRECISION
+ channelSettings.module_settings.position_precision = USERPREFS_CHANNEL_2_PRECISION;
+#endif
+ break;
+ default:
+ break;
}
}
@@ -213,7 +245,15 @@ void Channels::initDefaults()
channelFile.channels_count = MAX_NUM_CHANNELS;
for (int i = 0; i < channelFile.channels_count; i++)
fixupChannel(i);
+ initDefaultLoraConfig();
+
+#ifdef USERPREFS_CHANNELS_TO_WRITE
+ for (int i = 0; i < USERPREFS_CHANNELS_TO_WRITE; i++) {
+ initDefaultChannel(i);
+ }
+#else
initDefaultChannel(0);
+#endif
}
void Channels::onConfigChanged()
@@ -277,7 +317,7 @@ void Channels::setChannel(const meshtastic_Channel &c)
bool Channels::anyMqttEnabled()
{
-#if EVENT_MODE
+#if USERPREFS_EVENT_MODE
// Don't publish messages on the public MQTT broker if we are in event mode
if (strcmp(moduleConfig.mqtt.address, default_mqtt_address) == 0) {
return false;
@@ -363,4 +403,4 @@ bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash)
int16_t Channels::setActiveByIndex(ChannelIndex channelIndex)
{
return setCrypto(channelIndex);
-}
+}
\ No newline at end of file
diff --git a/src/mesh/Channels.h b/src/mesh/Channels.h
index 4f87cb309..b0c9b3d07 100644
--- a/src/mesh/Channels.h
+++ b/src/mesh/Channels.h
@@ -117,7 +117,12 @@ class Channels
meshtastic_Channel &fixupChannel(ChannelIndex chIndex);
/**
- * Write a default channel to the specified channel index
+ * Writes the default lora config
+ */
+ void initDefaultLoraConfig();
+
+ /**
+ * Write default channels defined in UserPrefs
*/
void initDefaultChannel(ChannelIndex chIndex);
@@ -129,4 +134,12 @@ class Channels
};
/// Singleton channel table
-extern Channels channels;
\ No newline at end of file
+extern Channels channels;
+
+/// 16 bytes of random PSK for our _public_ default channel that all devices power up on (AES128)
+static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
+ 0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0x01};
+
+static const uint8_t eventpsk[] = {0x38, 0x4b, 0xbc, 0xc0, 0x1d, 0xc0, 0x22, 0xd1, 0x81, 0xbf, 0x36,
+ 0xb8, 0x61, 0x21, 0xe1, 0xfb, 0x96, 0xb7, 0x2e, 0x55, 0xbf, 0x74,
+ 0x22, 0x7e, 0x9d, 0x6a, 0xfb, 0x48, 0xd6, 0x4c, 0xb1, 0xa1};
\ No newline at end of file
diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp
index a5322a65a..a875eb8b2 100644
--- a/src/mesh/CryptoEngine.cpp
+++ b/src/mesh/CryptoEngine.cpp
@@ -66,12 +66,11 @@ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_
uint8_t *bytesOut)
{
uint8_t *auth;
- uint32_t *extraNonce;
long extraNonceTmp = random();
auth = bytesOut + numBytes;
- extraNonce = (uint32_t *)(auth + 8);
- *extraNonce = extraNonceTmp;
- LOG_INFO("Random nonce value: %d\n", *extraNonce);
+ memcpy((uint8_t *)(auth + 8), &extraNonceTmp,
+ sizeof(uint32_t)); // do not use dereference on potential non aligned pointers : *extraNonce = extraNonceTmp;
+ LOG_INFO("Random nonce value: %d\n", extraNonceTmp);
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(toNode);
if (node->num < 1 || node->user.public_key.size == 0) {
LOG_DEBUG("Node %d or their public_key not found\n", toNode);
@@ -80,14 +79,15 @@ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_
if (!crypto->setDHKey(toNode)) {
return false;
}
- initNonce(fromNode, packetNum, *extraNonce);
+ initNonce(fromNode, packetNum, extraNonceTmp);
// Calculate the shared secret with the destination node and encrypt
printBytes("Attempting encrypt using nonce: ", nonce, 13);
- printBytes("Attempting encrypt using shared_key: ", shared_key, 32);
+ printBytes("Attempting encrypt using shared_key starting with: ", shared_key, 8);
aes_ccm_ae(shared_key, 32, nonce, 8, bytes, numBytes, nullptr, 0, bytesOut,
auth); // this can write up to 15 bytes longer than numbytes past bytesOut
- *extraNonce = extraNonceTmp;
+ memcpy((uint8_t *)(auth + 8), &extraNonceTmp,
+ sizeof(uint32_t)); // do not use dereference on potential non aligned pointers : *extraNonce = extraNonceTmp;
return true;
}
@@ -99,11 +99,13 @@ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, uint64_
*/
bool CryptoEngine::decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes, uint8_t *bytesOut)
{
- uint8_t *auth; // set to last 8 bytes of text?
- uint32_t *extraNonce;
+ uint8_t *auth; // set to last 8 bytes of text?
+ uint32_t extraNonce; // pointer was not really used
auth = bytes + numBytes - 12;
- extraNonce = (uint32_t *)(auth + 8);
- LOG_INFO("Random nonce value: %d\n", *extraNonce);
+ memcpy(&extraNonce, auth + 8,
+ sizeof(uint32_t)); // do not use dereference on potential non aligned pointers : (uint32_t *)(auth + 8);
+#ifndef PIO_UNIT_TESTING
+ LOG_INFO("Random nonce value: %d\n", extraNonce);
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(fromNode);
if (node == nullptr || node->num < 1 || node->user.public_key.size == 0) {
@@ -115,9 +117,10 @@ bool CryptoEngine::decryptCurve25519(uint32_t fromNode, uint64_t packetNum, size
if (!crypto->setDHKey(fromNode)) {
return false;
}
- initNonce(fromNode, packetNum, *extraNonce);
+#endif
+ initNonce(fromNode, packetNum, extraNonce);
printBytes("Attempting decrypt using nonce: ", nonce, 13);
- printBytes("Attempting decrypt using shared_key: ", shared_key, 32);
+ printBytes("Attempting decrypt using shared_key starting with: ", shared_key, 8);
return aes_ccm_ad(shared_key, 32, nonce, 8, bytes, numBytes - 12, nullptr, 0, auth, bytesOut);
}
@@ -137,11 +140,12 @@ bool CryptoEngine::setDHKey(uint32_t nodeNum)
LOG_DEBUG("Node %d or their public_key not found\n", nodeNum);
return false;
}
-
+ printBytes("Generating DH with remote pubkey: ", node->user.public_key.bytes, 32);
+ printBytes("And local pubkey: ", config.security.public_key.bytes, 32);
if (!setDHPublicKey(node->user.public_key.bytes))
return false;
- printBytes("DH Output: ", shared_key, 32);
+ // printBytes("DH Output: ", shared_key, 32);
/**
* D.J. Bernstein reccomends hashing the shared key. We want to do this because there are
@@ -166,12 +170,12 @@ bool CryptoEngine::setDHKey(uint32_t nodeNum)
void CryptoEngine::hash(uint8_t *bytes, size_t numBytes)
{
SHA256 hash;
- size_t posn, len;
+ size_t posn;
uint8_t size = numBytes;
uint8_t inc = 16;
hash.reset();
for (posn = 0; posn < size; posn += inc) {
- len = size - posn;
+ size_t len = size - posn;
if (len > inc)
len = inc;
hash.update(bytes + posn, len);
diff --git a/src/mesh/Default.cpp b/src/mesh/Default.cpp
index ac7441394..0bedcfc91 100644
--- a/src/mesh/Default.cpp
+++ b/src/mesh/Default.cpp
@@ -45,7 +45,7 @@ uint32_t Default::getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t d
uint8_t Default::getConfiguredOrDefaultHopLimit(uint8_t configured)
{
-#if EVENT_MODE
+#if USERPREFS_EVENT_MODE
return (configured > HOP_RELIABLE) ? HOP_RELIABLE : config.lora.hop_limit;
#else
return (configured >= HOP_MAX) ? HOP_MAX : config.lora.hop_limit;
diff --git a/src/mesh/Default.h b/src/mesh/Default.h
index bafa60898..5641b5d25 100644
--- a/src/mesh/Default.h
+++ b/src/mesh/Default.h
@@ -3,6 +3,8 @@
#include
#define ONE_DAY 24 * 60 * 60
#define ONE_MINUTE_MS 60 * 1000
+#define THIRTY_SECONDS_MS 30 * 1000
+#define FIVE_SECONDS_MS 5 * 1000
#define default_gps_update_interval IF_ROUTER(ONE_DAY, 2 * 60)
#define default_telemetry_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 30 * 60)
diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp
index fbe56159c..23f6b6f92 100644
--- a/src/mesh/FloodingRouter.cpp
+++ b/src/mesh/FloodingRouter.cpp
@@ -21,11 +21,13 @@ ErrorCode FloodingRouter::send(meshtastic_MeshPacket *p)
bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
{
if (wasSeenRecently(p)) { // Note: this will also add a recent packet record
- printPacket("Ignoring incoming msg we've already seen", p);
+ printPacket("Ignoring dupe incoming msg", p);
+ rxDupe++;
if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER &&
config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) {
// cancel rebroadcast of this message *if* there was already one, unless we're a router/repeater!
- Router::cancelSending(p->from, p->id);
+ if (Router::cancelSending(p->from, p->id))
+ txRelayCanceled++;
}
return true;
}
@@ -36,18 +38,18 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c)
{
bool isAckorReply = (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) && (p->decoded.request_id != 0);
- if (isAckorReply && p->to != getNodeNum() && p->to != NODENUM_BROADCAST) {
+ if (isAckorReply && !isToUs(p) && p->to != NODENUM_BROADCAST) {
// do not flood direct message that is ACKed or replied to
- LOG_DEBUG("Receiving an ACK or reply not for me, but don't need to rebroadcast this direct message anymore.\n");
+ LOG_DEBUG("Rxd an ACK/reply not for me, cancel rebroadcast.\n");
Router::cancelSending(p->to, p->decoded.request_id); // cancel rebroadcast for this DM
}
- if ((p->to != getNodeNum()) && (p->hop_limit > 0) && (getFrom(p) != getNodeNum())) {
+ if (!isToUs(p) && (p->hop_limit > 0) && !isFromUs(p)) {
if (p->id != 0) {
if (config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE) {
meshtastic_MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it
tosend->hop_limit--; // bump down the hop count
-#if EVENT_MODE
+#if USERPREFS_EVENT_MODE
if (tosend->hop_limit > 2) {
// if we are "correcting" the hop_limit, "correct" the hop_start by the same amount to preserve hops away.
tosend->hop_start -= (tosend->hop_limit - 2);
@@ -55,7 +57,7 @@ void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
}
#endif
- LOG_INFO("Rebroadcasting received floodmsg to neighbors\n");
+ LOG_INFO("Rebroadcasting received floodmsg\n");
// Note: we are careful to resend using the original senders node id
// We are careful not to call our hooked version of send() - because we don't want to check this again
Router::send(tosend);
@@ -63,7 +65,7 @@ void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
LOG_DEBUG("Not rebroadcasting. Role = Role_ClientMute\n");
}
} else {
- LOG_DEBUG("Ignoring a simple (0 id) broadcast\n");
+ LOG_DEBUG("Ignoring 0 id broadcast\n");
}
}
// handle the packet as normal
diff --git a/src/mesh/InterfacesTemplates.cpp b/src/mesh/InterfacesTemplates.cpp
index f2cac8028..2720e8525 100644
--- a/src/mesh/InterfacesTemplates.cpp
+++ b/src/mesh/InterfacesTemplates.cpp
@@ -8,12 +8,19 @@
#include "api/ServerAPI.h"
// We need this declaration for proper linking in derived classes
+#if RADIOLIB_EXCLUDE_SX126X != 1
template class SX126xInterface;
template class SX126xInterface;
template class SX126xInterface;
+#endif
+#if RADIOLIB_EXCLUDE_SX128X != 1
template class SX128xInterface;
+#endif
+#if RADIOLIB_EXCLUDE_LR11X0 != 1
template class LR11x0Interface;
template class LR11x0Interface;
+template class LR11x0Interface;
+#endif
#ifdef ARCH_STM32WL
template class SX126xInterface;
#endif
diff --git a/src/mesh/LLCC68Interface.cpp b/src/mesh/LLCC68Interface.cpp
index 8109765a6..d92ea5450 100644
--- a/src/mesh/LLCC68Interface.cpp
+++ b/src/mesh/LLCC68Interface.cpp
@@ -1,3 +1,4 @@
+#if RADIOLIB_EXCLUDE_SX126X != 1
#include "LLCC68Interface.h"
#include "configuration.h"
#include "error.h"
@@ -6,4 +7,5 @@ LLCC68Interface::LLCC68Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, R
RADIOLIB_PIN_TYPE busy)
: SX126xInterface(hal, cs, irq, rst, busy)
{
-}
\ No newline at end of file
+}
+#endif
\ No newline at end of file
diff --git a/src/mesh/LLCC68Interface.h b/src/mesh/LLCC68Interface.h
index 7e0fa1439..1cd23e92b 100644
--- a/src/mesh/LLCC68Interface.h
+++ b/src/mesh/LLCC68Interface.h
@@ -1,5 +1,5 @@
#pragma once
-
+#if RADIOLIB_EXCLUDE_SX126X != 1
#include "SX126xInterface.h"
/**
@@ -15,4 +15,5 @@ class LLCC68Interface : public SX126xInterface
public:
LLCC68Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy);
-};
\ No newline at end of file
+};
+#endif
\ No newline at end of file
diff --git a/src/mesh/LR1110Interface.cpp b/src/mesh/LR1110Interface.cpp
index c000bd838..5dbd3ff38 100644
--- a/src/mesh/LR1110Interface.cpp
+++ b/src/mesh/LR1110Interface.cpp
@@ -1,3 +1,5 @@
+#if RADIOLIB_EXCLUDE_LR11X0 != 1
+
#include "LR1110Interface.h"
#include "configuration.h"
#include "error.h"
@@ -6,4 +8,5 @@ LR1110Interface::LR1110Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, R
RADIOLIB_PIN_TYPE busy)
: LR11x0Interface(hal, cs, irq, rst, busy)
{
-}
\ No newline at end of file
+}
+#endif
\ No newline at end of file
diff --git a/src/mesh/LR1110Interface.h b/src/mesh/LR1110Interface.h
index 79e7c36ca..2a2e6e861 100644
--- a/src/mesh/LR1110Interface.h
+++ b/src/mesh/LR1110Interface.h
@@ -1,5 +1,5 @@
#pragma once
-
+#if RADIOLIB_EXCLUDE_LR11X0 != 1
#include "LR11x0Interface.h"
/**
@@ -10,4 +10,5 @@ class LR1110Interface : public LR11x0Interface
public:
LR1110Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy);
-};
\ No newline at end of file
+};
+#endif
\ No newline at end of file
diff --git a/src/mesh/LR1120Interface.cpp b/src/mesh/LR1120Interface.cpp
index 94f3568f7..a17ac87ef 100644
--- a/src/mesh/LR1120Interface.cpp
+++ b/src/mesh/LR1120Interface.cpp
@@ -1,3 +1,5 @@
+#if RADIOLIB_EXCLUDE_LR11X0 != 1
+
#include "LR1120Interface.h"
#include "configuration.h"
#include "error.h"
@@ -6,4 +8,10 @@ LR1120Interface::LR1120Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, R
RADIOLIB_PIN_TYPE busy)
: LR11x0Interface(hal, cs, irq, rst, busy)
{
-}
\ No newline at end of file
+}
+
+bool LR1120Interface::wideLora()
+{
+ return true;
+}
+#endif
\ No newline at end of file
diff --git a/src/mesh/LR1120Interface.h b/src/mesh/LR1120Interface.h
index fc59293ec..d81a480a9 100644
--- a/src/mesh/LR1120Interface.h
+++ b/src/mesh/LR1120Interface.h
@@ -1,7 +1,7 @@
#pragma once
+#if RADIOLIB_EXCLUDE_LR11X0 != 1
#include "LR11x0Interface.h"
-
/**
* Our adapter for LR1120 wideband radios
*/
@@ -10,4 +10,6 @@ class LR1120Interface : public LR11x0Interface
public:
LR1120Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy);
-};
\ No newline at end of file
+ bool wideLora() override;
+};
+#endif
\ No newline at end of file
diff --git a/src/mesh/LR1121Interface.cpp b/src/mesh/LR1121Interface.cpp
new file mode 100644
index 000000000..29bd07d08
--- /dev/null
+++ b/src/mesh/LR1121Interface.cpp
@@ -0,0 +1,16 @@
+#if RADIOLIB_EXCLUDE_LR11X0 != 1
+#include "LR1121Interface.h"
+#include "configuration.h"
+#include "error.h"
+
+LR1121Interface::LR1121Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
+ RADIOLIB_PIN_TYPE busy)
+ : LR11x0Interface(hal, cs, irq, rst, busy)
+{
+}
+
+bool LR1121Interface::wideLora()
+{
+ return true;
+}
+#endif
\ No newline at end of file
diff --git a/src/mesh/LR1121Interface.h b/src/mesh/LR1121Interface.h
new file mode 100644
index 000000000..ebc5b59a9
--- /dev/null
+++ b/src/mesh/LR1121Interface.h
@@ -0,0 +1,16 @@
+#pragma once
+#if RADIOLIB_EXCLUDE_LR11X0 != 1
+
+#include "LR11x0Interface.h"
+
+/**
+ * Our adapter for LR1121 wideband radios
+ */
+class LR1121Interface : public LR11x0Interface
+{
+ public:
+ LR1121Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
+ RADIOLIB_PIN_TYPE busy);
+ bool wideLora() override;
+};
+#endif
\ No newline at end of file
diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp
index 8f0dc062e..6641634c4 100644
--- a/src/mesh/LR11x0Interface.cpp
+++ b/src/mesh/LR11x0Interface.cpp
@@ -1,15 +1,33 @@
+#if RADIOLIB_EXCLUDE_LR11X0 != 1
#include "LR11x0Interface.h"
+#include "Throttle.h"
#include "configuration.h"
#include "error.h"
#include "mesh/NodeDB.h"
+#ifdef LR11X0_DIO_AS_RF_SWITCH
+#include "rfswitch.h"
+#else
+static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC};
+static const Module::RfSwitchMode_t rfswitch_table[] = {
+ {LR11x0::MODE_STBY, {}}, {LR11x0::MODE_RX, {}}, {LR11x0::MODE_TX, {}}, {LR11x0::MODE_TX_HP, {}},
+ {LR11x0::MODE_TX_HF, {}}, {LR11x0::MODE_GNSS, {}}, {LR11x0::MODE_WIFI, {}}, END_OF_MODE_TABLE,
+};
+#endif
+
#ifdef ARCH_PORTDUINO
#include "PortduinoGlue.h"
#endif
// Particular boards might define a different max power based on what their hardware can do, default to max power output if not
// specified (may be dangerous if using external PA and LR11x0 power config forgotten)
-#ifndef LR11X0_MAX_POWER
-#define LR11X0_MAX_POWER 22
+#ifndef LR1110_MAX_POWER
+#define LR1110_MAX_POWER 22
+#endif
+
+// the 2.4G part maxes at 13dBm
+
+#ifndef LR1120_MAX_POWER
+#define LR1120_MAX_POWER 13
#endif
template
@@ -45,65 +63,28 @@ template bool LR11x0Interface::init()
RadioLibInterface::init();
- if (power > LR11X0_MAX_POWER) // Clamp power to maximum defined level
- power = LR11X0_MAX_POWER;
+ if (power > LR1110_MAX_POWER) // Clamp power to maximum defined level
+ power = LR1110_MAX_POWER;
+
+ if ((power > LR1120_MAX_POWER) &&
+ (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) // clamp again if wide freq range
+ power = LR1120_MAX_POWER;
limitPower();
-#ifdef TRACKER_T1000_E // Tracker T1000E uses DIO5, DIO6, DIO7, DIO8 for RF switching
-
- static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_LR11X0_DIO7,
- RADIOLIB_LR11X0_DIO8, RADIOLIB_NC};
-
- static const Module::RfSwitchMode_t rfswitch_table[] = {
- // mode DIO5 DIO6 DIO7 DIO8
- {LR11x0::MODE_STBY, {LOW, LOW, LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW, LOW, HIGH}},
- {LR11x0::MODE_TX, {HIGH, HIGH, LOW, HIGH}}, {LR11x0::MODE_TX_HP, {LOW, HIGH, LOW, HIGH}},
- {LR11x0::MODE_TX_HF, {LOW, LOW, LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW, HIGH, LOW}},
- {LR11x0::MODE_WIFI, {LOW, LOW, LOW, LOW}}, END_OF_MODE_TABLE,
- };
-
-#else
-
- // set RF switch configuration for Wio WM1110
- // Wio WM1110 uses DIO5 and DIO6 for RF switching
-
- static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_NC, RADIOLIB_NC,
- RADIOLIB_NC};
-
- static const Module::RfSwitchMode_t rfswitch_table[] = {
- // mode DIO5 DIO6
- {LR11x0::MODE_STBY, {LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW}},
- {LR11x0::MODE_TX, {HIGH, HIGH}}, {LR11x0::MODE_TX_HP, {LOW, HIGH}},
- {LR11x0::MODE_TX_HF, {LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW}},
- {LR11x0::MODE_WIFI, {LOW, LOW}}, END_OF_MODE_TABLE,
- };
-
-#endif
-
-// We need to do this before begin() call
-#ifdef LR11X0_DIO_AS_RF_SWITCH
- LOG_DEBUG("Setting DIO RF switch\n");
- bool dioAsRfSwitch = true;
-#elif defined(ARCH_PORTDUINO)
- bool dioAsRfSwitch = false;
- if (settingsMap[dio2_as_rf_switch]) {
- LOG_DEBUG("Setting DIO RF switch\n");
- dioAsRfSwitch = true;
- }
-#else
- bool dioAsRfSwitch = false;
-#endif
-
- if (dioAsRfSwitch)
- lora.setRfSwitchTable(rfswitch_dio_pins, rfswitch_table);
-
int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage);
// \todo Display actual typename of the adapter, not just `LR11x0`
LOG_INFO("LR11x0 init result %d\n", res);
if (res == RADIOLIB_ERR_CHIP_NOT_FOUND)
return false;
+ LR11x0VersionInfo_t version;
+ res = lora.getVersionInfo(&version);
+ if (res == RADIOLIB_ERR_NONE)
+ LOG_DEBUG("LR11x0 Device %d, HW %d, FW %d.%d, WiFi %d.%d, GNSS %d.%d\n", version.device, version.hardware,
+ version.fwMajor, version.fwMinor, version.fwMajorWiFi, version.fwMinorWiFi, version.fwGNSS,
+ version.almanacGNSS);
+
LOG_INFO("Frequency set to %f\n", getFreq());
LOG_INFO("Bandwidth set to %f\n", bw);
LOG_INFO("Power output set to %d\n", power);
@@ -114,6 +95,23 @@ template bool LR11x0Interface::init()
// FIXME: May want to set depending on a definition, currently all LR1110 variant files use the DC-DC regulator option
if (res == RADIOLIB_ERR_NONE)
res = lora.setRegulatorDCDC();
+
+#ifdef LR11X0_DIO_AS_RF_SWITCH
+ bool dioAsRfSwitch = true;
+#elif defined(ARCH_PORTDUINO)
+ bool dioAsRfSwitch = false;
+ if (settingsMap[dio2_as_rf_switch]) {
+ dioAsRfSwitch = true;
+ }
+#else
+ bool dioAsRfSwitch = false;
+#endif
+
+ if (dioAsRfSwitch) {
+ lora.setRfSwitchTable(rfswitch_dio_pins, rfswitch_table);
+ LOG_DEBUG("Setting DIO RF switch\n", res);
+ }
+
if (res == RADIOLIB_ERR_NONE) {
if (config.lora.sx126x_rx_boosted_gain) { // the name is unfortunate but historically accurate
res = lora.setRxBoostedGainMode(true);
@@ -165,8 +163,10 @@ template bool LR11x0Interface::reconfigure()
if (err != RADIOLIB_ERR_NONE)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING);
- if (power > LR11X0_MAX_POWER) // This chip has lower power limits than some
- power = LR11X0_MAX_POWER;
+ if (power > LR1110_MAX_POWER) // This chip has lower power limits than some
+ power = LR1110_MAX_POWER;
+ if ((power > LR1120_MAX_POWER) && (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) // 2.4G power limit
+ power = LR1120_MAX_POWER;
err = lora.setOutputPower(power);
assert(err == RADIOLIB_ERR_NONE);
@@ -263,29 +263,8 @@ template bool LR11x0Interface::isActivelyReceiving()
{
// The IRQ status will be cleared when we start our read operation. Check if we've started a header, but haven't yet
// received and handled the interrupt for reading the packet/handling errors.
-
- uint16_t irq = lora.getIrqStatus();
- bool detected = (irq & (RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID | RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED));
- // Handle false detections
- if (detected) {
- uint32_t now = millis();
- if (!activeReceiveStart) {
- activeReceiveStart = now;
- } else if ((now - activeReceiveStart > 2 * preambleTimeMsec) && !(irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID)) {
- // The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag
- activeReceiveStart = 0;
- LOG_DEBUG("Ignore false preamble detection.\n");
- return false;
- } else if (now - activeReceiveStart > maxPacketTimeMsec) {
- // We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag
- activeReceiveStart = 0;
- LOG_DEBUG("Ignore false header detection.\n");
- return false;
- }
- }
-
- // if (detected) LOG_DEBUG("rx detected\n");
- return detected;
+ return receiveDetected(lora.getIrqStatus(), RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID,
+ RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED);
}
template bool LR11x0Interface::sleep()
@@ -306,4 +285,5 @@ template bool LR11x0Interface::sleep()
#endif
return true;
-}
\ No newline at end of file
+}
+#endif
\ No newline at end of file
diff --git a/src/mesh/LR11x0Interface.h b/src/mesh/LR11x0Interface.h
index 9272f43f0..4829ddc1d 100644
--- a/src/mesh/LR11x0Interface.h
+++ b/src/mesh/LR11x0Interface.h
@@ -1,5 +1,5 @@
#pragma once
-
+#if RADIOLIB_EXCLUDE_LR11X0 != 1
#include "RadioLibInterface.h"
/**
@@ -65,7 +65,5 @@ template class LR11x0Interface : public RadioLibInterface
virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) override;
virtual void setStandby() override;
-
- private:
- uint32_t activeReceiveStart = 0;
};
+#endif
\ No newline at end of file
diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp
index 3b137d4bd..c60404c98 100644
--- a/src/mesh/MeshModule.cpp
+++ b/src/mesh/MeshModule.cpp
@@ -86,7 +86,7 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src)
// Was this message directed to us specifically? Will be false if we are sniffing someone elses packets
auto ourNodeNum = nodeDB->getNodeNum();
- bool toUs = mp.to == NODENUM_BROADCAST || mp.to == ourNodeNum;
+ bool toUs = mp.to == NODENUM_BROADCAST || isToUs(&mp);
for (auto i = modules->begin(); i != modules->end(); ++i) {
auto &pi = **i;
@@ -141,8 +141,7 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src)
// because currently when the phone sends things, it sends things using the local node ID as the from address. A
// better solution (FIXME) would be to let phones have their own distinct addresses and we 'route' to them like
// any other node.
- if (isDecoded && mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) &&
- !currentReply) {
+ if (isDecoded && mp.decoded.want_response && toUs && (!isFromUs(&mp) || isToUs(&mp)) && !currentReply) {
pi.sendResponse(mp);
ignoreRequest = ignoreRequest || pi.ignoreRequest; // If at least one module asks it, we may ignore a request
LOG_INFO("Asked module '%s' to send a response\n", pi.name);
@@ -241,7 +240,7 @@ std::vector MeshModule::GetMeshModulesWithUIFrames()
for (auto i = modules->begin(); i != modules->end(); ++i) {
auto &pi = **i;
if (pi.wantUIFrame()) {
- LOG_DEBUG("Module wants a UI Frame\n");
+ LOG_DEBUG("%s wants a UI Frame\n", pi.name);
modulesWithUIFrames.push_back(&pi);
}
}
@@ -256,7 +255,7 @@ void MeshModule::observeUIEvents(Observer *observer)
auto &pi = **i;
Observable *observable = pi.getUIFrameObservable();
if (observable != NULL) {
- LOG_DEBUG("Module wants a UI Frame\n");
+ LOG_DEBUG("%s wants a UI Frame\n", pi.name);
observer->observe(observable);
}
}
@@ -297,4 +296,4 @@ bool MeshModule::isRequestingFocus()
} else
return false;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/src/mesh/MeshModule.h b/src/mesh/MeshModule.h
index c341b301a..7929ba972 100644
--- a/src/mesh/MeshModule.h
+++ b/src/mesh/MeshModule.h
@@ -79,7 +79,8 @@ class MeshModule
meshtastic_AdminMessage *response);
#if HAS_SCREEN
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; }
- virtual bool isRequestingFocus(); // Checked by screen, when regenerating frameset
+ virtual bool isRequestingFocus(); // Checked by screen, when regenerating frameset
+ virtual bool interceptingKeyboardInput() { return false; } // Can screen use keyboard for nav, or is module handling input?
#endif
protected:
const char *name;
diff --git a/src/mesh/MeshPacketQueue.cpp b/src/mesh/MeshPacketQueue.cpp
index 6581b1ce4..99ef41c1e 100644
--- a/src/mesh/MeshPacketQueue.cpp
+++ b/src/mesh/MeshPacketQueue.cpp
@@ -1,4 +1,5 @@
#include "MeshPacketQueue.h"
+#include "NodeDB.h"
#include "configuration.h"
#include
@@ -16,13 +17,9 @@ bool CompareMeshPacketFunc(const meshtastic_MeshPacket *p1, const meshtastic_Mes
{
assert(p1 && p2);
auto p1p = getPriority(p1), p2p = getPriority(p2);
-
// If priorities differ, use that
- // for equal priorities, order by id (older packets have higher priority - this will briefly be wrong when IDs roll over but
- // no big deal)
- return (p1p != p2p)
- ? (p1p < p2p) // prefer bigger priorities
- : ((p1->id & ID_COUNTER_MASK) >= (p2->id & ID_COUNTER_MASK)); // Mask to counter portion, prefer smaller packet ids
+ // for equal priorities, prefer packets already on mesh.
+ return (p1p != p2p) ? (p1p > p2p) : (!isFromUs(p1) && isFromUs(p2));
}
MeshPacketQueue::MeshPacketQueue(size_t _maxLen) : maxLen(_maxLen) {}
@@ -69,8 +66,9 @@ bool MeshPacketQueue::enqueue(meshtastic_MeshPacket *p)
return replaceLowerPriorityPacket(p);
}
- queue.push_back(p);
- std::push_heap(queue.begin(), queue.end(), &CompareMeshPacketFunc);
+ // Find the correct position using upper_bound to maintain a stable order
+ auto it = std::upper_bound(queue.begin(), queue.end(), p, CompareMeshPacketFunc);
+ queue.insert(it, p); // Insert packet at the found position
return true;
}
@@ -81,9 +79,7 @@ meshtastic_MeshPacket *MeshPacketQueue::dequeue()
}
auto *p = queue.front();
- std::pop_heap(queue.begin(), queue.end(), &CompareMeshPacketFunc);
- queue.pop_back();
-
+ queue.erase(queue.begin()); // Remove the highest-priority packet
return p;
}
@@ -104,7 +100,6 @@ meshtastic_MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id)
auto p = (*it);
if (getFrom(p) == from && p->id == id) {
queue.erase(it);
- std::make_heap(queue.begin(), queue.end(), &CompareMeshPacketFunc);
return p;
}
}
@@ -115,28 +110,21 @@ meshtastic_MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id)
/** Attempt to find and remove a packet from this queue. Returns the packet which was removed from the queue */
bool MeshPacketQueue::replaceLowerPriorityPacket(meshtastic_MeshPacket *p)
{
- std::sort_heap(queue.begin(), queue.end(), &CompareMeshPacketFunc); // sort ascending based on priority (0 -> 127)
- // find first packet which does not compare less (in priority) than parameter packet
- auto low = std::lower_bound(queue.begin(), queue.end(), p, &CompareMeshPacketFunc);
-
- if (low == queue.begin()) { // if already at start, there are no packets with lower priority
- return false;
+ if (queue.empty()) {
+ return false; // No packets to replace
+ }
+ // Check if the packet at the back has a lower priority than the new packet
+ auto &backPacket = queue.back();
+ if (backPacket->priority < p->priority) {
+ // Remove the back packet
+ packetPool.release(backPacket);
+ queue.pop_back();
+ // Insert the new packet in the correct order
+ enqueue(p);
+ return true;
}
- if (low == queue.end()) {
- // all priorities in the vector are smaller than the incoming packet. Replace the lowest priority (first) element
- low = queue.begin();
- } else {
- // 'low' iterator points to first packet which does not compare less than parameter
- --low; // iterate to lower priority packet
- }
-
- if (getPriority(p) > getPriority(*low)) {
- packetPool.release(*low); // deallocate and drop the packet we're replacing
- *low = p; // replace low-pri packet at this position with incoming packet with higher priority
- }
-
- std::make_heap(queue.begin(), queue.end(), &CompareMeshPacketFunc);
- return true;
+ // If the back packet's priority is not lower, no replacement occurs
+ return false;
}
\ No newline at end of file
diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp
index 2c502d899..7b0bc0c5b 100644
--- a/src/mesh/MeshService.cpp
+++ b/src/mesh/MeshService.cpp
@@ -80,12 +80,11 @@ int MeshService::handleFromRadio(const meshtastic_MeshPacket *mp)
nodeDB->updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio
if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag &&
mp->decoded.portnum == meshtastic_PortNum_TELEMETRY_APP && mp->decoded.request_id > 0) {
- LOG_DEBUG(
- "Received telemetry response. Skip sending our NodeInfo because this potentially a Repeater which will ignore our "
- "request for its NodeInfo.\n");
+ LOG_DEBUG("Received telemetry response. Skip sending our NodeInfo.\n"); // because this potentially a Repeater which will
+ // ignore our request for its NodeInfo
} else if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB->getMeshNode(mp->from)->has_user &&
nodeInfoModule) {
- LOG_INFO("Heard a node on channel %d we don't know, sending NodeInfo and asking for a response.\n", mp->channel);
+ LOG_INFO("Heard new node on channel %d, sending NodeInfo and asking for a response.\n", mp->channel);
if (airTime->isTxAllowedChannelUtil(true)) {
nodeInfoModule->sendOurNodeInfo(mp->from, true, mp->channel);
} else {
@@ -223,7 +222,7 @@ ErrorCode MeshService::sendQueueStatusToPhone(const meshtastic_QueueStatus &qs,
copied->mesh_packet_id = mesh_packet_id;
if (toPhoneQueueStatusQueue.numFree() == 0) {
- LOG_DEBUG("NOTE: tophone queue status queue is full, discarding oldest\n");
+ LOG_INFO("tophone queue status queue is full, discarding oldest\n");
meshtastic_QueueStatus *d = toPhoneQueueStatusQueue.dequeuePtr(0);
if (d)
releaseQueueStatusToPool(d);
@@ -317,7 +316,7 @@ void MeshService::sendToPhone(meshtastic_MeshPacket *p)
void MeshService::sendMqttMessageToClientProxy(meshtastic_MqttClientProxyMessage *m)
{
- LOG_DEBUG("Sending mqtt message on topic '%s' to client for proxying to server\n", m->topic);
+ LOG_DEBUG("Sending mqtt message on topic '%s' to client for proxy\n", m->topic);
if (toPhoneMqttProxyQueue.numFree() == 0) {
LOG_WARN("MqttClientProxyMessagePool queue is full, discarding oldest\n");
meshtastic_MqttClientProxyMessage *d = toPhoneMqttProxyQueue.dequeuePtr(0);
@@ -408,3 +407,15 @@ bool MeshService::isToPhoneQueueEmpty()
{
return toPhoneQueue.isEmpty();
}
+
+uint32_t MeshService::GetTimeSinceMeshPacket(const meshtastic_MeshPacket *mp)
+{
+ uint32_t now = getTime();
+
+ uint32_t last_seen = mp->rx_time;
+ int delta = (int)(now - last_seen);
+ if (delta < 0) // our clock must be slightly off still - not set from GPS yet
+ delta = 0;
+
+ return delta;
+}
diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h
index ea1c4e345..1ccca4e6d 100644
--- a/src/mesh/MeshService.h
+++ b/src/mesh/MeshService.h
@@ -13,9 +13,9 @@
#if defined(ARCH_PORTDUINO) && !HAS_RADIO
#include "../platform/portduino/SimRadio.h"
#endif
-#ifdef ARCH_ESP32
+#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
-#include "modules/esp32/StoreForwardModule.h"
+#include "modules/StoreForwardModule.h"
#endif
#endif
@@ -92,6 +92,9 @@ class MeshService
/// Return the next MqttClientProxyMessage packet destined to the phone.
meshtastic_MqttClientProxyMessage *getMqttClientProxyMessageForPhone() { return toPhoneMqttProxyQueue.dequeuePtr(0); }
+ /// Return the next ClientNotification packet destined to the phone.
+ meshtastic_ClientNotification *getClientNotificationForPhone() { return toPhoneClientNotificationQueue.dequeuePtr(0); }
+
// search the queue for a request id and return the matching nodenum
NodeNum getNodenumFromRequestId(uint32_t request_id);
@@ -148,6 +151,8 @@ class MeshService
ErrorCode sendQueueStatusToPhone(const meshtastic_QueueStatus &qs, ErrorCode res, uint32_t mesh_packet_id);
+ uint32_t GetTimeSinceMeshPacket(const meshtastic_MeshPacket *mp);
+
private:
#if HAS_GPS
/// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh
@@ -160,4 +165,4 @@ class MeshService
friend class RoutingModule;
};
-extern MeshService *service;
\ No newline at end of file
+extern MeshService *service;
diff --git a/src/mesh/MeshTypes.h b/src/mesh/MeshTypes.h
index 90cfd5897..27d100fbe 100644
--- a/src/mesh/MeshTypes.h
+++ b/src/mesh/MeshTypes.h
@@ -50,5 +50,11 @@ extern Allocator &packetPool;
*/
NodeNum getFrom(const meshtastic_MeshPacket *p);
+// Returns true if the packet originated from the local node
+bool isFromUs(const meshtastic_MeshPacket *p);
+
+// Returns true if the packet is destined to us
+bool isToUs(const meshtastic_MeshPacket *p);
+
/* Some clients might not properly set priority, therefore we fix it here. */
void fixPriority(meshtastic_MeshPacket *p);
\ No newline at end of file
diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp
index 71bb8b1fe..b7ab61c74 100644
--- a/src/mesh/NodeDB.cpp
+++ b/src/mesh/NodeDB.cpp
@@ -32,12 +32,13 @@
#if HAS_WIFI
#include "mesh/wifi/WiFiAPClient.h"
#endif
-#include "modules/esp32/StoreForwardModule.h"
+#include "modules/StoreForwardModule.h"
#include
#include
#endif
#ifdef ARCH_PORTDUINO
+#include "modules/StoreForwardModule.h"
#include "platform/portduino/PortduinoGlue.h"
#endif
@@ -49,7 +50,7 @@
NodeDB *nodeDB = nullptr;
// we have plenty of ram so statically alloc this tempbuf (for now)
-EXT_RAM_ATTR meshtastic_DeviceState devicestate;
+EXT_RAM_BSS_ATTR meshtastic_DeviceState devicestate;
meshtastic_MyNodeInfo &myNodeInfo = devicestate.my_node;
meshtastic_LocalConfig config;
meshtastic_LocalModuleConfig moduleConfig;
@@ -121,6 +122,8 @@ NodeDB::NodeDB()
owner.hw_model = HW_VENDOR;
// Ensure user (nodeinfo) role is set to whatever we're configured to
owner.role = config.device.role;
+ // Ensure macaddr is set to our macaddr as it will be copied in our info below
+ memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr));
// Include our owner in the node db under our nodenum
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum());
@@ -129,39 +132,31 @@ NodeDB::NodeDB()
config.security.serial_enabled = config.device.serial_enabled;
config.security.is_managed = config.device.is_managed;
}
-#if !(MESHTASTIC_EXCLUDE_PKI)
+
+#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI)
+ bool keygenSuccess = false;
+ if (config.security.private_key.size == 32) {
+ if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
+ keygenSuccess = true;
+ }
+ } else {
+ LOG_INFO("Generating new PKI keys\n");
+ crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
+ keygenSuccess = true;
+ }
+ if (keygenSuccess) {
+ config.security.public_key.size = 32;
+ config.security.private_key.size = 32;
+ owner.public_key.size = 32;
+ memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
+ }
+#elif !(MESHTASTIC_EXCLUDE_PKI)
// Calculate Curve25519 public and private keys
- printBytes("Old Pubkey", config.security.public_key.bytes, 32);
if (config.security.private_key.size == 32 && config.security.public_key.size == 32) {
- LOG_INFO("Using saved PKI keys\n");
owner.public_key.size = config.security.public_key.size;
memcpy(owner.public_key.bytes, config.security.public_key.bytes, config.security.public_key.size);
crypto->setDHPrivateKey(config.security.private_key.bytes);
- } else {
-#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN)
- bool keygenSuccess = false;
- if (config.security.private_key.size == 32) {
- LOG_INFO("Calculating PKI Public Key\n");
- if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
- keygenSuccess = true;
- }
- } else {
- LOG_INFO("Generating new PKI keys\n");
- crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
- keygenSuccess = true;
- }
- if (keygenSuccess) {
- config.security.public_key.size = 32;
- config.security.private_key.size = 32;
- printBytes("New Pubkey", config.security.public_key.bytes, 32);
- owner.public_key.size = 32;
- memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
- }
-#else
- LOG_INFO("No PKI keys set, and generation disabled!\n");
-#endif
}
-
#endif
info->user = TypeConversions::ConvertToUserLite(owner);
@@ -201,6 +196,18 @@ NodeNum getFrom(const meshtastic_MeshPacket *p)
return (p->from == 0) ? nodeDB->getNodeNum() : p->from;
}
+// Returns true if the packet originated from the local node
+bool isFromUs(const meshtastic_MeshPacket *p)
+{
+ return p->from == 0 || p->from == nodeDB->getNodeNum();
+}
+
+// Returns true if the packet is destined to us
+bool isToUs(const meshtastic_MeshPacket *p)
+{
+ return p->to == nodeDB->getNodeNum();
+}
+
bool NodeDB::resetRadioConfig(bool factory_reset)
{
bool didFactoryReset = false;
@@ -243,7 +250,7 @@ bool NodeDB::factoryReset(bool eraseBleBonds)
#endif
// second, install default state (this will deal with the duplicate mac address issue)
installDefaultDeviceState();
- installDefaultConfig();
+ installDefaultConfig(!eraseBleBonds); // Also preserve the private key if we're not erasing BLE bonds
installDefaultModuleConfig();
installDefaultChannels();
// third, write everything to disk
@@ -266,8 +273,13 @@ bool NodeDB::factoryReset(bool eraseBleBonds)
return true;
}
-void NodeDB::installDefaultConfig()
+void NodeDB::installDefaultConfig(bool preserveKey = false)
{
+ uint8_t private_key_temp[32];
+ bool shouldPreserveKey = preserveKey && config.has_security && config.security.private_key.size > 0;
+ if (shouldPreserveKey) {
+ memcpy(private_key_temp, config.security.private_key.bytes, config.security.private_key.size);
+ }
LOG_INFO("Installing default LocalConfig\n");
memset(&config, 0, sizeof(meshtastic_LocalConfig));
config.version = DEVICESTATE_CUR_VER;
@@ -285,30 +297,36 @@ void NodeDB::installDefaultConfig()
config.lora.tx_enabled =
true; // FIXME: maybe false in the future, and setting region to enable it. (unset region forces it off)
config.lora.override_duty_cycle = false;
-#ifdef CONFIG_LORA_REGION_USERPREFS
- config.lora.region = CONFIG_LORA_REGION_USERPREFS;
+ config.lora.config_ok_to_mqtt = false;
+#ifdef USERPREFS_CONFIG_LORA_REGION
+ config.lora.region = USERPREFS_CONFIG_LORA_REGION;
#else
config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_UNSET;
#endif
-#ifdef LORACONFIG_MODEM_PRESET_USERPREFS
- config.lora.modem_preset = LORACONFIG_MODEM_PRESET_USERPREFS;
+#ifdef USERPREFS_LORACONFIG_MODEM_PRESET
+ config.lora.modem_preset = USERPREFS_LORACONFIG_MODEM_PRESET;
#else
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST;
#endif
config.lora.hop_limit = HOP_RELIABLE;
-#ifdef CONFIG_LORA_IGNORE_MQTT_USERPREFS
- config.lora.ignore_mqtt = CONFIG_LORA_IGNORE_MQTT_USERPREFS;
+#ifdef USERPREFS_CONFIG_LORA_IGNORE_MQTT
+ config.lora.ignore_mqtt = USERPREFS_CONFIG_LORA_IGNORE_MQTT;
#else
config.lora.ignore_mqtt = false;
#endif
-#ifdef ADMIN_KEY_USERPREFS
- memcpy(config.security.admin_key[0].bytes, admin_key_userprefs, 32);
+#ifdef USERPREFS_USE_ADMIN_KEY
+ memcpy(config.security.admin_key[0].bytes, USERPREFS_ADMIN_KEY, 32);
config.security.admin_key[0].size = 32;
-#else
- config.security.admin_key[0].size = 0;
+ config.security.admin_key_count = 1;
#endif
+ if (shouldPreserveKey) {
+ config.security.private_key.size = 32;
+ memcpy(config.security.private_key.bytes, private_key_temp, config.security.private_key.size);
+ printBytes("Restored key", config.security.private_key.bytes, config.security.private_key.size);
+ } else {
+ config.security.private_key.size = 0;
+ }
config.security.public_key.size = 0;
- config.security.private_key.size = 0;
#ifdef PIN_GPS_EN
config.position.gps_en_gpio = PIN_GPS_EN;
#endif
@@ -317,7 +335,9 @@ void NodeDB::installDefaultConfig()
#else
config.device.disable_triple_click = true;
#endif
-#if !HAS_GPS || defined(T_DECK) || defined(TLORA_T3S3_EPAPER)
+#if defined(USERPREFS_CONFIG_GPS_MODE)
+ config.position.gps_mode = USERPREFS_CONFIG_GPS_MODE;
+#elif !HAS_GPS || defined(T_DECK) || defined(TLORA_T3S3_EPAPER)
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT;
#elif !defined(GPS_RX_PIN)
if (config.position.rx_gpio == 0)
@@ -339,8 +359,8 @@ void NodeDB::installDefaultConfig()
// FIXME: Default to bluetooth capability of platform as default
config.bluetooth.enabled = true;
config.bluetooth.fixed_pin = defaultBLEPin;
-#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(HX8357_CS) || \
- defined(USE_ST7789)
+#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \
+ defined(HX8357_CS) || defined(USE_ST7789)
bool hasScreen = true;
#elif ARCH_PORTDUINO
bool hasScreen = false;
@@ -362,6 +382,9 @@ void NodeDB::installDefaultConfig()
#ifdef DISPLAY_FLIP_SCREEN
config.display.flip_screen = true;
#endif
+#ifdef RAK4630
+ config.display.wake_on_tap_or_motion = true;
+#endif
#ifdef T_WATCH_S3
config.display.screen_on_secs = 30;
config.display.wake_on_tap_or_motion = true;
@@ -455,7 +478,7 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.has_detection_sensor = true;
moduleConfig.detection_sensor.enabled = false;
- moduleConfig.detection_sensor.detection_triggered_high = true;
+ moduleConfig.detection_sensor.detection_trigger_type = meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_LOGIC_HIGH;
moduleConfig.detection_sensor.minimum_broadcast_secs = 45;
moduleConfig.has_ambient_lighting = true;
@@ -510,6 +533,7 @@ void NodeDB::installRoleDefaults(meshtastic_Config_DeviceConfig_Role role)
moduleConfig.telemetry.device_update_interval = UINT32_MAX;
moduleConfig.telemetry.environment_update_interval = UINT32_MAX;
moduleConfig.telemetry.air_quality_interval = UINT32_MAX;
+ moduleConfig.telemetry.health_update_interval = UINT32_MAX;
}
}
@@ -520,6 +544,7 @@ void NodeDB::initModuleConfigIntervals()
moduleConfig.telemetry.environment_update_interval = 0;
moduleConfig.telemetry.air_quality_interval = 0;
moduleConfig.telemetry.power_update_interval = 0;
+ moduleConfig.telemetry.health_update_interval = 0;
moduleConfig.neighbor_info.update_interval = 0;
moduleConfig.paxcounter.paxcounter_update_interval = 0;
}
@@ -602,18 +627,20 @@ void NodeDB::installDefaultDeviceState()
// devicestate.node_db_lite_count = 0;
devicestate.version = DEVICESTATE_CUR_VER;
devicestate.receive_queue_count = 0; // Not yet implemented FIXME
+ devicestate.has_rx_waypoint = false;
+ devicestate.has_rx_text_message = false;
generatePacketId(); // FIXME - ugly way to init current_packet_id;
// Set default owner name
pickNewNodeNum(); // based on macaddr now
-#ifdef CONFIG_OWNER_LONG_NAME_USERPREFS
- snprintf(owner.long_name, sizeof(owner.long_name), CONFIG_OWNER_LONG_NAME_USERPREFS);
+#ifdef USERPREFS_CONFIG_OWNER_LONG_NAME
+ snprintf(owner.long_name, sizeof(owner.long_name), USERPREFS_CONFIG_OWNER_LONG_NAME);
#else
snprintf(owner.long_name, sizeof(owner.long_name), "Meshtastic %02x%02x", ourMacAddr[4], ourMacAddr[5]);
#endif
-#ifdef CONFIG_OWNER_SHORT_NAME_USERPREFS
- snprintf(owner.short_name, sizeof(owner.short_name), CONFIG_OWNER_SHORT_NAME_USERPREFS);
+#ifdef USERPREFS_CONFIG_OWNER_SHORT_NAME
+ snprintf(owner.short_name, sizeof(owner.short_name), USERPREFS_CONFIG_OWNER_SHORT_NAME);
#else
snprintf(owner.short_name, sizeof(owner.short_name), "%02x%02x", ourMacAddr[4], ourMacAddr[5]);
#endif
@@ -637,10 +664,13 @@ void NodeDB::pickNewNodeNum()
}
meshtastic_NodeInfoLite *found;
- while ((nodeNum == NODENUM_BROADCAST || nodeNum < NUM_RESERVED) ||
- ((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 candidate = random(NUM_RESERVED, LONG_MAX); // try a new random choice
- LOG_WARN("NOTE! Our desired nodenum 0x%x is invalid or in use, so trying for 0x%x\n", nodeNum, candidate);
+ if (found)
+ LOG_WARN("NOTE! Our desired nodenum 0x%x is invalid or in use, by MAC ending in 0x%02x%02x vs our 0x%02x%02x, so "
+ "trying for 0x%x\n",
+ nodeNum, found->user.macaddr[4], found->user.macaddr[5], ourMacAddr[4], ourMacAddr[5], candidate);
nodeNum = candidate;
}
LOG_DEBUG("Using nodenum 0x%x \n", nodeNum);
@@ -706,7 +736,7 @@ void NodeDB::loadFromDisk()
//} else {
if (devicestate.version < DEVICESTATE_MIN_VER) {
LOG_WARN("Devicestate %d is old, discarding\n", devicestate.version);
- factoryReset();
+ installDefaultDeviceState();
} else {
LOG_INFO("Loaded saved devicestate version %d, with nodecount: %d\n", devicestate.version,
devicestate.node_db_lite.size());
@@ -722,7 +752,7 @@ void NodeDB::loadFromDisk()
} else {
if (config.version < DEVICESTATE_MIN_VER) {
LOG_WARN("config %d is old, discarding\n", config.version);
- installDefaultConfig();
+ installDefaultConfig(true);
} else {
LOG_INFO("Loaded saved config version %d\n", config.version);
}
@@ -1035,7 +1065,7 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde
if (p.public_key.size > 0) {
printBytes("Incoming Pubkey: ", p.public_key.bytes, 32);
if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one
- LOG_INFO("Public Key set for node, not updateing!\n");
+ LOG_INFO("Public Key set for node, not updating!\n");
// we copy the key into the incoming packet, to prevent overwrite
memcpy(p.public_key.bytes, info->user.public_key.bytes, 32);
} else {
@@ -1066,7 +1096,7 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde
// We just changed something about the user, store our DB
Throttle::execute(
&lastNodeDbSave, ONE_MINUTE_MS, []() { nodeDB->saveToDisk(SEGMENT_DEVICESTATE); },
- []() { LOG_DEBUG("Deferring NodeDB saveToDisk for now, since we saved less than a minute ago\n"); });
+ []() { LOG_DEBUG("Deferring NodeDB saveToDisk for now\n"); }); // since we saved less than a minute ago
}
return changed;
@@ -1093,8 +1123,10 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp)
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)
+ 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;
+ }
}
}
@@ -1190,4 +1222,4 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co
LOG_ERROR("A critical failure occurred, portduino is exiting...");
exit(2);
#endif
-}
\ No newline at end of file
+}
diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h
index a71f3e134..1be61759a 100644
--- a/src/mesh/NodeDB.h
+++ b/src/mesh/NodeDB.h
@@ -2,11 +2,13 @@
#include "Observer.h"
#include
+#include
#include
#include
#include "MeshTypes.h"
#include "NodeStatus.h"
+#include "configuration.h"
#include "mesh-pb-constants.h"
#include "mesh/generated/meshtastic/mesh.pb.h" // For CriticalErrorCode
@@ -182,7 +184,8 @@ class NodeDB
void cleanupMeshDB();
/// Reinit device state from scratch (not loading from disk)
- void installDefaultDeviceState(), installDefaultChannels(), installDefaultConfig(), installDefaultModuleConfig();
+ void installDefaultDeviceState(), installDefaultChannels(), installDefaultConfig(bool preserveKey),
+ installDefaultModuleConfig();
/// write to flash
/// @return true if the save was successful
diff --git a/src/mesh/PacketHistory.cpp b/src/mesh/PacketHistory.cpp
index 26a73a3fe..ed1c3c59c 100644
--- a/src/mesh/PacketHistory.cpp
+++ b/src/mesh/PacketHistory.cpp
@@ -5,6 +5,7 @@
#ifdef ARCH_PORTDUINO
#include "platform/portduino/PortduinoGlue.h"
#endif
+#include "Throttle.h"
PacketHistory::PacketHistory()
{
@@ -22,18 +23,17 @@ bool PacketHistory::wasSeenRecently(const meshtastic_MeshPacket *p, bool withUpd
return false; // Not a floodable message ID, so we don't care
}
- uint32_t now = millis();
-
PacketRecord r;
r.id = p->id;
r.sender = getFrom(p);
- r.rxTimeMsec = now;
+ r.rxTimeMsec = millis();
auto found = recentPackets.find(r);
bool seenRecently = (found != recentPackets.end()); // found not equal to .end() means packet was seen recently
- if (seenRecently && (now - found->rxTimeMsec) >= FLOOD_EXPIRE_TIME) { // Check whether found packet has already expired
- recentPackets.erase(found); // Erase and pretend packet has not been seen recently
+ if (seenRecently &&
+ !Throttle::isWithinTimespanMs(found->rxTimeMsec, FLOOD_EXPIRE_TIME)) { // Check whether found packet has already expired
+ recentPackets.erase(found); // Erase and pretend packet has not been seen recently
found = recentPackets.end();
seenRecently = false;
}
@@ -64,12 +64,10 @@ bool PacketHistory::wasSeenRecently(const meshtastic_MeshPacket *p, bool withUpd
*/
void PacketHistory::clearExpiredRecentPackets()
{
- uint32_t now = millis();
-
LOG_DEBUG("recentPackets size=%ld\n", recentPackets.size());
for (auto it = recentPackets.begin(); it != recentPackets.end();) {
- if ((now - it->rxTimeMsec) >= FLOOD_EXPIRE_TIME) {
+ if (!Throttle::isWithinTimespanMs(it->rxTimeMsec, FLOOD_EXPIRE_TIME)) {
it = recentPackets.erase(it); // erase returns iterator pointing to element immediately following the one erased
} else {
++it;
diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp
index 30af9d7b0..ad4a1f33d 100644
--- a/src/mesh/PhoneAPI.cpp
+++ b/src/mesh/PhoneAPI.cpp
@@ -8,6 +8,7 @@
#include "FSCommon.h"
#include "MeshService.h"
#include "NodeDB.h"
+#include "PacketHistory.h"
#include "PhoneAPI.h"
#include "PowerFSM.h"
#include "RadioInterface.h"
@@ -25,10 +26,13 @@
#if !MESHTASTIC_EXCLUDE_MQTT
#include "mqtt/MQTT.h"
#endif
+#include "Throttle.h"
+#include
PhoneAPI::PhoneAPI()
{
lastContactMsec = millis();
+ std::fill(std::begin(recentToRadioPacketIds), std::end(recentToRadioPacketIds), 0);
}
PhoneAPI::~PhoneAPI()
@@ -60,9 +64,11 @@ void PhoneAPI::handleStartConfig()
void PhoneAPI::close()
{
+ LOG_INFO("PhoneAPI::close()\n");
+
if (state != STATE_SEND_NOTHING) {
state = STATE_SEND_NOTHING;
-
+ resetReadIndex();
unobserve(&service->fromNumChanged);
#ifdef FSCom
unobserve(&xModem.packetReady);
@@ -70,8 +76,17 @@ void PhoneAPI::close()
releasePhonePacket(); // Don't leak phone packets on shutdown
releaseQueueStatusPhonePacket();
releaseMqttClientProxyPhonePacket();
-
+ releaseClientNotification();
onConnectionChanged(false);
+ fromRadioScratch = {};
+ toRadioScratch = {};
+ nodeInfoForPhone = {};
+ packetForPhone = NULL;
+ filesManifest.clear();
+ fromRadioNum = 0;
+ config_nonce = 0;
+ config_state = 0;
+ pauseBluetoothLogging = false;
}
}
@@ -96,8 +111,6 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // As long as the phone keeps talking to us, don't let the radio go to sleep
lastContactMsec = millis();
- // return (lastContactMsec != 0) &&
-
memset(&toRadioScratch, 0, sizeof(toRadioScratch));
if (pb_decode_from_bytes(buf, bufLength, &meshtastic_ToRadio_msg, &toRadioScratch)) {
switch (toRadioScratch.which_payload_variant) {
@@ -194,7 +207,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
auto us = nodeDB->readNextMeshNode(readIndex);
if (us) {
nodeInfoForPhone = TypeConversions::ConvertToNodeInfo(us);
- nodeInfoForPhone.hops_away = 0;
+ nodeInfoForPhone.has_hops_away = false;
nodeInfoForPhone.is_favorite = true;
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
fromRadioScratch.node_info = nodeInfoForPhone;
@@ -403,6 +416,10 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_xmodemPacket_tag;
fromRadioScratch.xmodemPacket = xmodemPacketForPhone;
xmodemPacketForPhone = meshtastic_XModem_init_zero;
+ } else if (clientNotification) {
+ fromRadioScratch.which_payload_variant = meshtastic_FromRadio_clientNotification_tag;
+ fromRadioScratch.clientNotification = *clientNotification;
+ releaseClientNotification();
} else if (packetForPhone) {
printPacket("phone downloaded packet", packetForPhone);
@@ -442,13 +459,6 @@ void PhoneAPI::sendConfigComplete()
pauseBluetoothLogging = false;
}
-void PhoneAPI::handleDisconnect()
-{
- filesManifest.clear();
- pauseBluetoothLogging = false;
- LOG_INFO("PhoneAPI disconnect\n");
-}
-
void PhoneAPI::releasePhonePacket()
{
if (packetForPhone) {
@@ -473,6 +483,14 @@ void PhoneAPI::releaseMqttClientProxyPhonePacket()
}
}
+void PhoneAPI::releaseClientNotification()
+{
+ if (clientNotification) {
+ service->releaseClientNotificationToPool(clientNotification);
+ clientNotification = NULL;
+ }
+}
+
/**
* Return true if we have data available to send to the phone
*/
@@ -507,7 +525,9 @@ bool PhoneAPI::available()
queueStatusPacketForPhone = service->getQueueStatusForPhone();
if (!mqttClientProxyMessageForPhone)
mqttClientProxyMessageForPhone = service->getMqttClientProxyMessageForPhone();
- bool hasPacket = !!queueStatusPacketForPhone || !!mqttClientProxyMessageForPhone;
+ if (!clientNotification)
+ clientNotification = service->getClientNotificationForPhone();
+ bool hasPacket = !!queueStatusPacketForPhone || !!mqttClientProxyMessageForPhone || !!clientNotification;
if (hasPacket)
return true;
@@ -541,14 +561,64 @@ bool PhoneAPI::available()
return false;
}
+void PhoneAPI::sendNotification(meshtastic_LogRecord_Level level, uint32_t replyId, const char *message)
+{
+ meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
+ cn->has_reply_id = true;
+ cn->reply_id = replyId;
+ cn->level = meshtastic_LogRecord_Level_WARNING;
+ cn->time = getValidTime(RTCQualityFromNet);
+ strncpy(cn->message, message, sizeof(cn->message));
+ service->sendClientNotification(cn);
+}
+
+bool PhoneAPI::wasSeenRecently(uint32_t id)
+{
+ for (int i = 0; i < 20; i++) {
+ if (recentToRadioPacketIds[i] == id) {
+ return true;
+ }
+ if (recentToRadioPacketIds[i] == 0) {
+ recentToRadioPacketIds[i] = id;
+ return false;
+ }
+ }
+ // If the array is full, shift all elements to the left and add the new id at the end
+ memmove(recentToRadioPacketIds, recentToRadioPacketIds + 1, (19) * sizeof(uint32_t));
+ recentToRadioPacketIds[19] = id;
+ return false;
+}
+
/**
* Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool
*/
bool PhoneAPI::handleToRadioPacket(meshtastic_MeshPacket &p)
{
printPacket("PACKET FROM PHONE", &p);
- service->handleToRadio(p);
+ if (p.id > 0 && wasSeenRecently(p.id)) {
+ LOG_DEBUG("Ignoring packet from phone, already seen recently\n");
+ return false;
+ }
+
+ if (p.decoded.portnum == meshtastic_PortNum_TRACEROUTE_APP && lastPortNumToRadio[p.decoded.portnum] &&
+ Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], THIRTY_SECONDS_MS)) {
+ LOG_WARN("Rate limiting portnum %d\n", p.decoded.portnum);
+ sendNotification(meshtastic_LogRecord_Level_WARNING, p.id, "TraceRoute can only be sent once every 30 seconds");
+ meshtastic_QueueStatus qs = router->getQueueStatus();
+ service->sendQueueStatusToPhone(qs, 0, p.id);
+ return false;
+ } else if (p.decoded.portnum == meshtastic_PortNum_POSITION_APP && lastPortNumToRadio[p.decoded.portnum] &&
+ Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], FIVE_SECONDS_MS)) {
+ LOG_WARN("Rate limiting portnum %d\n", p.decoded.portnum);
+ meshtastic_QueueStatus qs = router->getQueueStatus();
+ service->sendQueueStatusToPhone(qs, 0, p.id);
+ // FIXME: Figure out why this continues to happen
+ // sendNotification(meshtastic_LogRecord_Level_WARNING, p.id, "Position can only be sent once every 5 seconds");
+ return false;
+ }
+ lastPortNumToRadio[p.decoded.portnum] = millis();
+ service->handleToRadio(p);
return true;
}
diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h
index 5feb1c4bf..3247fee5c 100644
--- a/src/mesh/PhoneAPI.h
+++ b/src/mesh/PhoneAPI.h
@@ -2,8 +2,10 @@
#include "Observer.h"
#include "mesh-pb-constants.h"
+#include "meshtastic/portnums.pb.h"
#include
#include
+#include
#include
// Make sure that we never let our packets grow too large for one BLE packet
@@ -48,6 +50,10 @@ class PhoneAPI
uint8_t config_state = 0;
+ // Hashmap of timestamps for last time we received a packet on the API per portnum
+ std::unordered_map lastPortNumToRadio;
+ uint32_t recentToRadioPacketIds[20]; // Last 20 ToRadio MeshPacket IDs we have seen
+
/**
* Each packet sent to the phone has an incrementing count
*/
@@ -99,6 +105,11 @@ class PhoneAPI
*/
virtual bool handleToRadio(const uint8_t *buf, size_t len);
+ /**
+ * Send a (client)notification to the phone
+ */
+ virtual void sendNotification(meshtastic_LogRecord_Level level, uint32_t replyId, const char *message);
+
/**
* Get the next packet we want to send to the phone
*
@@ -137,11 +148,6 @@ class PhoneAPI
*/
virtual void onNowHasData(uint32_t fromRadioNum) {}
- /**
- * Subclasses can use this to find out when a client drops the link
- */
- virtual void handleDisconnect();
-
private:
void releasePhonePacket();
@@ -149,9 +155,13 @@ class PhoneAPI
void releaseMqttClientProxyPhonePacket();
+ void releaseClientNotification();
+
/// begin a new connection
void handleStartConfig();
+ bool wasSeenRecently(uint32_t packetId);
+
/**
* Handle a packet that the phone wants us to send. We can write to it but can not keep a reference to it
* @return true true if a packet was queued for sending
diff --git a/src/mesh/ProtobufModule.h b/src/mesh/ProtobufModule.h
index 3b438ebeb..e4d4e5c09 100644
--- a/src/mesh/ProtobufModule.h
+++ b/src/mesh/ProtobufModule.h
@@ -108,7 +108,7 @@ template class ProtobufModule : protected SinglePortModule
T *decoded = NULL;
if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.decoded.portnum == ourPortNum) {
memset(&scratch, 0, sizeof(scratch));
- auto &p = mp.decoded;
+ const meshtastic_Data &p = mp.decoded;
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch)) {
decoded = &scratch;
} else {
diff --git a/src/mesh/RF95Interface.cpp b/src/mesh/RF95Interface.cpp
index 25df3258f..581fd9034 100644
--- a/src/mesh/RF95Interface.cpp
+++ b/src/mesh/RF95Interface.cpp
@@ -1,3 +1,4 @@
+#if RADIOLIB_EXCLUDE_SX127X != 1
#include "RF95Interface.h"
#include "MeshRadio.h" // kinda yucky, but we need to know which region we are in
#include "RadioLibRF95.h"
@@ -219,17 +220,17 @@ bool RF95Interface::reconfigure()
err = lora->setSyncWord(syncWord);
if (err != RADIOLIB_ERR_NONE)
- LOG_ERROR("Radiolib error %d when attempting RF95 setSyncWord!\n", err);
+ LOG_ERROR("RF95 setSyncWord %s%d\n", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
err = lora->setCurrentLimit(currentLimit);
if (err != RADIOLIB_ERR_NONE)
- LOG_ERROR("Radiolib error %d when attempting RF95 setCurrentLimit!\n", err);
+ LOG_ERROR("RF95 setCurrentLimit %s%d\n", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
err = lora->setPreambleLength(preambleLength);
if (err != RADIOLIB_ERR_NONE)
- LOG_ERROR("Radiolib error %d when attempting RF95 setPreambleLength!\n", err);
+ LOG_ERROR(" RF95 setPreambleLength %s%d\n", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
err = lora->setFrequency(getFreq());
@@ -265,7 +266,7 @@ void RF95Interface::setStandby()
{
int err = lora->standby();
if (err != RADIOLIB_ERR_NONE)
- LOG_ERROR("Radiolib error %d when attempting RF95 standby!\n", err);
+ LOG_ERROR("RF95 standby %s%d\n", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
isReceiving = false; // If we were receiving, not any more
@@ -289,7 +290,7 @@ void RF95Interface::startReceive()
setStandby();
int err = lora->startReceive();
if (err != RADIOLIB_ERR_NONE)
- LOG_ERROR("Radiolib error %d when attempting RF95 startReceive!\n", err);
+ LOG_ERROR("RF95 startReceive %s%d\n", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
isReceiving = true;
@@ -311,7 +312,7 @@ bool RF95Interface::isChannelActive()
return true;
}
if (result != RADIOLIB_CHANNEL_FREE)
- LOG_ERROR("Radiolib error %d when attempting RF95 isChannelActive!\n", result);
+ LOG_ERROR("RF95 isChannelActive %s%d\n", radioLibErr, result);
assert(result != RADIOLIB_ERR_WRONG_MODEM);
// LOG_DEBUG("Channel is free!\n");
@@ -336,3 +337,4 @@ bool RF95Interface::sleep()
return true;
}
+#endif
\ No newline at end of file
diff --git a/src/mesh/RF95Interface.h b/src/mesh/RF95Interface.h
index a50cf93a2..327e57900 100644
--- a/src/mesh/RF95Interface.h
+++ b/src/mesh/RF95Interface.h
@@ -1,5 +1,5 @@
#pragma once
-
+#if RADIOLIB_EXCLUDE_SX127X != 1
#include "MeshRadio.h" // kinda yucky, but we need to know which region we are in
#include "RadioLibInterface.h"
#include "RadioLibRF95.h"
@@ -69,3 +69,4 @@ class RF95Interface : public RadioLibInterface
/** Some boards require GPIO control of tx vs rx paths */
void setTransmitEnable(bool txon);
};
+#endif
\ No newline at end of file
diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp
index eacd49644..7501852f2 100644
--- a/src/mesh/RadioInterface.cpp
+++ b/src/mesh/RadioInterface.cpp
@@ -136,6 +136,17 @@ const RegionInfo regions[] = {
*/
RDEF(SG_923, 917.0f, 925.0f, 100, 0, 20, true, false, false),
+ /*
+ Philippines
+ 433 - 434.7 MHz <10 mW erp, NTC approved device required
+ 868 - 869.4 MHz <25 mW erp, NTC approved device required
+ 915 - 918 MHz <250 mW EIRP, no external antennna allowed
+ https://github.com/meshtastic/firmware/issues/4948#issuecomment-2394926135
+ */
+
+ RDEF(PH_433, 433.0f, 434.7f, 100, 0, 10, true, false, false), RDEF(PH_868, 868.0f, 869.4f, 100, 0, 14, true, false, false),
+ RDEF(PH_915, 915.0f, 918.0f, 100, 0, 24, true, false, false),
+
/*
2.4 GHZ WLAN Band equivalent. Only for SX128x chips.
*/
@@ -151,7 +162,7 @@ const RegionInfo regions[] = {
const RegionInfo *myRegion;
bool RadioInterface::uses_default_frequency_slot = true;
-static uint8_t bytes[MAX_RHPACKETLEN];
+static uint8_t bytes[MAX_LORA_PAYLOAD_LEN + 1];
void initRegion()
{
@@ -202,8 +213,6 @@ uint32_t RadioInterface::getPacketTime(uint32_t pl)
uint32_t msecs = tPacket * 1000;
- LOG_DEBUG("(bw=%d, sf=%d, cr=4/%d) packet symLen=%d ms, payloadSize=%u, time %d ms\n", (int)bw, sf, cr, (int)(tSym * 1000),
- pl, msecs);
return msecs;
}
@@ -326,7 +335,7 @@ void printPacket(const char *prefix, const meshtastic_MeshPacket *p)
RadioInterface::RadioInterface()
{
- assert(sizeof(PacketHeader) == 16); // make sure the compiler did what we expected
+ assert(sizeof(PacketHeader) == MESHTASTIC_HEADER_LENGTH); // make sure the compiler did what we expected
}
bool RadioInterface::reconfigure()
@@ -420,7 +429,7 @@ void RadioInterface::applyModemConfig()
switch (loraConfig.modem_preset) {
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
- bw = (myRegion->wideLora) ? 812.5 : 500;
+ bw = (myRegion->wideLora) ? 1625.0 : 500;
cr = 5;
sf = 7;
break;
@@ -550,11 +559,11 @@ void RadioInterface::applyModemConfig()
LOG_INFO("Radio freq=%.3f, config.lora.frequency_offset=%.3f\n", freq, loraConfig.frequency_offset);
LOG_INFO("Set radio: region=%s, name=%s, config=%u, ch=%d, power=%d\n", myRegion->name, channelName, loraConfig.modem_preset,
channel_num, power);
- LOG_INFO("Radio myRegion->freqStart -> myRegion->freqEnd: %f -> %f (%f MHz)\n", myRegion->freqStart, myRegion->freqEnd,
+ LOG_INFO("myRegion->freqStart -> myRegion->freqEnd: %f -> %f (%f MHz)\n", myRegion->freqStart, myRegion->freqEnd,
myRegion->freqEnd - myRegion->freqStart);
- LOG_INFO("Radio myRegion->numChannels: %d x %.3fkHz\n", numChannels, bw);
- LOG_INFO("Radio channel_num: %d\n", channel_num + 1);
- LOG_INFO("Radio frequency: %f\n", getFreq());
+ LOG_INFO("numChannels: %d x %.3fkHz\n", numChannels, bw);
+ LOG_INFO("channel_num: %d\n", channel_num + 1);
+ LOG_INFO("frequency: %f\n", getFreq());
LOG_INFO("Slot time: %u msec\n", slotTimeMsec);
}
@@ -595,25 +604,24 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p)
lastTxStart = millis();
- PacketHeader *h = (PacketHeader *)radiobuf;
-
- h->from = p->from;
- h->to = p->to;
- h->id = p->id;
- h->channel = p->channel;
- h->next_hop = 0; // *** For future use ***
- h->relay_node = 0; // *** For future use ***
+ radioBuffer.header.from = p->from;
+ radioBuffer.header.to = p->to;
+ radioBuffer.header.id = p->id;
+ radioBuffer.header.channel = p->channel;
+ radioBuffer.header.next_hop = 0; // *** For future use ***
+ radioBuffer.header.relay_node = 0; // *** For future use ***
if (p->hop_limit > HOP_MAX) {
LOG_WARN("hop limit %d is too high, setting to %d\n", p->hop_limit, HOP_RELIABLE);
p->hop_limit = HOP_RELIABLE;
}
- h->flags = p->hop_limit | (p->want_ack ? PACKET_FLAGS_WANT_ACK_MASK : 0) | (p->via_mqtt ? PACKET_FLAGS_VIA_MQTT_MASK : 0);
- h->flags |= (p->hop_start << PACKET_FLAGS_HOP_START_SHIFT) & PACKET_FLAGS_HOP_START_MASK;
+ radioBuffer.header.flags =
+ p->hop_limit | (p->want_ack ? PACKET_FLAGS_WANT_ACK_MASK : 0) | (p->via_mqtt ? PACKET_FLAGS_VIA_MQTT_MASK : 0);
+ radioBuffer.header.flags |= (p->hop_start << PACKET_FLAGS_HOP_START_SHIFT) & PACKET_FLAGS_HOP_START_MASK;
// if the sender nodenum is zero, that means uninitialized
- assert(h->from);
+ assert(radioBuffer.header.from);
- memcpy(radiobuf + sizeof(PacketHeader), p->encrypted.bytes, p->encrypted.size);
+ memcpy(radioBuffer.payload, p->encrypted.bytes, p->encrypted.size);
sendingPacket = p;
return p->encrypted.size + sizeof(PacketHeader);
diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h
index f1016e3d8..89a4c7087 100644
--- a/src/mesh/RadioInterface.h
+++ b/src/mesh/RadioInterface.h
@@ -9,7 +9,9 @@
#define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission
-#define MAX_RHPACKETLEN 256
+#define MAX_LORA_PAYLOAD_LEN 255 // max length of 255 per Semtech's datasheets on SX12xx
+#define MESHTASTIC_HEADER_LENGTH 16
+#define MESHTASTIC_PKC_OVERHEAD 12
#define PACKET_FLAGS_HOP_LIMIT_MASK 0x07
#define PACKET_FLAGS_WANT_ACK_MASK 0x08
@@ -43,6 +45,20 @@ typedef struct {
uint8_t relay_node;
} PacketHeader;
+/**
+ * This structure represent the structured buffer : a PacketHeader then the payload. The whole is
+ * MAX_LORA_PAYLOAD_LEN + 1 length
+ * It makes the use of its data easier, and avoids manipulating pointers (and potential non aligned accesses)
+ */
+typedef struct {
+ /** The header, as defined just before */
+ PacketHeader header;
+
+ /** The payload, of maximum length minus the header, aligned just to be sure */
+ uint8_t payload[MAX_LORA_PAYLOAD_LEN + 1 - sizeof(PacketHeader)] __attribute__((__aligned__));
+
+} RadioBuffer;
+
/**
* Basic operations all radio chipsets must implement.
*
@@ -89,8 +105,7 @@ class RadioInterface
/**
* A temporary buffer used for sending/receiving packets, sized to hold the biggest buffer we might need
* */
- uint8_t radiobuf[MAX_RHPACKETLEN];
-
+ RadioBuffer radioBuffer __attribute__((__aligned__));
/**
* Enqueue a received packet for the registered receiver
*/
diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp
index f299ebff2..818826018 100644
--- a/src/mesh/RadioLibInterface.cpp
+++ b/src/mesh/RadioLibInterface.cpp
@@ -3,6 +3,7 @@
#include "NodeDB.h"
#include "PowerMon.h"
#include "SPILock.h"
+#include "Throttle.h"
#include "configuration.h"
#include "error.h"
#include "main.h"
@@ -41,7 +42,7 @@ void LockingArduinoHal::spiTransfer(uint8_t *out, size_t len, uint8_t *in)
uint32_t start = millis();
while (digitalRead(busy)) {
- if (millis() - start >= 2000) {
+ if (!Throttle::isWithinTimespanMs(start, 2000)) {
LOG_ERROR("GPIO mid-transfer timeout, is it connected?");
return;
}
@@ -114,7 +115,7 @@ bool RadioLibInterface::canSendImmediately()
}
// If we've been trying to send the same packet more than one minute and we haven't gotten a
// TX IRQ from the radio, the radio is probably broken.
- if (busyTx && (millis() - lastTxStart > 60000)) {
+ if (busyTx && !Throttle::isWithinTimespanMs(lastTxStart, 60000)) {
LOG_ERROR("Hardware Failure! busyTx for more than 60s\n");
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_TRANSMIT_FAILED);
// reboot in 5 seconds when this condition occurs.
@@ -128,6 +129,28 @@ bool RadioLibInterface::canSendImmediately()
return true;
}
+bool RadioLibInterface::receiveDetected(uint16_t irq, ulong syncWordHeaderValidFlag, ulong preambleDetectedFlag)
+{
+ bool detected = (irq & (syncWordHeaderValidFlag | preambleDetectedFlag));
+ // Handle false detections
+ if (detected) {
+ if (!activeReceiveStart) {
+ activeReceiveStart = millis();
+ } else if (!Throttle::isWithinTimespanMs(activeReceiveStart, 2 * preambleTimeMsec) && !(irq & syncWordHeaderValidFlag)) {
+ // The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag
+ activeReceiveStart = 0;
+ LOG_DEBUG("Ignore false preamble detection.\n");
+ return false;
+ } else if (!Throttle::isWithinTimespanMs(activeReceiveStart, maxPacketTimeMsec)) {
+ // We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag
+ activeReceiveStart = 0;
+ LOG_DEBUG("Ignore false header detection.\n");
+ return false;
+ }
+ }
+ return detected;
+}
+
/// Send a packet (possibly by enquing in a private fifo). This routine will
/// later free() the packet to pool. This routine is not allowed to stall because it is called from
/// bluetooth comms code. If the txmit queue is empty it might return an error
@@ -144,7 +167,7 @@ ErrorCode RadioLibInterface::send(meshtastic_MeshPacket *p)
}
} else {
- LOG_WARN("send - lora tx disable because RegionCode_Unset\n");
+ LOG_WARN("send - lora tx disabled because RegionCode_Unset\n");
packetPool.release(p);
return ERRNO_DISABLED;
}
@@ -163,7 +186,7 @@ ErrorCode RadioLibInterface::send(meshtastic_MeshPacket *p)
#ifndef LORA_DISABLE_SENDING
printPacket("enqueuing for send", p);
- LOG_DEBUG("txGood=%d,rxGood=%d,rxBad=%d\n", txGood, rxGood, rxBad);
+ LOG_DEBUG("txGood=%d,txRelay=%d,rxGood=%d,rxBad=%d\n", txGood, txRelay, rxGood, rxBad);
ErrorCode res = txQueue.enqueue(p) ? ERRNO_OK : ERRNO_UNKNOWN;
if (res != ERRNO_OK) { // we weren't able to queue it, so we must drop it to prevent leaks
@@ -330,6 +353,8 @@ void RadioLibInterface::completeSending()
if (p) {
txGood++;
+ if (!isFromUs(p))
+ txRelay++;
printPacket("Completed sending", p);
// We are done sending that packet, release it
@@ -356,7 +381,15 @@ void RadioLibInterface::handleReceiveInterrupt()
xmitMsec = getPacketTime(length);
- int state = iface->readData(radiobuf, length);
+#ifndef DISABLE_WELCOME_UNSET
+ if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
+ LOG_WARN("recv - lora rx disabled because RegionCode_Unset\n");
+ airTime->logAirtime(RX_ALL_LOG, xmitMsec);
+ return;
+ }
+#endif
+
+ int state = iface->readData((uint8_t *)&radioBuffer, length);
if (state != RADIOLIB_ERR_NONE) {
LOG_ERROR("ignoring received packet due to error=%d\n", state);
rxBad++;
@@ -366,7 +399,6 @@ void RadioLibInterface::handleReceiveInterrupt()
} else {
// Skip the 4 headers that are at the beginning of the rxBuf
int32_t payloadLen = length - sizeof(PacketHeader);
- const uint8_t *payload = radiobuf + sizeof(PacketHeader);
// check for short packets
if (payloadLen < 0) {
@@ -374,10 +406,9 @@ void RadioLibInterface::handleReceiveInterrupt()
rxBad++;
airTime->logAirtime(RX_ALL_LOG, xmitMsec);
} else {
- const PacketHeader *h = (PacketHeader *)radiobuf;
rxGood++;
// altered packet with "from == 0" can do Remote Node Administration without permission
- if (h->from == 0) {
+ if (radioBuffer.header.from == 0) {
LOG_WARN("ignoring received packet without sender\n");
return;
}
@@ -387,22 +418,22 @@ void RadioLibInterface::handleReceiveInterrupt()
// nodes.
meshtastic_MeshPacket *mp = packetPool.allocZeroed();
- mp->from = h->from;
- mp->to = h->to;
- mp->id = h->id;
- mp->channel = h->channel;
+ mp->from = radioBuffer.header.from;
+ mp->to = radioBuffer.header.to;
+ mp->id = radioBuffer.header.id;
+ mp->channel = radioBuffer.header.channel;
assert(HOP_MAX <= PACKET_FLAGS_HOP_LIMIT_MASK); // If hopmax changes, carefully check this code
- mp->hop_limit = h->flags & PACKET_FLAGS_HOP_LIMIT_MASK;
- mp->hop_start = (h->flags & PACKET_FLAGS_HOP_START_MASK) >> PACKET_FLAGS_HOP_START_SHIFT;
- mp->want_ack = !!(h->flags & PACKET_FLAGS_WANT_ACK_MASK);
- mp->via_mqtt = !!(h->flags & PACKET_FLAGS_VIA_MQTT_MASK);
+ mp->hop_limit = radioBuffer.header.flags & PACKET_FLAGS_HOP_LIMIT_MASK;
+ mp->hop_start = (radioBuffer.header.flags & PACKET_FLAGS_HOP_START_MASK) >> PACKET_FLAGS_HOP_START_SHIFT;
+ mp->want_ack = !!(radioBuffer.header.flags & PACKET_FLAGS_WANT_ACK_MASK);
+ mp->via_mqtt = !!(radioBuffer.header.flags & PACKET_FLAGS_VIA_MQTT_MASK);
addReceiveMetadata(mp);
mp->which_payload_variant =
meshtastic_MeshPacket_encrypted_tag; // Mark that the payload is still encrypted at this point
assert(((uint32_t)payloadLen) <= sizeof(mp->encrypted.bytes));
- memcpy(mp->encrypted.bytes, payload, payloadLen);
+ memcpy(mp->encrypted.bytes, radioBuffer.payload, payloadLen);
mp->encrypted.size = payloadLen;
printPacket("Lora RX", mp);
@@ -437,14 +468,14 @@ void RadioLibInterface::startSend(meshtastic_MeshPacket *txp)
{
printPacket("Starting low level send", txp);
if (disabled || !config.lora.tx_enabled) {
- LOG_WARN("startSend is dropping tx packet because we are disabled\n");
+ LOG_WARN("Drop Tx packet because LoRa Tx disabled\n");
packetPool.release(txp);
} else {
configHardwareForSend(); // must be after setStandby
size_t numbytes = beginSending(txp);
- int res = iface->startTransmit(radiobuf, numbytes);
+ int res = iface->startTransmit((uint8_t *)&radioBuffer, numbytes);
if (res != RADIOLIB_ERR_NONE) {
LOG_ERROR("startTransmit failed, error=%d\n", res);
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_RADIO_SPI_BUG);
diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h
index edcbb394f..673f53a32 100644
--- a/src/mesh/RadioLibInterface.h
+++ b/src/mesh/RadioLibInterface.h
@@ -107,7 +107,7 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
/**
* Debugging counts
*/
- uint32_t rxBad = 0, rxGood = 0, txGood = 0;
+ uint32_t rxBad = 0, rxGood = 0, txGood = 0, txRelay = 0;
public:
RadioLibInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
@@ -167,6 +167,10 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
meshtastic_QueueStatus getQueueStatus();
protected:
+ uint32_t activeReceiveStart = 0;
+
+ bool receiveDetected(uint16_t irq, ulong syncWordHeaderValidFlag, ulong preambleDetectedFlag);
+
/** Do any hardware setup needed on entry into send configuration for the radio.
* Subclasses can customize, but must also call this base method */
virtual void configHardwareForSend();
@@ -192,4 +196,6 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
* Subclasses must override, implement and then call into this base class implementation
*/
virtual void setStandby();
+
+ const char *radioLibErr = "RadioLib err=\n";
};
\ No newline at end of file
diff --git a/src/mesh/RadioLibRF95.cpp b/src/mesh/RadioLibRF95.cpp
index a202d4f4d..fe9bbdc93 100644
--- a/src/mesh/RadioLibRF95.cpp
+++ b/src/mesh/RadioLibRF95.cpp
@@ -1,3 +1,4 @@
+#if RADIOLIB_EXCLUDE_SX127X != 1
#include "RadioLibRF95.h"
#include "configuration.h"
@@ -81,4 +82,5 @@ uint8_t RadioLibRF95::readReg(uint8_t addr)
{
Module *mod = this->getMod();
return mod->SPIreadRegister(addr);
-}
\ No newline at end of file
+}
+#endif
\ No newline at end of file
diff --git a/src/mesh/RadioLibRF95.h b/src/mesh/RadioLibRF95.h
index 3bdb794f2..916a33234 100644
--- a/src/mesh/RadioLibRF95.h
+++ b/src/mesh/RadioLibRF95.h
@@ -1,4 +1,5 @@
#pragma once
+#if RADIOLIB_EXCLUDE_SX127X != 1
#include
/*!
@@ -69,3 +70,4 @@ class RadioLibRF95 : public SX1278
// use the previous value
float currentLimit = 100;
};
+#endif
\ No newline at end of file
diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp
index 1f2c01473..fa05e7973 100644
--- a/src/mesh/ReliableRouter.cpp
+++ b/src/mesh/ReliableRouter.cpp
@@ -78,7 +78,7 @@ bool ReliableRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
* Resending real ACKs is omitted, as you might receive a packet multiple times due to flooding and
* flooding this ACK back to the original sender already adds redundancy. */
bool isRepeated = p->hop_start == 0 ? (p->hop_limit == HOP_RELIABLE) : (p->hop_start == p->hop_limit);
- if (wasSeenRecently(p, false) && isRepeated && !MeshModule::currentReply && p->to != nodeDB->getNodeNum()) {
+ if (wasSeenRecently(p, false) && isRepeated && !MeshModule::currentReply && !isToUs(p)) {
LOG_DEBUG("Resending implicit ack for a repeated floodmsg\n");
meshtastic_MeshPacket *tosend = packetPool.allocCopy(*p);
tosend->hop_limit--; // bump down the hop count
@@ -102,17 +102,15 @@ bool ReliableRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
*/
void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c)
{
- NodeNum ourNode = getNodeNum();
-
- if (p->to == ourNode) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability)
+ if (isToUs(p)) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability)
if (p->want_ack) {
if (MeshModule::currentReply) {
- LOG_DEBUG("Some other module has replied to this message, no need for a 2nd ack\n");
+ LOG_DEBUG("Another module replied to this message, no need for 2nd ack\n");
} else if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, p->hop_start, p->hop_limit);
} else if (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && p->channel == 0 &&
(nodeDB->getMeshNode(p->from) == nullptr || nodeDB->getMeshNode(p->from)->user.public_key.size == 0)) {
- LOG_INFO("This looks like it might be a PKI packet from an unknown node, so send PKI_UNKNOWN_PUBKEY\n");
+ LOG_INFO("PKI packet from unknown node, send PKI_UNKNOWN_PUBKEY\n");
sendAckNak(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY, getFrom(p), p->id, channels.getPrimaryIndex(),
p->hop_start, p->hop_limit);
} else {
@@ -124,7 +122,7 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && c &&
c->error_reason == meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY) {
if (owner.public_key.size == 32) {
- LOG_INFO("This seems like a remote PKI decrypt failure, so send a NodeInfo");
+ LOG_INFO("PKI decrypt failure, send a NodeInfo");
nodeInfoModule->sendOurNodeInfo(p->from, false, p->channel, true);
}
}
@@ -136,11 +134,10 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
// We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records
if (ackId || nakId) {
+ LOG_DEBUG("Received a %s for 0x%x, stopping retransmissions\n", ackId ? "ACK" : "NAK", ackId);
if (ackId) {
- LOG_DEBUG("Received an ack for 0x%x, stopping retransmissions\n", ackId);
stopRetransmission(p->to, ackId);
} else {
- LOG_DEBUG("Received a nak for 0x%x, stopping retransmissions\n", nakId);
stopRetransmission(p->to, nakId);
}
}
diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp
index 8d27c5040..d6655d940 100644
--- a/src/mesh/Router.cpp
+++ b/src/mesh/Router.cpp
@@ -36,8 +36,8 @@ static MemoryDynamic staticPool;
Allocator &packetPool = staticPool;
-static uint8_t bytes[MAX_RHPACKETLEN];
-static uint8_t ScratchEncrypted[MAX_RHPACKETLEN];
+static uint8_t bytes[MAX_LORA_PAYLOAD_LEN + 1] __attribute__((__aligned__));
+static uint8_t ScratchEncrypted[MAX_LORA_PAYLOAD_LEN + 1] __attribute__((__aligned__));
/**
* Constructor
@@ -169,7 +169,7 @@ ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src)
LOG_ERROR("Packet received with to: of 0!\n");
}
// No need to deliver externally if the destination is the local node
- if (p->to == nodeDB->getNodeNum()) {
+ if (isToUs(p)) {
printPacket("Enqueued local", p);
enqueueReceivedMessage(p);
return ERRNO_OK;
@@ -185,9 +185,12 @@ ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src)
handleReceived(p, src);
}
- if (!p->channel) { // don't override if a channel was requested
- p->channel = nodeDB->getMeshNodeChannel(p->to);
- LOG_DEBUG("localSend to channel %d\n", p->channel);
+ if (!p->channel && !p->pki_encrypted) { // don't override if a channel was requested
+ meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to);
+ if (node && node->user.public_key.size == 0) {
+ p->channel = node->channel;
+ LOG_DEBUG("localSend to channel %d\n", p->channel);
+ }
}
return send(p);
@@ -201,7 +204,7 @@ ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src)
*/
ErrorCode Router::send(meshtastic_MeshPacket *p)
{
- if (p->to == nodeDB->getNodeNum()) {
+ if (isToUs(p)) {
LOG_ERROR("BUG! send() called with packet destined for local node!\n");
packetPool.release(p);
return meshtastic_Routing_Error_BAD_REQUEST;
@@ -223,7 +226,7 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
service->sendClientNotification(cn);
#endif
meshtastic_Routing_Error err = meshtastic_Routing_Error_DUTY_CYCLE_LIMIT;
- if (getFrom(p) == nodeDB->getNodeNum()) { // only send NAK to API, not to the mesh
+ if (isFromUs(p)) { // only send NAK to API, not to the mesh
abortSendAndNak(err, p);
} else {
packetPool.release(p);
@@ -245,7 +248,7 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
p->from = getFrom(p);
// If we are the original transmitter, set the hop limit with which we start
- if (p->from == getNodeNum())
+ if (isFromUs(p))
p->hop_start = p->hop_limit;
// If the packet hasn't yet been encrypted, do so now (it might already be encrypted if we are just forwarding it)
@@ -270,7 +273,7 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
}
#if !MESHTASTIC_EXCLUDE_MQTT
// Only publish to MQTT if we're the original transmitter of the packet
- if (moduleConfig.mqtt.enabled && p->from == nodeDB->getNodeNum() && mqtt) {
+ if (moduleConfig.mqtt.enabled && isFromUs(p) && mqtt) {
mqtt->onSend(*p, *p_decoded, chIndex);
}
#endif
@@ -325,15 +328,15 @@ bool perhapsDecode(meshtastic_MeshPacket *p)
memcpy(ScratchEncrypted, p->encrypted.bytes, rawSize);
#if !(MESHTASTIC_EXCLUDE_PKI)
// Attempt PKI decryption first
- if (p->channel == 0 && p->to == nodeDB->getNodeNum() && p->to > 0 && p->to != NODENUM_BROADCAST &&
- nodeDB->getMeshNode(p->from) != nullptr && nodeDB->getMeshNode(p->from)->user.public_key.size > 0 &&
- nodeDB->getMeshNode(p->to)->user.public_key.size > 0 && rawSize > 12) {
+ if (p->channel == 0 && isToUs(p) && p->to > 0 && p->to != NODENUM_BROADCAST && nodeDB->getMeshNode(p->from) != nullptr &&
+ nodeDB->getMeshNode(p->from)->user.public_key.size > 0 && nodeDB->getMeshNode(p->to)->user.public_key.size > 0 &&
+ rawSize > MESHTASTIC_PKC_OVERHEAD) {
LOG_DEBUG("Attempting PKI decryption\n");
if (crypto->decryptCurve25519(p->from, p->id, rawSize, ScratchEncrypted, bytes)) {
LOG_INFO("PKI Decryption worked!\n");
memset(&p->decoded, 0, sizeof(p->decoded));
- rawSize -= 12;
+ rawSize -= MESHTASTIC_PKC_OVERHEAD;
if (pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded) &&
p->decoded.portnum != meshtastic_PortNum_UNKNOWN_APP) {
decrypted = true;
@@ -344,8 +347,11 @@ bool perhapsDecode(meshtastic_MeshPacket *p)
// memcpy(bytes, ScratchEncrypted, rawSize); // TODO: Rename the bytes buffers
// chIndex = 8;
} else {
+ LOG_ERROR("PKC Decrypted, but pb_decode failed!\n");
return false;
}
+ } else {
+ LOG_WARN("PKC decrypt attempted but failed!\n");
}
}
#endif
@@ -378,6 +384,8 @@ bool perhapsDecode(meshtastic_MeshPacket *p)
// parsing was successful
p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded
p->channel = chIndex; // change to store the index instead of the hash
+ if (p->decoded.has_bitfield)
+ p->decoded.want_response |= p->decoded.bitfield & BITFIELD_WANT_RESPONSE_MASK;
/* Not actually ever used.
// Decompress if needed. jm
@@ -424,6 +432,12 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
// If the packet is not yet encrypted, do so now
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
+ if (isFromUs(p)) {
+ p->decoded.has_bitfield = true;
+ p->decoded.bitfield |= (config.lora.config_ok_to_mqtt << BITFIELD_OK_TO_MQTT_SHIFT);
+ p->decoded.bitfield |= (p->decoded.want_response << BITFIELD_WANT_RESPONSE_SHIFT);
+ }
+
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_Data_msg, &p->decoded);
/* Not actually used, so save the cycles
@@ -461,7 +475,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
}
} */
- if (numbytes > MAX_RHPACKETLEN)
+ if (numbytes + MESHTASTIC_HEADER_LENGTH > MAX_LORA_PAYLOAD_LEN)
return meshtastic_Routing_Error_TOO_LARGE;
// printBytes("plaintext", bytes, numbytes);
@@ -470,21 +484,31 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
#if !(MESHTASTIC_EXCLUDE_PKI)
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to);
- if (!owner.is_licensed && config.security.private_key.size == 32 && p->to != NODENUM_BROADCAST && node != nullptr &&
- node->user.public_key.size > 0 && p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP &&
- p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP && p->decoded.portnum != meshtastic_PortNum_ROUTING_APP &&
- p->decoded.portnum != meshtastic_PortNum_POSITION_APP) {
+ // We may want to retool things so we can send a PKC packet when the client specifies a key and nodenum, even if the node
+ // is not in the local nodedb
+ if (
+ // Don't use PKC with Ham mode
+ !owner.is_licensed &&
+ // Don't use PKC if it's not explicitly requested and a non-primary channel is requested
+ !(p->pki_encrypted != true && p->channel > 0) &&
+ // Check for valid keys and single node destination
+ config.security.private_key.size == 32 && p->to != NODENUM_BROADCAST && node != nullptr &&
+ // Check for a known public key for the destination
+ (node->user.public_key.size == 32) &&
+ // Some portnums either make no sense to send with PKC
+ p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP &&
+ p->decoded.portnum != meshtastic_PortNum_ROUTING_APP && p->decoded.portnum != meshtastic_PortNum_POSITION_APP) {
LOG_DEBUG("Using PKI!\n");
- if (numbytes + 12 > MAX_RHPACKETLEN)
+ if (numbytes + MESHTASTIC_HEADER_LENGTH + MESHTASTIC_PKC_OVERHEAD > MAX_LORA_PAYLOAD_LEN)
return meshtastic_Routing_Error_TOO_LARGE;
if (p->pki_encrypted && !memfll(p->public_key.bytes, 0, 32) &&
memcmp(p->public_key.bytes, node->user.public_key.bytes, 32) != 0) {
- LOG_WARN("Client public key for client differs from requested! Requested 0x%02x, but stored key begins 0x%02x\n",
- *p->public_key.bytes, *node->user.public_key.bytes);
+ LOG_WARN("Client public key differs from requested: 0x%02x, stored key begins 0x%02x\n", *p->public_key.bytes,
+ *node->user.public_key.bytes);
return meshtastic_Routing_Error_PKI_FAILED;
}
crypto->encryptCurve25519(p->to, getFrom(p), p->id, numbytes, bytes, ScratchEncrypted);
- numbytes += 12;
+ numbytes += MESHTASTIC_PKC_OVERHEAD;
memcpy(p->encrypted.bytes, ScratchEncrypted, numbytes);
p->channel = 0;
p->pki_encrypted = true;
@@ -566,7 +590,7 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
skipHandle = true;
}
-#if EVENT_MODE
+#if USERPREFS_EVENT_MODE
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag &&
(p->decoded.portnum == meshtastic_PortNum_ATAK_FORWARDER || p->decoded.portnum == meshtastic_PortNum_ATAK_PLUGIN ||
p->decoded.portnum == meshtastic_PortNum_PAXCOUNTER_APP || p->decoded.portnum == meshtastic_PortNum_IP_TUNNEL_APP ||
@@ -589,7 +613,7 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
#if !MESHTASTIC_EXCLUDE_MQTT
// After potentially altering it, publish received message to MQTT if we're not the original transmitter of the packet
- if (decoded && moduleConfig.mqtt.enabled && getFrom(p) != nodeDB->getNodeNum() && mqtt)
+ if (decoded && moduleConfig.mqtt.enabled && !isFromUs(p) && mqtt)
mqtt->onSend(*p_encrypted, *p, p->channel);
#endif
}
diff --git a/src/mesh/Router.h b/src/mesh/Router.h
index 98486745b..8ebc1a3e5 100644
--- a/src/mesh/Router.h
+++ b/src/mesh/Router.h
@@ -82,6 +82,10 @@ class Router : protected concurrency::OSThread
*/
virtual ErrorCode send(meshtastic_MeshPacket *p);
+ /* Statistics for the amount of duplicate received packets and the amount of times we cancel a relay because someone did it
+ before us */
+ uint32_t rxDupe = 0, txRelayCanceled = 0;
+
protected:
friend class RoutingModule;
@@ -148,3 +152,8 @@ extern Router *router;
/// Generate a unique packet id
// FIXME, move this someplace better
PacketId generatePacketId();
+
+#define BITFIELD_WANT_RESPONSE_SHIFT 1
+#define BITFIELD_OK_TO_MQTT_SHIFT 0
+#define BITFIELD_WANT_RESPONSE_MASK (1 << BITFIELD_WANT_RESPONSE_SHIFT)
+#define BITFIELD_OK_TO_MQTT_MASK (1 << BITFIELD_OK_TO_MQTT_SHIFT)
\ No newline at end of file
diff --git a/src/mesh/SX1262Interface.cpp b/src/mesh/SX1262Interface.cpp
index e96e72b71..4c0dea00b 100644
--- a/src/mesh/SX1262Interface.cpp
+++ b/src/mesh/SX1262Interface.cpp
@@ -1,3 +1,4 @@
+#if RADIOLIB_EXCLUDE_SX126X != 1
#include "SX1262Interface.h"
#include "configuration.h"
#include "error.h"
@@ -6,4 +7,5 @@ SX1262Interface::SX1262Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, R
RADIOLIB_PIN_TYPE busy)
: SX126xInterface(hal, cs, irq, rst, busy)
{
-}
\ No newline at end of file
+}
+#endif
\ No newline at end of file
diff --git a/src/mesh/SX1262Interface.h b/src/mesh/SX1262Interface.h
index 31a12ae90..6e4616c8b 100644
--- a/src/mesh/SX1262Interface.h
+++ b/src/mesh/SX1262Interface.h
@@ -1,3 +1,4 @@
+#if RADIOLIB_EXCLUDE_SX126X != 1
#pragma once
#include "SX126xInterface.h"
@@ -10,4 +11,5 @@ class SX1262Interface : public SX126xInterface
public:
SX1262Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy);
-};
\ No newline at end of file
+};
+#endif
\ No newline at end of file
diff --git a/src/mesh/SX1268Interface.cpp b/src/mesh/SX1268Interface.cpp
index ea299fce5..fe6e9af89 100644
--- a/src/mesh/SX1268Interface.cpp
+++ b/src/mesh/SX1268Interface.cpp
@@ -1,3 +1,4 @@
+#if RADIOLIB_EXCLUDE_SX126X != 1
#include "SX1268Interface.h"
#include "configuration.h"
#include "error.h"
@@ -15,4 +16,5 @@ float SX1268Interface::getFreq()
return 433.125f;
else
return savedFreq;
-}
\ No newline at end of file
+}
+#endif
\ No newline at end of file
diff --git a/src/mesh/SX1268Interface.h b/src/mesh/SX1268Interface.h
index c8bcf20a7..cc6dd3534 100644
--- a/src/mesh/SX1268Interface.h
+++ b/src/mesh/SX1268Interface.h
@@ -1,4 +1,5 @@
#pragma once
+#if RADIOLIB_EXCLUDE_SX126X != 1
#include "SX126xInterface.h"
@@ -13,3 +14,4 @@ class SX1268Interface : public SX126xInterface
SX1268Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy);
};
+#endif
\ No newline at end of file
diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp
index fcb3e4edb..924cdfa9f 100644
--- a/src/mesh/SX126xInterface.cpp
+++ b/src/mesh/SX126xInterface.cpp
@@ -1,3 +1,4 @@
+#if RADIOLIB_EXCLUDE_SX126X != 1
#include "SX126xInterface.h"
#include "configuration.h"
#include "error.h"
@@ -6,6 +7,8 @@
#include "PortduinoGlue.h"
#endif
+#include "Throttle.h"
+
// Particular boards might define a different max power based on what their hardware can do, default to max power output if not
// specified (may be dangerous if using external PA and SX126x power config forgotten)
#ifndef SX126X_MAX_POWER
@@ -25,15 +28,35 @@ SX126xInterface::SX126xInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs
/// \return true if initialisation succeeded.
template bool SX126xInterface::init()
{
-#ifdef SX126X_POWER_EN
- pinMode(SX126X_POWER_EN, OUTPUT);
+
+// Typically, the RF switch on SX126x boards is controlled by two signals, which are negations of each other (switched RFIO
+// paths). The negation is usually performed in hardware, or (suboptimal design) TXEN and RXEN are the two inputs to this style of
+// RF switch. On some boards, there is no hardware negation between CTRL and ¬CTRL, but CTRL is internally connected to DIO2, and
+// DIO2's switching is done by the SX126X itself, so the MCU can't control ¬CTRL at exactly the same time. One solution would be
+// to set ¬CTRL as SX126X_TXEN or SX126X_RXEN, but they may already be used for another purpose, such as controlling another
+// PA/LNA. Keeping ¬CTRL high seems to work, as long CTRL=1, ¬CTRL=1 has the opposite and stable RF path effect as CTRL=0 and
+// ¬CTRL=1, this depends on the RF switch, but it seems this usually works. Better hardware design, which is done most the time,
+// means this workaround is not necessary.
+#ifdef SX126X_ANT_SW // Perhaps add RADIOLIB_NC check, and beforehand define as such if it is undefined, but it is not commonly
+ // used and not part of the 'default' set of pin definitions.
+ digitalWrite(SX126X_ANT_SW, HIGH);
+ pinMode(SX126X_ANT_SW, OUTPUT);
+#endif
+
+#ifdef SX126X_POWER_EN // Perhaps add RADIOLIB_NC check, and beforehand define as such if it is undefined, but it is not commonly
+ // used and not part of the 'default' set of pin definitions.
digitalWrite(SX126X_POWER_EN, HIGH);
+ pinMode(SX126X_POWER_EN, OUTPUT);
#endif
#if ARCH_PORTDUINO
float tcxoVoltage = 0;
if (settingsMap[dio3_tcxo_voltage])
tcxoVoltage = 1.8;
+ if (settingsMap[sx126x_ant_sw] != RADIOLIB_NC) {
+ digitalWrite(settingsMap[sx126x_ant_sw], HIGH);
+ pinMode(settingsMap[sx126x_ant_sw], OUTPUT);
+ }
// FIXME: correct logic to default to not using TCXO if no voltage is specified for SX126X_DIO3_TCXO_VOLTAGE
#elif !defined(SX126X_DIO3_TCXO_VOLTAGE)
float tcxoVoltage =
@@ -81,21 +104,19 @@ template bool SX126xInterface::init()
LOG_DEBUG("Current limit set to %f\n", currentLimit);
LOG_DEBUG("Current limit set result %d\n", res);
-#ifdef SX126X_DIO2_AS_RF_SWITCH
- LOG_DEBUG("Setting DIO2 as RF switch\n");
- bool dio2AsRfSwitch = true;
-#elif defined(ARCH_PORTDUINO)
- bool dio2AsRfSwitch = false;
- if (settingsMap[dio2_as_rf_switch]) {
- LOG_DEBUG("Setting DIO2 as RF switch\n");
- dio2AsRfSwitch = true;
- }
-#else
- LOG_DEBUG("Setting DIO2 as not RF switch\n");
- bool dio2AsRfSwitch = false;
-#endif
if (res == RADIOLIB_ERR_NONE) {
+#ifdef SX126X_DIO2_AS_RF_SWITCH
+ bool dio2AsRfSwitch = true;
+#elif defined(ARCH_PORTDUINO)
+ bool dio2AsRfSwitch = false;
+ if (settingsMap[dio2_as_rf_switch]) {
+ dio2AsRfSwitch = true;
+ }
+#else
+ bool dio2AsRfSwitch = false;
+#endif
res = lora.setDio2AsRfSwitch(dio2AsRfSwitch);
+ LOG_DEBUG("Set DIO2 as %sRF switch, result: %d\n", dio2AsRfSwitch ? "" : "not ", res);
}
// If a pin isn't defined, we set it to RADIOLIB_NC, it is safe to always do external RF switching with RADIOLIB_NC as it has
@@ -182,17 +203,17 @@ template bool SX126xInterface::reconfigure()
err = lora.setSyncWord(syncWord);
if (err != RADIOLIB_ERR_NONE)
- LOG_ERROR("Radiolib error %d when attempting SX126X setSyncWord!\n", err);
+ LOG_ERROR("SX126X setSyncWord %s%d\n", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
err = lora.setCurrentLimit(currentLimit);
if (err != RADIOLIB_ERR_NONE)
- LOG_ERROR("Radiolib error %d when attempting SX126X setCurrentLimit!\n", err);
+ LOG_ERROR("SX126X setCurrentLimit %s%d\n", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
err = lora.setPreambleLength(preambleLength);
if (err != RADIOLIB_ERR_NONE)
- LOG_ERROR("Radiolib error %d when attempting SX126X setPreambleLength!\n", err);
+ LOG_ERROR("SX126X setPreambleLength %s%d\n", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
err = lora.setFrequency(getFreq());
@@ -204,7 +225,7 @@ template bool SX126xInterface::reconfigure()
err = lora.setOutputPower(power);
if (err != RADIOLIB_ERR_NONE)
- LOG_ERROR("Radiolib error %d when attempting SX126X setOutputPower!\n", err);
+ LOG_ERROR("SX126X setOutputPower %s%d\n", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
startReceive(); // restart receiving
@@ -224,7 +245,7 @@ template void SX126xInterface::setStandby()
int err = lora.standby();
if (err != RADIOLIB_ERR_NONE)
- LOG_DEBUG("SX126x standby failed with error %d\n", err);
+ LOG_DEBUG("SX126x standby %s%d\n", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
isReceiving = false; // If we were receiving, not any more
@@ -266,7 +287,7 @@ template void SX126xInterface::startReceive()
// Furthermore, we need the PREAMBLE_DETECTED and HEADER_VALID IRQ flag to detect whether we are actively receiving
int err = lora.startReceiveDutyCycleAuto(preambleLength, 8, RADIOLIB_IRQ_RX_DEFAULT_FLAGS | RADIOLIB_IRQ_PREAMBLE_DETECTED);
if (err != RADIOLIB_ERR_NONE)
- LOG_ERROR("Radiolib error %d when attempting SX126X startReceiveDutyCycleAuto!\n", err);
+ LOG_ERROR("SX126X startReceiveDutyCycleAuto %s%d\n", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
RadioLibInterface::startReceive();
@@ -287,7 +308,7 @@ template bool SX126xInterface::isChannelActive()
if (result == RADIOLIB_LORA_DETECTED)
return true;
if (result != RADIOLIB_CHANNEL_FREE)
- LOG_ERROR("Radiolib error %d when attempting SX126X scanChannel!\n", result);
+ LOG_ERROR("SX126X scanChannel %s%d\n", radioLibErr, result);
assert(result != RADIOLIB_ERR_WRONG_MODEM);
return false;
@@ -298,37 +319,15 @@ template bool SX126xInterface::isActivelyReceiving()
{
// The IRQ status will be cleared when we start our read operation. Check if we've started a header, but haven't yet
// received and handled the interrupt for reading the packet/handling errors.
-
- uint16_t irq = lora.getIrqFlags();
- bool detected = (irq & (RADIOLIB_SX126X_IRQ_HEADER_VALID | RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED));
- // Handle false detections
- if (detected) {
- uint32_t now = millis();
- if (!activeReceiveStart) {
- activeReceiveStart = now;
- } else if ((now - activeReceiveStart > 2 * preambleTimeMsec) && !(irq & RADIOLIB_SX126X_IRQ_HEADER_VALID)) {
- // The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag
- activeReceiveStart = 0;
- LOG_DEBUG("Ignore false preamble detection.\n");
- return false;
- } else if (now - activeReceiveStart > maxPacketTimeMsec) {
- // We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag
- activeReceiveStart = 0;
- LOG_DEBUG("Ignore false header detection.\n");
- return false;
- }
- }
-
- // if (detected) LOG_DEBUG("rx detected\n");
- return detected;
+ return receiveDetected(lora.getIrqFlags(), RADIOLIB_SX126X_IRQ_HEADER_VALID, RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED);
}
template bool SX126xInterface::sleep()
{
// Not keeping config is busted - next time nrf52 board boots lora sending fails tcxo related? - see datasheet
// \todo Display actual typename of the adapter, not just `SX126x`
- LOG_DEBUG("SX126x entering sleep mode (FIXME, don't keep config)\n");
- setStandby(); // Stop any pending operations
+ LOG_DEBUG("SX126x entering sleep mode\n"); // (FIXME, don't keep config)
+ setStandby(); // Stop any pending operations
// turn off TCXO if it was powered
// FIXME - this isn't correct
@@ -344,3 +343,4 @@ template bool SX126xInterface::sleep()
return true;
}
+#endif
\ No newline at end of file
diff --git a/src/mesh/SX126xInterface.h b/src/mesh/SX126xInterface.h
index b392cd3e4..45b39a68a 100644
--- a/src/mesh/SX126xInterface.h
+++ b/src/mesh/SX126xInterface.h
@@ -1,4 +1,5 @@
#pragma once
+#if RADIOLIB_EXCLUDE_SX126X != 1
#include "RadioLibInterface.h"
@@ -67,7 +68,5 @@ template class SX126xInterface : public RadioLibInterface
virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) override;
virtual void setStandby() override;
-
- private:
- uint32_t activeReceiveStart = 0;
};
+#endif
\ No newline at end of file
diff --git a/src/mesh/SX1280Interface.cpp b/src/mesh/SX1280Interface.cpp
index 3287f141f..9e0d42122 100644
--- a/src/mesh/SX1280Interface.cpp
+++ b/src/mesh/SX1280Interface.cpp
@@ -1,3 +1,4 @@
+#if RADIOLIB_EXCLUDE_SX128X != 1
#include "SX1280Interface.h"
#include "configuration.h"
#include "error.h"
@@ -7,3 +8,4 @@ SX1280Interface::SX1280Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, R
: SX128xInterface(hal, cs, irq, rst, busy)
{
}
+#endif
\ No newline at end of file
diff --git a/src/mesh/SX1280Interface.h b/src/mesh/SX1280Interface.h
index 8f2c4ec2e..534dd8084 100644
--- a/src/mesh/SX1280Interface.h
+++ b/src/mesh/SX1280Interface.h
@@ -1,5 +1,5 @@
#pragma once
-
+#if RADIOLIB_EXCLUDE_SX128X != 1
#include "SX128xInterface.h"
/**
@@ -12,3 +12,4 @@ class SX1280Interface : public SX128xInterface
SX1280Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE busy);
};
+#endif
diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp
index 9ff9ac2d7..d379f26e6 100644
--- a/src/mesh/SX128xInterface.cpp
+++ b/src/mesh/SX128xInterface.cpp
@@ -1,4 +1,6 @@
+#if RADIOLIB_EXCLUDE_SX128X != 1
#include "SX128xInterface.h"
+#include "Throttle.h"
#include "configuration.h"
#include "error.h"
#include "mesh/NodeDB.h"
@@ -127,12 +129,12 @@ template bool SX128xInterface::reconfigure()
err = lora.setSyncWord(syncWord);
if (err != RADIOLIB_ERR_NONE)
- LOG_ERROR("Radiolib error %d when attempting SX128X setSyncWord!\n", err);
+ LOG_ERROR("SX128X setSyncWord %s%d\n", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
err = lora.setPreambleLength(preambleLength);
if (err != RADIOLIB_ERR_NONE)
- LOG_ERROR("Radiolib error %d when attempting SX128X setPreambleLength!\n", err);
+ LOG_ERROR("SX128X setPreambleLength %s%d\n", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
err = lora.setFrequency(getFreq());
@@ -144,7 +146,7 @@ template bool SX128xInterface::reconfigure()
err = lora.setOutputPower(power);
if (err != RADIOLIB_ERR_NONE)
- LOG_ERROR("Radiolib error %d when attempting SX128X setOutputPower!\n", err);
+ LOG_ERROR("SX128X setOutputPower %s%d\n", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
startReceive(); // restart receiving
@@ -169,7 +171,7 @@ template void SX128xInterface::setStandby()
int err = lora.standby();
if (err != RADIOLIB_ERR_NONE)
- LOG_ERROR("SX128x standby failed with error %d\n", err);
+ LOG_ERROR("SX128x standby %s%d\n", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
#if ARCH_PORTDUINO
if (settingsMap[rxen] != RADIOLIB_NC) {
@@ -259,7 +261,7 @@ template void SX128xInterface::startReceive()
int err = lora.startReceive(RADIOLIB_SX128X_RX_TIMEOUT_INF, RADIOLIB_IRQ_RX_DEFAULT_FLAGS | RADIOLIB_IRQ_PREAMBLE_DETECTED);
if (err != RADIOLIB_ERR_NONE)
- LOG_ERROR("Radiolib error %d when attempting SX128X startReceive!\n", err);
+ LOG_ERROR("SX128X startReceive %s%d\n", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
RadioLibInterface::startReceive();
@@ -280,7 +282,7 @@ template bool SX128xInterface::isChannelActive()
if (result == RADIOLIB_LORA_DETECTED)
return true;
if (result != RADIOLIB_CHANNEL_FREE)
- LOG_ERROR("Radiolib error %d when attempting SX128X scanChannel!\n", result);
+ LOG_ERROR("SX128X scanChannel %s%d\n", radioLibErr, result);
assert(result != RADIOLIB_ERR_WRONG_MODEM);
return false;
@@ -289,36 +291,15 @@ template bool SX128xInterface::isChannelActive()
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
template bool SX128xInterface::isActivelyReceiving()
{
- uint16_t irq = lora.getIrqStatus();
- bool detected = (irq & (RADIOLIB_SX128X_IRQ_HEADER_VALID | RADIOLIB_SX128X_IRQ_PREAMBLE_DETECTED));
-
- // Handle false detections
- if (detected) {
- uint32_t now = millis();
- if (!activeReceiveStart) {
- activeReceiveStart = now;
- } else if ((now - activeReceiveStart > 2 * preambleTimeMsec) && !(irq & RADIOLIB_SX128X_IRQ_HEADER_VALID)) {
- // The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag
- activeReceiveStart = 0;
- LOG_DEBUG("Ignore false preamble detection.\n");
- return false;
- } else if (now - activeReceiveStart > maxPacketTimeMsec) {
- // We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag
- activeReceiveStart = 0;
- LOG_DEBUG("Ignore false header detection.\n");
- return false;
- }
- }
-
- return detected;
+ return receiveDetected(lora.getIrqStatus(), RADIOLIB_SX128X_IRQ_HEADER_VALID, RADIOLIB_SX128X_IRQ_PREAMBLE_DETECTED);
}
template bool SX128xInterface::sleep()
{
// Not keeping config is busted - next time nrf52 board boots lora sending fails tcxo related? - see datasheet
// \todo Display actual typename of the adapter, not just `SX128x`
- LOG_DEBUG("SX128x entering sleep mode (FIXME, don't keep config)\n");
- setStandby(); // Stop any pending operations
+ LOG_DEBUG("SX128x entering sleep mode\n"); // (FIXME, don't keep config)
+ setStandby(); // Stop any pending operations
// turn off TCXO if it was powered
// FIXME - this isn't correct
@@ -333,4 +314,5 @@ template bool SX128xInterface::sleep()
#endif
return true;
-}
\ No newline at end of file
+}
+#endif
\ No newline at end of file
diff --git a/src/mesh/SX128xInterface.h b/src/mesh/SX128xInterface.h
index f7fd35b25..bba31dab4 100644
--- a/src/mesh/SX128xInterface.h
+++ b/src/mesh/SX128xInterface.h
@@ -67,7 +67,4 @@ template class SX128xInterface : public RadioLibInterface
virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) override;
virtual void setStandby() override;
-
- private:
- uint32_t activeReceiveStart = 0;
};
diff --git a/src/mesh/StreamAPI.cpp b/src/mesh/StreamAPI.cpp
index e10638c03..a230de099 100644
--- a/src/mesh/StreamAPI.cpp
+++ b/src/mesh/StreamAPI.cpp
@@ -1,6 +1,6 @@
#include "StreamAPI.h"
#include "PowerFSM.h"
-#include "configuration.h"
+#include "Throttle.h"
#include "gps/RTC.h"
#define START1 0x94
@@ -20,10 +20,9 @@ int32_t StreamAPI::runOncePart()
*/
int32_t StreamAPI::readStream()
{
- uint32_t now = millis();
if (!stream->available()) {
// Nothing available this time, if the computer has talked to us recently, poll often, otherwise let CPU sleep a long time
- bool recentRx = (now - lastRxMsec) < 2000;
+ bool recentRx = Throttle::isWithinTimespanMs(lastRxMsec, 2000);
return recentRx ? 5 : 250;
} else {
while (stream->available()) { // Currently we never want to block
@@ -71,7 +70,7 @@ int32_t StreamAPI::readStream()
}
// we had bytes available this time, so assume we might have them next time also
- lastRxMsec = now;
+ lastRxMsec = millis();
return 0;
}
}
diff --git a/src/mesh/Throttle.cpp b/src/mesh/Throttle.cpp
index d8f23f9dc..f278cc843 100644
--- a/src/mesh/Throttle.cpp
+++ b/src/mesh/Throttle.cpp
@@ -24,4 +24,12 @@ bool Throttle::execute(uint32_t *lastExecutionMs, uint32_t minumumIntervalMs, vo
onDefer();
}
return false;
+}
+
+/// @brief Check if the last execution time is within the interval
+/// @param lastExecutionMs The last execution time in milliseconds
+/// @param timeSpanMs The interval in milliseconds of the timespan
+bool Throttle::isWithinTimespanMs(uint32_t lastExecutionMs, uint32_t timeSpanMs)
+{
+ return (millis() - lastExecutionMs) < timeSpanMs;
}
\ No newline at end of file
diff --git a/src/mesh/Throttle.h b/src/mesh/Throttle.h
index 8115595a4..8b4bb5d30 100644
--- a/src/mesh/Throttle.h
+++ b/src/mesh/Throttle.h
@@ -6,4 +6,5 @@ class Throttle
{
public:
static bool execute(uint32_t *lastExecutionMs, uint32_t minumumIntervalMs, void (*func)(void), void (*onDefer)(void) = NULL);
+ static bool isWithinTimespanMs(uint32_t lastExecutionMs, uint32_t intervalMs);
};
\ No newline at end of file
diff --git a/src/mesh/TypeConversions.cpp b/src/mesh/TypeConversions.cpp
index 513728ca5..550f87021 100644
--- a/src/mesh/TypeConversions.cpp
+++ b/src/mesh/TypeConversions.cpp
@@ -11,9 +11,13 @@ meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfo
info.last_heard = lite->last_heard;
info.channel = lite->channel;
info.via_mqtt = lite->via_mqtt;
- info.hops_away = lite->hops_away;
info.is_favorite = lite->is_favorite;
+ if (lite->has_hops_away) {
+ info.has_hops_away = true;
+ info.hops_away = lite->hops_away;
+ }
+
if (lite->has_position) {
info.has_position = true;
if (lite->position.latitude_i != 0)
@@ -78,7 +82,7 @@ meshtastic_UserLite TypeConversions::ConvertToUserLite(meshtastic_User user)
lite.hw_model = user.hw_model;
lite.role = user.role;
lite.is_licensed = user.is_licensed;
- memccpy(lite.macaddr, user.macaddr, sizeof(user.macaddr), sizeof(lite.macaddr));
+ memcpy(lite.macaddr, user.macaddr, sizeof(lite.macaddr));
memcpy(lite.public_key.bytes, user.public_key.bytes, sizeof(lite.public_key.bytes));
lite.public_key.size = user.public_key.size;
return lite;
@@ -94,7 +98,7 @@ meshtastic_User TypeConversions::ConvertToUser(uint32_t nodeNum, meshtastic_User
user.hw_model = lite.hw_model;
user.role = lite.role;
user.is_licensed = lite.is_licensed;
- memccpy(user.macaddr, lite.macaddr, sizeof(lite.macaddr), sizeof(user.macaddr));
+ memcpy(user.macaddr, lite.macaddr, sizeof(user.macaddr));
memcpy(user.public_key.bytes, lite.public_key.bytes, sizeof(user.public_key.bytes));
user.public_key.size = lite.public_key.size;
diff --git a/src/mesh/TypedQueue.h b/src/mesh/TypedQueue.h
index c96edae8e..f7d016f10 100644
--- a/src/mesh/TypedQueue.h
+++ b/src/mesh/TypedQueue.h
@@ -14,7 +14,7 @@
*/
template class TypedQueue
{
- static_assert(std::is_pod::value, "T must be pod");
+ static_assert(std::is_standard_layout::value, "T must be standard layout");
QueueHandle_t h;
concurrency::OSThread *reader = NULL;
diff --git a/src/mesh/aes-ccm.cpp b/src/mesh/aes-ccm.cpp
index cd18ae6c5..b9af14fdb 100644
--- a/src/mesh/aes-ccm.cpp
+++ b/src/mesh/aes-ccm.cpp
@@ -95,7 +95,7 @@ static void aes_ccm_encr(size_t L, const uint8_t *in, size_t len, uint8_t *out,
*out++ ^= *in++;
}
}
-static void aes_ccm_encr_auth(size_t M, uint8_t *x, uint8_t *a, uint8_t *auth)
+static void aes_ccm_encr_auth(size_t M, const uint8_t *x, uint8_t *a, uint8_t *auth)
{
size_t i;
uint8_t tmp[AES_BLOCK_SIZE];
diff --git a/src/mesh/api/ServerAPI.cpp b/src/mesh/api/ServerAPI.cpp
index 140567ad2..42217428d 100644
--- a/src/mesh/api/ServerAPI.cpp
+++ b/src/mesh/api/ServerAPI.cpp
@@ -5,7 +5,7 @@
template
ServerAPI::ServerAPI(T &_client) : StreamAPI(&client), concurrency::OSThread("ServerAPI"), client(_client)
{
- LOG_INFO("Incoming wifi connection\n");
+ LOG_INFO("Incoming API connection\n");
}
template ServerAPI::~ServerAPI()
@@ -45,10 +45,28 @@ template void APIServerPort::init()
template int32_t APIServerPort::runOnce()
{
+#ifdef ARCH_ESP32
+#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
+ auto client = U::accept();
+#else
auto client = U::available();
+#endif
+#else
+ auto client = U::available();
+#endif
if (client) {
// Close any previous connection (see FIXME in header file)
if (openAPI) {
+#if RAK_4631
+ // RAK13800 Ethernet requests periodically take more time
+ // This backoff addresses most cases keeping max wait < 1s
+ // Reconnections are delayed by full wait time
+ if (waitTime < 400) {
+ waitTime *= 2;
+ LOG_INFO("Previous TCP connection still open, trying again in %dms\n", waitTime);
+ return waitTime;
+ }
+#endif
LOG_INFO("Force closing previous TCP connection\n");
delete openAPI;
}
@@ -56,5 +74,8 @@ template int32_t APIServerPort::runOnce()
openAPI = new T(client);
}
+#if RAK_4631
+ waitTime = 100;
+#endif
return 100; // only check occasionally for incoming connections
}
diff --git a/src/mesh/api/ServerAPI.h b/src/mesh/api/ServerAPI.h
index dd2a767c9..5b84fddd7 100644
--- a/src/mesh/api/ServerAPI.h
+++ b/src/mesh/api/ServerAPI.h
@@ -31,7 +31,7 @@ template class ServerAPI : public StreamAPI, private concurrency::OSTh
};
/**
- * Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed
+ * Listens for incoming connections and does accepts and creates instances of ServerAPI as needed
*/
template class APIServerPort : public U, private concurrency::OSThread
{
@@ -41,6 +41,10 @@ template class APIServerPort : public U, private concurrency:
* delegate to the worker. Once coroutines are implemented we can relax this restriction.
*/
T *openAPI = NULL;
+#if RAK_4631
+ // Track wait time for RAK13800 Ethernet requests
+ int32_t waitTime = 100;
+#endif
public:
explicit APIServerPort(int port);
diff --git a/src/mesh/api/ethServerAPI.h b/src/mesh/api/ethServerAPI.h
index 59673a684..6f214c75a 100644
--- a/src/mesh/api/ethServerAPI.h
+++ b/src/mesh/api/ethServerAPI.h
@@ -14,7 +14,7 @@ class ethServerAPI : public ServerAPI
};
/**
- * Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed
+ * Listens for incoming connections and does accepts and creates instances of EthernetServerAPI as needed
*/
class ethServerPort : public APIServerPort
{
diff --git a/src/mesh/compression/unishox2.cpp b/src/mesh/compression/unishox2.cpp
index 4e239d489..9fc012a76 100644
--- a/src/mesh/compression/unishox2.cpp
+++ b/src/mesh/compression/unishox2.cpp
@@ -339,7 +339,7 @@ int32_t readUTF8(const char *in, int len, int l, int *utf8len)
/// This is also used for Unicode strings \n
/// This is a crude implementation that is not optimized. Assuming only short strings \n
/// are encoded, this is not much of an issue.
-int matchOccurance(const char *in, int len, int l, char *out, int olen, int *ol, uint8_t *state, const uint8_t usx_hcodes[],
+int matchOccurance(const char *in, int len, int l, char *out, int olen, int *ol, const uint8_t *state, const uint8_t usx_hcodes[],
const uint8_t usx_hcode_lens[])
{
int j, k;
@@ -383,7 +383,7 @@ int matchOccurance(const char *in, int len, int l, char *out, int olen, int *ol,
/// This is also used for Unicode strings \n
/// This is a crude implementation that is not optimized. Assuming only short strings \n
/// are encoded, this is not much of an issue.
-int matchLine(const char *in, int len, int l, char *out, int olen, int *ol, struct us_lnk_lst *prev_lines, uint8_t *state,
+int matchLine(const char *in, int len, int l, char *out, int olen, int *ol, struct us_lnk_lst *prev_lines, const uint8_t *state,
const uint8_t usx_hcodes[], const uint8_t usx_hcode_lens[])
{
int last_ol = *ol;
diff --git a/src/mesh/eth/ethClient.cpp b/src/mesh/eth/ethClient.cpp
index 9f3bb8ab7..1c97f3bed 100644
--- a/src/mesh/eth/ethClient.cpp
+++ b/src/mesh/eth/ethClient.cpp
@@ -38,7 +38,7 @@ static int32_t reconnectETH()
Ethernet.maintain();
if (!ethStartupComplete) {
// Start web server
- LOG_INFO("... Starting network services\n");
+ LOG_INFO("Starting Ethernet network services\n");
#ifndef DISABLE_NTP
LOG_INFO("Starting NTP time client\n");
@@ -131,7 +131,8 @@ bool initEthernet()
status = Ethernet.begin(mac);
} else if (config.network.address_mode == meshtastic_Config_NetworkConfig_AddressMode_STATIC) {
LOG_INFO("starting Ethernet Static\n");
- Ethernet.begin(mac, config.network.ipv4_config.ip, config.network.ipv4_config.dns, config.network.ipv4_config.subnet);
+ Ethernet.begin(mac, config.network.ipv4_config.ip, config.network.ipv4_config.dns, config.network.ipv4_config.gateway,
+ config.network.ipv4_config.subnet);
status = 1;
} else {
LOG_INFO("Ethernet Disabled\n");
@@ -186,4 +187,4 @@ bool isEthernetAvailable()
}
}
-#endif
\ No newline at end of file
+#endif
diff --git a/src/mesh/generated/meshtastic/admin.pb.cpp b/src/mesh/generated/meshtastic/admin.pb.cpp
index 339960302..8b3fd3d1b 100644
--- a/src/mesh/generated/meshtastic/admin.pb.cpp
+++ b/src/mesh/generated/meshtastic/admin.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/admin.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
@@ -18,3 +18,5 @@ PB_BIND(meshtastic_NodeRemoteHardwarePinsResponse, meshtastic_NodeRemoteHardware
+
+
diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h
index c1ff7ebd4..bf81269b4 100644
--- a/src/mesh/generated/meshtastic/admin.pb.h
+++ b/src/mesh/generated/meshtastic/admin.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_ADMIN_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_ADMIN_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/apponly.pb.cpp b/src/mesh/generated/meshtastic/apponly.pb.cpp
index 44b0ea3cc..64d43b7ee 100644
--- a/src/mesh/generated/meshtastic/apponly.pb.cpp
+++ b/src/mesh/generated/meshtastic/apponly.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/apponly.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/apponly.pb.h b/src/mesh/generated/meshtastic/apponly.pb.h
index f5bacea52..dc08d9ff3 100644
--- a/src/mesh/generated/meshtastic/apponly.pb.h
+++ b/src/mesh/generated/meshtastic/apponly.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_APPONLY_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_APPONLY_PB_H_INCLUDED
@@ -55,7 +55,7 @@ extern const pb_msgdesc_t meshtastic_ChannelSet_msg;
/* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_APPONLY_PB_H_MAX_SIZE meshtastic_ChannelSet_size
-#define meshtastic_ChannelSet_size 676
+#define meshtastic_ChannelSet_size 679
#ifdef __cplusplus
} /* extern "C" */
diff --git a/src/mesh/generated/meshtastic/atak.pb.cpp b/src/mesh/generated/meshtastic/atak.pb.cpp
index 491336bcf..6dbc69fb4 100644
--- a/src/mesh/generated/meshtastic/atak.pb.cpp
+++ b/src/mesh/generated/meshtastic/atak.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/atak.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
@@ -27,3 +27,5 @@ PB_BIND(meshtastic_PLI, meshtastic_PLI, AUTO)
+
+
diff --git a/src/mesh/generated/meshtastic/atak.pb.h b/src/mesh/generated/meshtastic/atak.pb.h
index 5fd18f963..15a86788b 100644
--- a/src/mesh/generated/meshtastic/atak.pb.h
+++ b/src/mesh/generated/meshtastic/atak.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_ATAK_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_ATAK_PB_H_INCLUDED
@@ -120,6 +120,7 @@ typedef struct _meshtastic_PLI {
uint16_t course;
} meshtastic_PLI;
+typedef PB_BYTES_ARRAY_T(220) meshtastic_TAKPacket_detail_t;
/* Packets for the official ATAK Plugin */
typedef struct _meshtastic_TAKPacket {
/* Are the payloads strings compressed for LoRA transport? */
@@ -139,6 +140,9 @@ typedef struct _meshtastic_TAKPacket {
meshtastic_PLI pli;
/* ATAK GeoChat message */
meshtastic_GeoChat chat;
+ /* Generic CoT detail XML
+ May be compressed / truncated by the sender (EUD) */
+ meshtastic_TAKPacket_detail_t detail;
} payload_variant;
} meshtastic_TAKPacket;
@@ -199,6 +203,7 @@ extern "C" {
#define meshtastic_TAKPacket_status_tag 4
#define meshtastic_TAKPacket_pli_tag 5
#define meshtastic_TAKPacket_chat_tag 6
+#define meshtastic_TAKPacket_detail_tag 7
/* Struct field encoding specification for nanopb */
#define meshtastic_TAKPacket_FIELDLIST(X, a) \
@@ -207,7 +212,8 @@ X(a, STATIC, OPTIONAL, MESSAGE, contact, 2) \
X(a, STATIC, OPTIONAL, MESSAGE, group, 3) \
X(a, STATIC, OPTIONAL, MESSAGE, status, 4) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,pli,payload_variant.pli), 5) \
-X(a, STATIC, ONEOF, MESSAGE, (payload_variant,chat,payload_variant.chat), 6)
+X(a, STATIC, ONEOF, MESSAGE, (payload_variant,chat,payload_variant.chat), 6) \
+X(a, STATIC, ONEOF, BYTES, (payload_variant,detail,payload_variant.detail), 7)
#define meshtastic_TAKPacket_CALLBACK NULL
#define meshtastic_TAKPacket_DEFAULT NULL
#define meshtastic_TAKPacket_contact_MSGTYPE meshtastic_Contact
diff --git a/src/mesh/generated/meshtastic/cannedmessages.pb.cpp b/src/mesh/generated/meshtastic/cannedmessages.pb.cpp
index 71e659be2..9f51e9634 100644
--- a/src/mesh/generated/meshtastic/cannedmessages.pb.cpp
+++ b/src/mesh/generated/meshtastic/cannedmessages.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/cannedmessages.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/cannedmessages.pb.h b/src/mesh/generated/meshtastic/cannedmessages.pb.h
index c3f9a8b9b..06d14b98f 100644
--- a/src/mesh/generated/meshtastic/cannedmessages.pb.h
+++ b/src/mesh/generated/meshtastic/cannedmessages.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_CANNEDMESSAGES_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_CANNEDMESSAGES_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/channel.pb.cpp b/src/mesh/generated/meshtastic/channel.pb.cpp
index fe76d8140..52f923b13 100644
--- a/src/mesh/generated/meshtastic/channel.pb.cpp
+++ b/src/mesh/generated/meshtastic/channel.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/channel.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
@@ -17,3 +17,4 @@ PB_BIND(meshtastic_Channel, meshtastic_Channel, AUTO)
+
diff --git a/src/mesh/generated/meshtastic/channel.pb.h b/src/mesh/generated/meshtastic/channel.pb.h
index d9c7d4ffa..3d617ae39 100644
--- a/src/mesh/generated/meshtastic/channel.pb.h
+++ b/src/mesh/generated/meshtastic/channel.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_CHANNEL_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_CHANNEL_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/clientonly.pb.cpp b/src/mesh/generated/meshtastic/clientonly.pb.cpp
index 44c6f95ce..d99af8cf5 100644
--- a/src/mesh/generated/meshtastic/clientonly.pb.cpp
+++ b/src/mesh/generated/meshtastic/clientonly.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/clientonly.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/clientonly.pb.h b/src/mesh/generated/meshtastic/clientonly.pb.h
index dc323292a..bf32d7875 100644
--- a/src/mesh/generated/meshtastic/clientonly.pb.h
+++ b/src/mesh/generated/meshtastic/clientonly.pb.h
@@ -1,10 +1,11 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_CLIENTONLY_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_CLIENTONLY_PB_H_INCLUDED
#include
#include "meshtastic/localonly.pb.h"
+#include "meshtastic/mesh.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
@@ -28,6 +29,15 @@ typedef struct _meshtastic_DeviceProfile {
/* The ModuleConfig of the node */
bool has_module_config;
meshtastic_LocalModuleConfig module_config;
+ /* Fixed position data */
+ bool has_fixed_position;
+ meshtastic_Position fixed_position;
+ /* Ringtone for ExternalNotification */
+ bool has_ringtone;
+ char ringtone[231];
+ /* Predefined messages for CannedMessage */
+ bool has_canned_messages;
+ char canned_messages[201];
} meshtastic_DeviceProfile;
@@ -36,8 +46,8 @@ extern "C" {
#endif
/* Initializer values for message structs */
-#define meshtastic_DeviceProfile_init_default {false, "", false, "", {{NULL}, NULL}, false, meshtastic_LocalConfig_init_default, false, meshtastic_LocalModuleConfig_init_default}
-#define meshtastic_DeviceProfile_init_zero {false, "", false, "", {{NULL}, NULL}, false, meshtastic_LocalConfig_init_zero, false, meshtastic_LocalModuleConfig_init_zero}
+#define meshtastic_DeviceProfile_init_default {false, "", false, "", {{NULL}, NULL}, false, meshtastic_LocalConfig_init_default, false, meshtastic_LocalModuleConfig_init_default, false, meshtastic_Position_init_default, false, "", false, ""}
+#define meshtastic_DeviceProfile_init_zero {false, "", false, "", {{NULL}, NULL}, false, meshtastic_LocalConfig_init_zero, false, meshtastic_LocalModuleConfig_init_zero, false, meshtastic_Position_init_zero, false, "", false, ""}
/* Field tags (for use in manual encoding/decoding) */
#define meshtastic_DeviceProfile_long_name_tag 1
@@ -45,6 +55,9 @@ extern "C" {
#define meshtastic_DeviceProfile_channel_url_tag 3
#define meshtastic_DeviceProfile_config_tag 4
#define meshtastic_DeviceProfile_module_config_tag 5
+#define meshtastic_DeviceProfile_fixed_position_tag 6
+#define meshtastic_DeviceProfile_ringtone_tag 7
+#define meshtastic_DeviceProfile_canned_messages_tag 8
/* Struct field encoding specification for nanopb */
#define meshtastic_DeviceProfile_FIELDLIST(X, a) \
@@ -52,11 +65,15 @@ X(a, STATIC, OPTIONAL, STRING, long_name, 1) \
X(a, STATIC, OPTIONAL, STRING, short_name, 2) \
X(a, CALLBACK, OPTIONAL, STRING, channel_url, 3) \
X(a, STATIC, OPTIONAL, MESSAGE, config, 4) \
-X(a, STATIC, OPTIONAL, MESSAGE, module_config, 5)
+X(a, STATIC, OPTIONAL, MESSAGE, module_config, 5) \
+X(a, STATIC, OPTIONAL, MESSAGE, fixed_position, 6) \
+X(a, STATIC, OPTIONAL, STRING, ringtone, 7) \
+X(a, STATIC, OPTIONAL, STRING, canned_messages, 8)
#define meshtastic_DeviceProfile_CALLBACK pb_default_field_callback
#define meshtastic_DeviceProfile_DEFAULT NULL
#define meshtastic_DeviceProfile_config_MSGTYPE meshtastic_LocalConfig
#define meshtastic_DeviceProfile_module_config_MSGTYPE meshtastic_LocalModuleConfig
+#define meshtastic_DeviceProfile_fixed_position_MSGTYPE meshtastic_Position
extern const pb_msgdesc_t meshtastic_DeviceProfile_msg;
diff --git a/src/mesh/generated/meshtastic/config.pb.cpp b/src/mesh/generated/meshtastic/config.pb.cpp
index 92c3313bd..23f4d542b 100644
--- a/src/mesh/generated/meshtastic/config.pb.cpp
+++ b/src/mesh/generated/meshtastic/config.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/config.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
@@ -46,6 +46,19 @@ PB_BIND(meshtastic_Config_SessionkeyConfig, meshtastic_Config_SessionkeyConfig,
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h
index eb03ddc58..988f852ff 100644
--- a/src/mesh/generated/meshtastic/config.pb.h
+++ b/src/mesh/generated/meshtastic/config.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_CONFIG_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_CONFIG_PB_H_INCLUDED
@@ -238,7 +238,13 @@ typedef enum _meshtastic_Config_LoRaConfig_RegionCode {
/* Malaysia 919mhz */
meshtastic_Config_LoRaConfig_RegionCode_MY_919 = 17,
/* Singapore 923mhz */
- meshtastic_Config_LoRaConfig_RegionCode_SG_923 = 18
+ meshtastic_Config_LoRaConfig_RegionCode_SG_923 = 18,
+ /* Philippines 433mhz */
+ meshtastic_Config_LoRaConfig_RegionCode_PH_433 = 19,
+ /* Philippines 868mhz */
+ meshtastic_Config_LoRaConfig_RegionCode_PH_868 = 20,
+ /* Philippines 915mhz */
+ meshtastic_Config_LoRaConfig_RegionCode_PH_915 = 21
} meshtastic_Config_LoRaConfig_RegionCode;
/* Standard predefined channel settings
@@ -510,6 +516,8 @@ typedef struct _meshtastic_Config_LoRaConfig {
uint32_t ignore_incoming[3];
/* If true, the device will not process any packets received via LoRa that passed via MQTT anywhere on the path towards it. */
bool ignore_mqtt;
+ /* Sets the ok_to_mqtt bit on outgoing packets */
+ bool config_ok_to_mqtt;
} meshtastic_Config_LoRaConfig;
typedef struct _meshtastic_Config_BluetoothConfig {
@@ -533,7 +541,7 @@ typedef struct _meshtastic_Config_SecurityConfig {
meshtastic_Config_SecurityConfig_private_key_t private_key;
/* The public key authorized to send admin messages to this node. */
pb_size_t admin_key_count;
- meshtastic_Config_SecurityConfig_admin_key_t admin_key[1];
+ meshtastic_Config_SecurityConfig_admin_key_t admin_key[3];
/* If true, device is considered to be "managed" by a mesh administrator via admin messages
Device is managed by a mesh administrator. */
bool is_managed;
@@ -613,8 +621,8 @@ extern "C" {
#define _meshtastic_Config_DisplayConfig_CompassOrientation_ARRAYSIZE ((meshtastic_Config_DisplayConfig_CompassOrientation)(meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270_INVERTED+1))
#define _meshtastic_Config_LoRaConfig_RegionCode_MIN meshtastic_Config_LoRaConfig_RegionCode_UNSET
-#define _meshtastic_Config_LoRaConfig_RegionCode_MAX meshtastic_Config_LoRaConfig_RegionCode_SG_923
-#define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_SG_923+1))
+#define _meshtastic_Config_LoRaConfig_RegionCode_MAX meshtastic_Config_LoRaConfig_RegionCode_PH_915
+#define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_PH_915+1))
#define _meshtastic_Config_LoRaConfig_ModemPreset_MIN meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST
#define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO
@@ -656,9 +664,9 @@ extern "C" {
#define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, ""}
#define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0}
#define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN}
-#define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0}
+#define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0}
#define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0}
-#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, 0, {{0, {0}}}, 0, 0, 0, 0}
+#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, 0, {{0, {0}}, {0, {0}}, {0, {0}}}, 0, 0, 0, 0}
#define meshtastic_Config_SessionkeyConfig_init_default {0}
#define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}}
#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0}
@@ -667,9 +675,9 @@ extern "C" {
#define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, ""}
#define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0}
#define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN}
-#define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0}
+#define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0}
#define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0}
-#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, 0, {{0, {0}}}, 0, 0, 0, 0}
+#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, 0, {{0, {0}}, {0, {0}}, {0, {0}}}, 0, 0, 0, 0}
#define meshtastic_Config_SessionkeyConfig_init_zero {0}
/* Field tags (for use in manual encoding/decoding) */
@@ -746,6 +754,7 @@ extern "C" {
#define meshtastic_Config_LoRaConfig_pa_fan_disabled_tag 15
#define meshtastic_Config_LoRaConfig_ignore_incoming_tag 103
#define meshtastic_Config_LoRaConfig_ignore_mqtt_tag 104
+#define meshtastic_Config_LoRaConfig_config_ok_to_mqtt_tag 105
#define meshtastic_Config_BluetoothConfig_enabled_tag 1
#define meshtastic_Config_BluetoothConfig_mode_tag 2
#define meshtastic_Config_BluetoothConfig_fixed_pin_tag 3
@@ -887,7 +896,8 @@ X(a, STATIC, SINGULAR, BOOL, sx126x_rx_boosted_gain, 13) \
X(a, STATIC, SINGULAR, FLOAT, override_frequency, 14) \
X(a, STATIC, SINGULAR, BOOL, pa_fan_disabled, 15) \
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) \
-X(a, STATIC, SINGULAR, BOOL, ignore_mqtt, 104)
+X(a, STATIC, SINGULAR, BOOL, ignore_mqtt, 104) \
+X(a, STATIC, SINGULAR, BOOL, config_ok_to_mqtt, 105)
#define meshtastic_Config_LoRaConfig_CALLBACK NULL
#define meshtastic_Config_LoRaConfig_DEFAULT NULL
@@ -944,12 +954,12 @@ extern const pb_msgdesc_t meshtastic_Config_SessionkeyConfig_msg;
#define meshtastic_Config_BluetoothConfig_size 10
#define meshtastic_Config_DeviceConfig_size 98
#define meshtastic_Config_DisplayConfig_size 30
-#define meshtastic_Config_LoRaConfig_size 82
+#define meshtastic_Config_LoRaConfig_size 85
#define meshtastic_Config_NetworkConfig_IpV4Config_size 20
#define meshtastic_Config_NetworkConfig_size 196
#define meshtastic_Config_PositionConfig_size 62
#define meshtastic_Config_PowerConfig_size 52
-#define meshtastic_Config_SecurityConfig_size 111
+#define meshtastic_Config_SecurityConfig_size 178
#define meshtastic_Config_SessionkeyConfig_size 0
#define meshtastic_Config_size 199
diff --git a/src/mesh/generated/meshtastic/connection_status.pb.cpp b/src/mesh/generated/meshtastic/connection_status.pb.cpp
index fc5a364dd..d1495bb83 100644
--- a/src/mesh/generated/meshtastic/connection_status.pb.cpp
+++ b/src/mesh/generated/meshtastic/connection_status.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/connection_status.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/connection_status.pb.h b/src/mesh/generated/meshtastic/connection_status.pb.h
index 1c618e4d4..c433e370b 100644
--- a/src/mesh/generated/meshtastic/connection_status.pb.h
+++ b/src/mesh/generated/meshtastic/connection_status.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_CONNECTION_STATUS_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_CONNECTION_STATUS_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.cpp b/src/mesh/generated/meshtastic/deviceonly.pb.cpp
index 2747ac9d9..135634762 100644
--- a/src/mesh/generated/meshtastic/deviceonly.pb.cpp
+++ b/src/mesh/generated/meshtastic/deviceonly.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/deviceonly.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
@@ -26,3 +26,4 @@ PB_BIND(meshtastic_OEMStore, meshtastic_OEMStore, 2)
+
diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h
index 692402210..2aa8fda8e 100644
--- a/src/mesh/generated/meshtastic/deviceonly.pb.h
+++ b/src/mesh/generated/meshtastic/deviceonly.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_INCLUDED
@@ -94,6 +94,7 @@ typedef struct _meshtastic_NodeInfoLite {
/* True if we witnessed the node over MQTT instead of LoRA transport */
bool via_mqtt;
/* Number of hops away from us this node is (0 if adjacent) */
+ bool has_hops_away;
uint8_t hops_away;
/* True if node is in our favorites list
Persists between NodeDB internal clean ups */
@@ -202,13 +203,13 @@ extern "C" {
/* Initializer values for message structs */
#define meshtastic_PositionLite_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN}
#define meshtastic_UserLite_init_default {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}}
-#define meshtastic_NodeInfoLite_init_default {0, false, meshtastic_UserLite_init_default, false, meshtastic_PositionLite_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, 0, 0}
+#define meshtastic_NodeInfoLite_init_default {0, false, meshtastic_UserLite_init_default, false, meshtastic_PositionLite_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, false, 0, 0}
#define meshtastic_DeviceState_init_default {false, meshtastic_MyNodeInfo_init_default, false, meshtastic_User_init_default, 0, {meshtastic_MeshPacket_init_default}, false, meshtastic_MeshPacket_init_default, 0, 0, 0, false, meshtastic_MeshPacket_init_default, 0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}, {0}}
#define meshtastic_ChannelFile_init_default {0, {meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default}, 0}
#define meshtastic_OEMStore_init_default {0, 0, {0, {0}}, _meshtastic_ScreenFonts_MIN, "", {0, {0}}, false, meshtastic_LocalConfig_init_default, false, meshtastic_LocalModuleConfig_init_default}
#define meshtastic_PositionLite_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN}
#define meshtastic_UserLite_init_zero {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}}
-#define meshtastic_NodeInfoLite_init_zero {0, false, meshtastic_UserLite_init_zero, false, meshtastic_PositionLite_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, 0, 0}
+#define meshtastic_NodeInfoLite_init_zero {0, false, meshtastic_UserLite_init_zero, false, meshtastic_PositionLite_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, false, 0, 0}
#define meshtastic_DeviceState_init_zero {false, meshtastic_MyNodeInfo_init_zero, false, meshtastic_User_init_zero, 0, {meshtastic_MeshPacket_init_zero}, false, meshtastic_MeshPacket_init_zero, 0, 0, 0, false, meshtastic_MeshPacket_init_zero, 0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}, {0}}
#define meshtastic_ChannelFile_init_zero {0, {meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero}, 0}
#define meshtastic_OEMStore_init_zero {0, 0, {0, {0}}, _meshtastic_ScreenFonts_MIN, "", {0, {0}}, false, meshtastic_LocalConfig_init_zero, false, meshtastic_LocalModuleConfig_init_zero}
@@ -287,7 +288,7 @@ X(a, STATIC, SINGULAR, FIXED32, last_heard, 5) \
X(a, STATIC, OPTIONAL, MESSAGE, device_metrics, 6) \
X(a, STATIC, SINGULAR, UINT32, channel, 7) \
X(a, STATIC, SINGULAR, BOOL, via_mqtt, 8) \
-X(a, STATIC, SINGULAR, UINT32, hops_away, 9) \
+X(a, STATIC, OPTIONAL, UINT32, hops_away, 9) \
X(a, STATIC, SINGULAR, BOOL, is_favorite, 10)
#define meshtastic_NodeInfoLite_CALLBACK NULL
#define meshtastic_NodeInfoLite_DEFAULT NULL
@@ -358,7 +359,7 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg;
#define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size
#define meshtastic_ChannelFile_size 718
#define meshtastic_NodeInfoLite_size 183
-#define meshtastic_OEMStore_size 3497
+#define meshtastic_OEMStore_size 3578
#define meshtastic_PositionLite_size 28
#define meshtastic_UserLite_size 96
diff --git a/src/mesh/generated/meshtastic/localonly.pb.cpp b/src/mesh/generated/meshtastic/localonly.pb.cpp
index 9bc98fb85..0a752a5a8 100644
--- a/src/mesh/generated/meshtastic/localonly.pb.cpp
+++ b/src/mesh/generated/meshtastic/localonly.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/localonly.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h
index 91a23dc4f..6409aef74 100644
--- a/src/mesh/generated/meshtastic/localonly.pb.h
+++ b/src/mesh/generated/meshtastic/localonly.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_INCLUDED
@@ -186,9 +186,9 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg;
#define meshtastic_LocalModuleConfig_fields &meshtastic_LocalModuleConfig_msg
/* Maximum encoded size of messages (where known) */
-#define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size
-#define meshtastic_LocalConfig_size 664
-#define meshtastic_LocalModuleConfig_size 687
+#define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalConfig_size
+#define meshtastic_LocalConfig_size 735
+#define meshtastic_LocalModuleConfig_size 697
#ifdef __cplusplus
} /* extern "C" */
diff --git a/src/mesh/generated/meshtastic/mesh.pb.cpp b/src/mesh/generated/meshtastic/mesh.pb.cpp
index 8c8b9ded7..a0c1e2e73 100644
--- a/src/mesh/generated/meshtastic/mesh.pb.cpp
+++ b/src/mesh/generated/meshtastic/mesh.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/mesh.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
@@ -90,4 +90,13 @@ PB_BIND(meshtastic_ChunkedPayloadResponse, meshtastic_ChunkedPayloadResponse, AU
+
+
+
+
+
+
+
+
+
diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h
index 9d7ff74a1..fb154e9d5 100644
--- a/src/mesh/generated/meshtastic/mesh.pb.h
+++ b/src/mesh/generated/meshtastic/mesh.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_MESH_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_MESH_PB_H_INCLUDED
@@ -71,6 +71,8 @@ typedef enum _meshtastic_HardwareModel {
meshtastic_HardwareModel_RAK2560 = 22,
/* Heltec HRU-3601: https://heltec.org/project/hru-3601/ */
meshtastic_HardwareModel_HELTEC_HRU_3601 = 23,
+ /* Heltec Wireless Bridge */
+ meshtastic_HardwareModel_HELTEC_WIRELESS_BRIDGE = 24,
/* B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station */
meshtastic_HardwareModel_STATION_G1 = 25,
/* RAK11310 (RP2040 + SX1262) */
@@ -107,7 +109,7 @@ typedef enum _meshtastic_HardwareModel {
meshtastic_HardwareModel_NRF52840_PCA10059 = 40,
/* Custom Disaster Radio esp32 v3 device https://github.com/sudomesh/disaster-radio/tree/master/hardware/board_esp32_v3 */
meshtastic_HardwareModel_DR_DEV = 41,
- /* M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/ */
+ /* M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/ */
meshtastic_HardwareModel_M5STACK = 42,
/* New Heltec LoRA32 with ESP32-S3 CPU */
meshtastic_HardwareModel_HELTEC_V3 = 43,
@@ -196,9 +198,19 @@ typedef enum _meshtastic_HardwareModel {
https://www.adafruit.com/product/938
^^^ short A0 to switch to I2C address 0x3C */
meshtastic_HardwareModel_RP2040_FEATHER_RFM95 = 76,
- /* M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/ */
+ /* M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/ */
meshtastic_HardwareModel_M5STACK_COREBASIC = 77,
meshtastic_HardwareModel_M5STACK_CORE2 = 78,
+ /* Pico2 with Waveshare Hat, same as Pico */
+ meshtastic_HardwareModel_RPI_PICO2 = 79,
+ /* M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/ */
+ meshtastic_HardwareModel_M5STACK_CORES3 = 80,
+ /* Seeed XIAO S3 DK */
+ meshtastic_HardwareModel_SEEED_XIAO_S3 = 81,
+ /* Nordic nRF52840+Semtech SX1262 LoRa BLE Combo Module. nRF52840+SX1262 MS24SF1 */
+ meshtastic_HardwareModel_MS24SF1 = 82,
+ /* Lilygo TLora-C6 with the new ESP32-C6 MCU */
+ meshtastic_HardwareModel_TLORA_C6 = 83,
/* ------------------------------------------------------------------------------------------------------------------------------------------
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
------------------------------------------------------------------------------------------------------------------------------------------ */
@@ -316,7 +328,11 @@ typedef enum _meshtastic_Routing_Error {
/* The client specified a PKI transport, but the node was unable to send the packet using PKI (and did not send the message at all) */
meshtastic_Routing_Error_PKI_FAILED = 34,
/* The receiving node does not have a Public Key to decode with */
- meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY = 35
+ meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY = 35,
+ /* Admin packet otherwise checks out, but uses a bogus or expired session key */
+ meshtastic_Routing_Error_ADMIN_BAD_SESSION_KEY = 36,
+ /* Admin packet sent using PKC, but not from a public key on the admin key list */
+ meshtastic_Routing_Error_ADMIN_PUBLIC_KEY_UNAUTHORIZED = 37
} meshtastic_Routing_Error;
/* The priority of this message for sending.
@@ -580,6 +596,9 @@ typedef struct _meshtastic_Data {
/* Defaults to false. If true, then what is in the payload should be treated as an emoji like giving
a message a heart or poop emoji. */
uint32_t emoji;
+ /* Bitfield for extra flags. First use is to indicate that user approves the packet being uploaded to MQTT. */
+ bool has_bitfield;
+ uint8_t bitfield;
} meshtastic_Data;
/* Waypoint message, used to share arbitrary locations across the mesh */
@@ -738,6 +757,7 @@ typedef struct _meshtastic_NodeInfo {
/* True if we witnessed the node over MQTT instead of LoRA transport */
bool via_mqtt;
/* Number of hops away from us this node is (0 if adjacent) */
+ bool has_hops_away;
uint8_t hops_away;
/* True if node is in our favorites list
Persists between NodeDB internal clean ups */
@@ -868,6 +888,8 @@ typedef struct _meshtastic_DeviceMetadata {
meshtastic_HardwareModel hw_model;
/* Has Remote Hardware enabled */
bool hasRemoteHardware;
+ /* Has PKC capabilities */
+ bool hasPKC;
} meshtastic_DeviceMetadata;
/* Packets from the radio to the phone will appear on the fromRadio characteristic.
@@ -1023,8 +1045,8 @@ extern "C" {
#define _meshtastic_Position_AltSource_ARRAYSIZE ((meshtastic_Position_AltSource)(meshtastic_Position_AltSource_ALT_BAROMETRIC+1))
#define _meshtastic_Routing_Error_MIN meshtastic_Routing_Error_NONE
-#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY
-#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY+1))
+#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_ADMIN_PUBLIC_KEY_UNAUTHORIZED
+#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_ADMIN_PUBLIC_KEY_UNAUTHORIZED+1))
#define _meshtastic_MeshPacket_Priority_MIN meshtastic_MeshPacket_Priority_UNSET
#define _meshtastic_MeshPacket_Priority_MAX meshtastic_MeshPacket_Priority_MAX
@@ -1082,11 +1104,11 @@ extern "C" {
#define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}}
#define meshtastic_RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define meshtastic_Routing_init_default {0, {meshtastic_RouteDiscovery_init_default}}
-#define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0}
+#define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0}
#define meshtastic_Waypoint_init_default {0, false, 0, false, 0, 0, 0, "", "", 0}
#define meshtastic_MqttClientProxyMessage_init_default {"", 0, {{0, {0}}}, 0}
#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0}
-#define meshtastic_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, 0, 0}
+#define meshtastic_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, false, 0, 0}
#define meshtastic_MyNodeInfo_init_default {0, 0, 0}
#define meshtastic_LogRecord_init_default {"", 0, "", _meshtastic_LogRecord_Level_MIN}
#define meshtastic_QueueStatus_init_default {0, 0, 0, 0}
@@ -1097,7 +1119,7 @@ extern "C" {
#define meshtastic_Compressed_init_default {_meshtastic_PortNum_MIN, {0, {0}}}
#define meshtastic_NeighborInfo_init_default {0, 0, 0, 0, {meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default}}
#define meshtastic_Neighbor_init_default {0, 0, 0, 0}
-#define meshtastic_DeviceMetadata_init_default {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0}
+#define meshtastic_DeviceMetadata_init_default {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0, 0}
#define meshtastic_Heartbeat_init_default {0}
#define meshtastic_NodeRemoteHardwarePin_init_default {0, false, meshtastic_RemoteHardwarePin_init_default}
#define meshtastic_ChunkedPayload_init_default {0, 0, 0, {0, {0}}}
@@ -1107,11 +1129,11 @@ extern "C" {
#define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}}
#define meshtastic_RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define meshtastic_Routing_init_zero {0, {meshtastic_RouteDiscovery_init_zero}}
-#define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0}
+#define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0}
#define meshtastic_Waypoint_init_zero {0, false, 0, false, 0, 0, 0, "", "", 0}
#define meshtastic_MqttClientProxyMessage_init_zero {"", 0, {{0, {0}}}, 0}
#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0}
-#define meshtastic_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, 0, 0}
+#define meshtastic_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, false, 0, 0}
#define meshtastic_MyNodeInfo_init_zero {0, 0, 0}
#define meshtastic_LogRecord_init_zero {"", 0, "", _meshtastic_LogRecord_Level_MIN}
#define meshtastic_QueueStatus_init_zero {0, 0, 0, 0}
@@ -1122,7 +1144,7 @@ extern "C" {
#define meshtastic_Compressed_init_zero {_meshtastic_PortNum_MIN, {0, {0}}}
#define meshtastic_NeighborInfo_init_zero {0, 0, 0, 0, {meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero}}
#define meshtastic_Neighbor_init_zero {0, 0, 0, 0}
-#define meshtastic_DeviceMetadata_init_zero {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0}
+#define meshtastic_DeviceMetadata_init_zero {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0, 0}
#define meshtastic_Heartbeat_init_zero {0}
#define meshtastic_NodeRemoteHardwarePin_init_zero {0, false, meshtastic_RemoteHardwarePin_init_zero}
#define meshtastic_ChunkedPayload_init_zero {0, 0, 0, {0, {0}}}
@@ -1176,6 +1198,7 @@ extern "C" {
#define meshtastic_Data_request_id_tag 6
#define meshtastic_Data_reply_id_tag 7
#define meshtastic_Data_emoji_tag 8
+#define meshtastic_Data_bitfield_tag 9
#define meshtastic_Waypoint_id_tag 1
#define meshtastic_Waypoint_latitude_i_tag 2
#define meshtastic_Waypoint_longitude_i_tag 3
@@ -1252,6 +1275,7 @@ extern "C" {
#define meshtastic_DeviceMetadata_position_flags_tag 8
#define meshtastic_DeviceMetadata_hw_model_tag 9
#define meshtastic_DeviceMetadata_hasRemoteHardware_tag 10
+#define meshtastic_DeviceMetadata_hasPKC_tag 11
#define meshtastic_FromRadio_id_tag 1
#define meshtastic_FromRadio_packet_tag 2
#define meshtastic_FromRadio_my_info_tag 3
@@ -1351,7 +1375,8 @@ X(a, STATIC, SINGULAR, FIXED32, dest, 4) \
X(a, STATIC, SINGULAR, FIXED32, source, 5) \
X(a, STATIC, SINGULAR, FIXED32, request_id, 6) \
X(a, STATIC, SINGULAR, FIXED32, reply_id, 7) \
-X(a, STATIC, SINGULAR, FIXED32, emoji, 8)
+X(a, STATIC, SINGULAR, FIXED32, emoji, 8) \
+X(a, STATIC, OPTIONAL, UINT32, bitfield, 9)
#define meshtastic_Data_CALLBACK NULL
#define meshtastic_Data_DEFAULT NULL
@@ -1406,7 +1431,7 @@ X(a, STATIC, SINGULAR, FIXED32, last_heard, 5) \
X(a, STATIC, OPTIONAL, MESSAGE, device_metrics, 6) \
X(a, STATIC, SINGULAR, UINT32, channel, 7) \
X(a, STATIC, SINGULAR, BOOL, via_mqtt, 8) \
-X(a, STATIC, SINGULAR, UINT32, hops_away, 9) \
+X(a, STATIC, OPTIONAL, UINT32, hops_away, 9) \
X(a, STATIC, SINGULAR, BOOL, is_favorite, 10)
#define meshtastic_NodeInfo_CALLBACK NULL
#define meshtastic_NodeInfo_DEFAULT NULL
@@ -1531,7 +1556,8 @@ X(a, STATIC, SINGULAR, BOOL, hasEthernet, 6) \
X(a, STATIC, SINGULAR, UENUM, role, 7) \
X(a, STATIC, SINGULAR, UINT32, position_flags, 8) \
X(a, STATIC, SINGULAR, UENUM, hw_model, 9) \
-X(a, STATIC, SINGULAR, BOOL, hasRemoteHardware, 10)
+X(a, STATIC, SINGULAR, BOOL, hasRemoteHardware, 10) \
+X(a, STATIC, SINGULAR, BOOL, hasPKC, 11)
#define meshtastic_DeviceMetadata_CALLBACK NULL
#define meshtastic_DeviceMetadata_DEFAULT NULL
@@ -1629,13 +1655,13 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
#define meshtastic_ChunkedPayload_size 245
#define meshtastic_ClientNotification_size 415
#define meshtastic_Compressed_size 243
-#define meshtastic_Data_size 270
-#define meshtastic_DeviceMetadata_size 46
+#define meshtastic_Data_size 273
+#define meshtastic_DeviceMetadata_size 48
#define meshtastic_FileInfo_size 236
#define meshtastic_FromRadio_size 510
#define meshtastic_Heartbeat_size 0
#define meshtastic_LogRecord_size 426
-#define meshtastic_MeshPacket_size 364
+#define meshtastic_MeshPacket_size 367
#define meshtastic_MqttClientProxyMessage_size 501
#define meshtastic_MyNodeInfo_size 18
#define meshtastic_NeighborInfo_size 258
diff --git a/src/mesh/generated/meshtastic/module_config.pb.cpp b/src/mesh/generated/meshtastic/module_config.pb.cpp
index 88a771d5b..c40041eab 100644
--- a/src/mesh/generated/meshtastic/module_config.pb.cpp
+++ b/src/mesh/generated/meshtastic/module_config.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/module_config.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
@@ -60,3 +60,10 @@ PB_BIND(meshtastic_RemoteHardwarePin, meshtastic_RemoteHardwarePin, AUTO)
+
+
+
+
+
+
+
diff --git a/src/mesh/generated/meshtastic/module_config.pb.h b/src/mesh/generated/meshtastic/module_config.pb.h
index 2e1985660..32d5ded23 100644
--- a/src/mesh/generated/meshtastic/module_config.pb.h
+++ b/src/mesh/generated/meshtastic/module_config.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_MODULE_CONFIG_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_MODULE_CONFIG_PB_H_INCLUDED
@@ -19,6 +19,23 @@ typedef enum _meshtastic_RemoteHardwarePinType {
meshtastic_RemoteHardwarePinType_DIGITAL_WRITE = 2
} meshtastic_RemoteHardwarePinType;
+typedef enum _meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType {
+ /* Event is triggered if pin is low */
+ meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_LOGIC_LOW = 0,
+ /* Event is triggered if pin is high */
+ meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_LOGIC_HIGH = 1,
+ /* Event is triggered when pin goes high to low */
+ meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_FALLING_EDGE = 2,
+ /* Event is triggered when pin goes low to high */
+ meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_RISING_EDGE = 3,
+ /* Event is triggered on every pin state change, low is considered to be
+ "active" */
+ meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_EITHER_EDGE_ACTIVE_LOW = 4,
+ /* Event is triggered on every pin state change, high is considered to be
+ "active" */
+ meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_EITHER_EDGE_ACTIVE_HIGH = 5
+} meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType;
+
/* Baudrate for codec2 voice */
typedef enum _meshtastic_ModuleConfig_AudioConfig_Audio_Baud {
meshtastic_ModuleConfig_AudioConfig_Audio_Baud_CODEC2_DEFAULT = 0,
@@ -144,11 +161,13 @@ typedef struct _meshtastic_ModuleConfig_NeighborInfoConfig {
typedef struct _meshtastic_ModuleConfig_DetectionSensorConfig {
/* Whether the Module is enabled */
bool enabled;
- /* Interval in seconds of how often we can send a message to the mesh when a state change is detected */
+ /* Interval in seconds of how often we can send a message to the mesh when a
+ trigger event is detected */
uint32_t minimum_broadcast_secs;
- /* Interval in seconds of how often we should send a message to the mesh with the current state regardless of changes
- When set to 0, only state changes will be broadcasted
- Works as a sort of status heartbeat for peace of mind */
+ /* Interval in seconds of how often we should send a message to the mesh
+ with the current state regardless of trigger events When set to 0, only
+ trigger events will be broadcasted Works as a sort of status heartbeat
+ for peace of mind */
uint32_t state_broadcast_secs;
/* Send ASCII bell with alert message
Useful for triggering ext. notification on bell */
@@ -159,9 +178,8 @@ typedef struct _meshtastic_ModuleConfig_DetectionSensorConfig {
char name[20];
/* GPIO pin to monitor for state changes */
uint8_t monitor_pin;
- /* Whether or not the GPIO pin state detection is triggered on HIGH (1)
- Otherwise LOW (0) */
- bool detection_triggered_high;
+ /* The type of trigger event to be used */
+ meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType detection_trigger_type;
/* Whether or not use INPUT_PULLUP mode for GPIO pin
Only applicable if the board uses pull-up resistors on the pin */
bool use_pullup;
@@ -309,15 +327,21 @@ typedef struct _meshtastic_ModuleConfig_TelemetryConfig {
/* Interval in seconds of how often we should try to send our
air quality metrics to the mesh */
uint32_t air_quality_interval;
- /* Interval in seconds of how often we should try to send our
- air quality metrics to the mesh */
+ /* Enable/disable Power metrics */
bool power_measurement_enabled;
/* Interval in seconds of how often we should try to send our
- air quality metrics to the mesh */
+ power metrics to the mesh */
uint32_t power_update_interval;
- /* Interval in seconds of how often we should try to send our
- air quality metrics to the mesh */
+ /* Enable/Disable the power measurement module on-device display */
bool power_screen_enabled;
+ /* Preferences for the (Health) Telemetry Module
+ Enable/Disable the telemetry measurement module measurement collection */
+ bool health_measurement_enabled;
+ /* Interval in seconds of how often we should try to send our
+ health metrics to the mesh */
+ uint32_t health_update_interval;
+ /* Enable/Disable the health telemetry module on-device display */
+ bool health_screen_enabled;
} meshtastic_ModuleConfig_TelemetryConfig;
/* TODO: REPLACE */
@@ -427,6 +451,10 @@ extern "C" {
#define _meshtastic_RemoteHardwarePinType_MAX meshtastic_RemoteHardwarePinType_DIGITAL_WRITE
#define _meshtastic_RemoteHardwarePinType_ARRAYSIZE ((meshtastic_RemoteHardwarePinType)(meshtastic_RemoteHardwarePinType_DIGITAL_WRITE+1))
+#define _meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_MIN meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_LOGIC_LOW
+#define _meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_MAX meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_EITHER_EDGE_ACTIVE_HIGH
+#define _meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_ARRAYSIZE ((meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType)(meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_EITHER_EDGE_ACTIVE_HIGH+1))
+
#define _meshtastic_ModuleConfig_AudioConfig_Audio_Baud_MIN meshtastic_ModuleConfig_AudioConfig_Audio_Baud_CODEC2_DEFAULT
#define _meshtastic_ModuleConfig_AudioConfig_Audio_Baud_MAX meshtastic_ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700B
#define _meshtastic_ModuleConfig_AudioConfig_Audio_Baud_ARRAYSIZE ((meshtastic_ModuleConfig_AudioConfig_Audio_Baud)(meshtastic_ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700B+1))
@@ -448,6 +476,7 @@ extern "C" {
+#define meshtastic_ModuleConfig_DetectionSensorConfig_detection_trigger_type_ENUMTYPE meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType
#define meshtastic_ModuleConfig_AudioConfig_bitrate_ENUMTYPE meshtastic_ModuleConfig_AudioConfig_Audio_Baud
@@ -473,14 +502,14 @@ extern "C" {
#define meshtastic_ModuleConfig_MapReportSettings_init_default {0, 0}
#define meshtastic_ModuleConfig_RemoteHardwareConfig_init_default {0, 0, 0, {meshtastic_RemoteHardwarePin_init_default, meshtastic_RemoteHardwarePin_init_default, meshtastic_RemoteHardwarePin_init_default, meshtastic_RemoteHardwarePin_init_default}}
#define meshtastic_ModuleConfig_NeighborInfoConfig_init_default {0, 0}
-#define meshtastic_ModuleConfig_DetectionSensorConfig_init_default {0, 0, 0, 0, "", 0, 0, 0}
+#define meshtastic_ModuleConfig_DetectionSensorConfig_init_default {0, 0, 0, 0, "", 0, _meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_MIN, 0}
#define meshtastic_ModuleConfig_AudioConfig_init_default {0, 0, _meshtastic_ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_PaxcounterConfig_init_default {0, 0, 0, 0}
#define meshtastic_ModuleConfig_SerialConfig_init_default {0, 0, 0, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN, 0}
#define meshtastic_ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_StoreForwardConfig_init_default {0, 0, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_RangeTestConfig_init_default {0, 0, 0}
-#define meshtastic_ModuleConfig_TelemetryConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+#define meshtastic_ModuleConfig_TelemetryConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_CannedMessageConfig_init_default {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0}
#define meshtastic_ModuleConfig_AmbientLightingConfig_init_default {0, 0, 0, 0, 0}
#define meshtastic_RemoteHardwarePin_init_default {0, "", _meshtastic_RemoteHardwarePinType_MIN}
@@ -489,14 +518,14 @@ extern "C" {
#define meshtastic_ModuleConfig_MapReportSettings_init_zero {0, 0}
#define meshtastic_ModuleConfig_RemoteHardwareConfig_init_zero {0, 0, 0, {meshtastic_RemoteHardwarePin_init_zero, meshtastic_RemoteHardwarePin_init_zero, meshtastic_RemoteHardwarePin_init_zero, meshtastic_RemoteHardwarePin_init_zero}}
#define meshtastic_ModuleConfig_NeighborInfoConfig_init_zero {0, 0}
-#define meshtastic_ModuleConfig_DetectionSensorConfig_init_zero {0, 0, 0, 0, "", 0, 0, 0}
+#define meshtastic_ModuleConfig_DetectionSensorConfig_init_zero {0, 0, 0, 0, "", 0, _meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_MIN, 0}
#define meshtastic_ModuleConfig_AudioConfig_init_zero {0, 0, _meshtastic_ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_PaxcounterConfig_init_zero {0, 0, 0, 0}
#define meshtastic_ModuleConfig_SerialConfig_init_zero {0, 0, 0, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN, 0}
#define meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_StoreForwardConfig_init_zero {0, 0, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_RangeTestConfig_init_zero {0, 0, 0}
-#define meshtastic_ModuleConfig_TelemetryConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+#define meshtastic_ModuleConfig_TelemetryConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_CannedMessageConfig_init_zero {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0}
#define meshtastic_ModuleConfig_AmbientLightingConfig_init_zero {0, 0, 0, 0, 0}
#define meshtastic_RemoteHardwarePin_init_zero {0, "", _meshtastic_RemoteHardwarePinType_MIN}
@@ -523,7 +552,7 @@ extern "C" {
#define meshtastic_ModuleConfig_DetectionSensorConfig_send_bell_tag 4
#define meshtastic_ModuleConfig_DetectionSensorConfig_name_tag 5
#define meshtastic_ModuleConfig_DetectionSensorConfig_monitor_pin_tag 6
-#define meshtastic_ModuleConfig_DetectionSensorConfig_detection_triggered_high_tag 7
+#define meshtastic_ModuleConfig_DetectionSensorConfig_detection_trigger_type_tag 7
#define meshtastic_ModuleConfig_DetectionSensorConfig_use_pullup_tag 8
#define meshtastic_ModuleConfig_AudioConfig_codec2_enabled_tag 1
#define meshtastic_ModuleConfig_AudioConfig_ptt_pin_tag 2
@@ -578,6 +607,9 @@ extern "C" {
#define meshtastic_ModuleConfig_TelemetryConfig_power_measurement_enabled_tag 8
#define meshtastic_ModuleConfig_TelemetryConfig_power_update_interval_tag 9
#define meshtastic_ModuleConfig_TelemetryConfig_power_screen_enabled_tag 10
+#define meshtastic_ModuleConfig_TelemetryConfig_health_measurement_enabled_tag 11
+#define meshtastic_ModuleConfig_TelemetryConfig_health_update_interval_tag 12
+#define meshtastic_ModuleConfig_TelemetryConfig_health_screen_enabled_tag 13
#define meshtastic_ModuleConfig_CannedMessageConfig_rotary1_enabled_tag 1
#define meshtastic_ModuleConfig_CannedMessageConfig_inputbroker_pin_a_tag 2
#define meshtastic_ModuleConfig_CannedMessageConfig_inputbroker_pin_b_tag 3
@@ -688,7 +720,7 @@ X(a, STATIC, SINGULAR, UINT32, state_broadcast_secs, 3) \
X(a, STATIC, SINGULAR, BOOL, send_bell, 4) \
X(a, STATIC, SINGULAR, STRING, name, 5) \
X(a, STATIC, SINGULAR, UINT32, monitor_pin, 6) \
-X(a, STATIC, SINGULAR, BOOL, detection_triggered_high, 7) \
+X(a, STATIC, SINGULAR, UENUM, detection_trigger_type, 7) \
X(a, STATIC, SINGULAR, BOOL, use_pullup, 8)
#define meshtastic_ModuleConfig_DetectionSensorConfig_CALLBACK NULL
#define meshtastic_ModuleConfig_DetectionSensorConfig_DEFAULT NULL
@@ -770,7 +802,10 @@ X(a, STATIC, SINGULAR, BOOL, air_quality_enabled, 6) \
X(a, STATIC, SINGULAR, UINT32, air_quality_interval, 7) \
X(a, STATIC, SINGULAR, BOOL, power_measurement_enabled, 8) \
X(a, STATIC, SINGULAR, UINT32, power_update_interval, 9) \
-X(a, STATIC, SINGULAR, BOOL, power_screen_enabled, 10)
+X(a, STATIC, SINGULAR, BOOL, power_screen_enabled, 10) \
+X(a, STATIC, SINGULAR, BOOL, health_measurement_enabled, 11) \
+X(a, STATIC, SINGULAR, UINT32, health_update_interval, 12) \
+X(a, STATIC, SINGULAR, BOOL, health_screen_enabled, 13)
#define meshtastic_ModuleConfig_TelemetryConfig_CALLBACK NULL
#define meshtastic_ModuleConfig_TelemetryConfig_DEFAULT NULL
@@ -855,7 +890,7 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg;
#define meshtastic_ModuleConfig_RemoteHardwareConfig_size 96
#define meshtastic_ModuleConfig_SerialConfig_size 28
#define meshtastic_ModuleConfig_StoreForwardConfig_size 24
-#define meshtastic_ModuleConfig_TelemetryConfig_size 36
+#define meshtastic_ModuleConfig_TelemetryConfig_size 46
#define meshtastic_ModuleConfig_size 257
#define meshtastic_RemoteHardwarePin_size 21
diff --git a/src/mesh/generated/meshtastic/mqtt.pb.cpp b/src/mesh/generated/meshtastic/mqtt.pb.cpp
index f00dd823b..74536cb79 100644
--- a/src/mesh/generated/meshtastic/mqtt.pb.cpp
+++ b/src/mesh/generated/meshtastic/mqtt.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/mqtt.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/mqtt.pb.h b/src/mesh/generated/meshtastic/mqtt.pb.h
index 8ec9f98c3..4d1027374 100644
--- a/src/mesh/generated/meshtastic/mqtt.pb.h
+++ b/src/mesh/generated/meshtastic/mqtt.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_MQTT_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_MQTT_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/paxcount.pb.cpp b/src/mesh/generated/meshtastic/paxcount.pb.cpp
index 67f07a31b..403288147 100644
--- a/src/mesh/generated/meshtastic/paxcount.pb.cpp
+++ b/src/mesh/generated/meshtastic/paxcount.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/paxcount.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/paxcount.pb.h b/src/mesh/generated/meshtastic/paxcount.pb.h
index 09377d833..b6b51fdd5 100644
--- a/src/mesh/generated/meshtastic/paxcount.pb.h
+++ b/src/mesh/generated/meshtastic/paxcount.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_PAXCOUNT_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_PAXCOUNT_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/portnums.pb.cpp b/src/mesh/generated/meshtastic/portnums.pb.cpp
index 8f32c0851..8fca9af79 100644
--- a/src/mesh/generated/meshtastic/portnums.pb.cpp
+++ b/src/mesh/generated/meshtastic/portnums.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/portnums.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
@@ -8,3 +8,4 @@
+
diff --git a/src/mesh/generated/meshtastic/portnums.pb.h b/src/mesh/generated/meshtastic/portnums.pb.h
index b9e537ddf..df6cf32c2 100644
--- a/src/mesh/generated/meshtastic/portnums.pb.h
+++ b/src/mesh/generated/meshtastic/portnums.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_PORTNUMS_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_PORTNUMS_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/powermon.pb.cpp b/src/mesh/generated/meshtastic/powermon.pb.cpp
index ce41ea021..6a9b7551a 100644
--- a/src/mesh/generated/meshtastic/powermon.pb.cpp
+++ b/src/mesh/generated/meshtastic/powermon.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/powermon.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
@@ -15,3 +15,5 @@ PB_BIND(meshtastic_PowerStressMessage, meshtastic_PowerStressMessage, AUTO)
+
+
diff --git a/src/mesh/generated/meshtastic/powermon.pb.h b/src/mesh/generated/meshtastic/powermon.pb.h
index 7de0618e9..5add85b85 100644
--- a/src/mesh/generated/meshtastic/powermon.pb.h
+++ b/src/mesh/generated/meshtastic/powermon.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_POWERMON_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_POWERMON_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/remote_hardware.pb.cpp b/src/mesh/generated/meshtastic/remote_hardware.pb.cpp
index 4a23698b2..239950e7e 100644
--- a/src/mesh/generated/meshtastic/remote_hardware.pb.cpp
+++ b/src/mesh/generated/meshtastic/remote_hardware.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/remote_hardware.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
@@ -11,3 +11,4 @@ PB_BIND(meshtastic_HardwareMessage, meshtastic_HardwareMessage, AUTO)
+
diff --git a/src/mesh/generated/meshtastic/remote_hardware.pb.h b/src/mesh/generated/meshtastic/remote_hardware.pb.h
index 936034b62..ade250e93 100644
--- a/src/mesh/generated/meshtastic/remote_hardware.pb.h
+++ b/src/mesh/generated/meshtastic/remote_hardware.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_REMOTE_HARDWARE_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_REMOTE_HARDWARE_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/rtttl.pb.cpp b/src/mesh/generated/meshtastic/rtttl.pb.cpp
index 8367fdbce..61ad8b73f 100644
--- a/src/mesh/generated/meshtastic/rtttl.pb.cpp
+++ b/src/mesh/generated/meshtastic/rtttl.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/rtttl.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
diff --git a/src/mesh/generated/meshtastic/rtttl.pb.h b/src/mesh/generated/meshtastic/rtttl.pb.h
index 452b0cf4b..2b7e35f11 100644
--- a/src/mesh/generated/meshtastic/rtttl.pb.h
+++ b/src/mesh/generated/meshtastic/rtttl.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_RTTTL_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_RTTTL_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/storeforward.pb.cpp b/src/mesh/generated/meshtastic/storeforward.pb.cpp
index 5b3fadd9a..71a232bf6 100644
--- a/src/mesh/generated/meshtastic/storeforward.pb.cpp
+++ b/src/mesh/generated/meshtastic/storeforward.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/storeforward.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
@@ -20,3 +20,4 @@ PB_BIND(meshtastic_StoreAndForward_Heartbeat, meshtastic_StoreAndForward_Heartbe
+
diff --git a/src/mesh/generated/meshtastic/storeforward.pb.h b/src/mesh/generated/meshtastic/storeforward.pb.h
index 311596c7f..71f2fcad5 100644
--- a/src/mesh/generated/meshtastic/storeforward.pb.h
+++ b/src/mesh/generated/meshtastic/storeforward.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_STOREFORWARD_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_STOREFORWARD_PB_H_INCLUDED
diff --git a/src/mesh/generated/meshtastic/telemetry.pb.cpp b/src/mesh/generated/meshtastic/telemetry.pb.cpp
index 90859c98e..f6d39da6e 100644
--- a/src/mesh/generated/meshtastic/telemetry.pb.cpp
+++ b/src/mesh/generated/meshtastic/telemetry.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/telemetry.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
@@ -21,6 +21,9 @@ PB_BIND(meshtastic_AirQualityMetrics, meshtastic_AirQualityMetrics, AUTO)
PB_BIND(meshtastic_LocalStats, meshtastic_LocalStats, AUTO)
+PB_BIND(meshtastic_HealthMetrics, meshtastic_HealthMetrics, AUTO)
+
+
PB_BIND(meshtastic_Telemetry, meshtastic_Telemetry, AUTO)
@@ -29,3 +32,4 @@ PB_BIND(meshtastic_Nau7802Config, meshtastic_Nau7802Config, AUTO)
+
diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h
index cedc2867e..a33988129 100644
--- a/src/mesh/generated/meshtastic/telemetry.pb.h
+++ b/src/mesh/generated/meshtastic/telemetry.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_TELEMETRY_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_TELEMETRY_PB_H_INCLUDED
@@ -71,7 +71,11 @@ typedef enum _meshtastic_TelemetrySensorType {
/* MAX17048 1S lipo battery sensor (voltage, state of charge, time to go) */
meshtastic_TelemetrySensorType_MAX17048 = 28,
/* Custom I2C sensor implementation based on https://github.com/meshtastic/i2c-sensor */
- meshtastic_TelemetrySensorType_CUSTOM_SENSOR = 29
+ meshtastic_TelemetrySensorType_CUSTOM_SENSOR = 29,
+ /* MAX30102 Pulse Oximeter and Heart-Rate Sensor */
+ meshtastic_TelemetrySensorType_MAX30102 = 30,
+ /* MLX90614 non-contact IR temperature sensor. */
+ meshtastic_TelemetrySensorType_MLX90614 = 31
} meshtastic_TelemetrySensorType;
/* Struct definitions */
@@ -223,7 +227,7 @@ typedef struct _meshtastic_LocalStats {
float air_util_tx;
/* Number of packets sent */
uint32_t num_packets_tx;
- /* Number of packets received good */
+ /* Number of packets received (both good and bad) */
uint32_t num_packets_rx;
/* Number of packets received that are malformed or violate the protocol */
uint32_t num_packets_rx_bad;
@@ -231,8 +235,29 @@ typedef struct _meshtastic_LocalStats {
uint16_t num_online_nodes;
/* Number of nodes total */
uint16_t num_total_nodes;
+ /* Number of received packets that were duplicates (due to multiple nodes relaying).
+ If this number is high, there are nodes in the mesh relaying packets when it's unnecessary, for example due to the ROUTER/REPEATER role. */
+ uint32_t num_rx_dupe;
+ /* Number of packets we transmitted that were a relay for others (not originating from ourselves). */
+ uint32_t num_tx_relay;
+ /* Number of times we canceled a packet to be relayed, because someone else did it before us.
+ This will always be zero for ROUTERs/REPEATERs. If this number is high, some other node(s) is/are relaying faster than you. */
+ uint32_t num_tx_relay_canceled;
} meshtastic_LocalStats;
+/* Health telemetry metrics */
+typedef struct _meshtastic_HealthMetrics {
+ /* Heart rate (beats per minute) */
+ bool has_heart_bpm;
+ uint8_t heart_bpm;
+ /* SpO2 (blood oxygen saturation) level */
+ bool has_spO2;
+ uint8_t spO2;
+ /* Body temperature in degrees Celsius */
+ bool has_temperature;
+ float temperature;
+} meshtastic_HealthMetrics;
+
/* Types of Measurements the telemetry module is equipped to handle */
typedef struct _meshtastic_Telemetry {
/* Seconds since 1970 - or 0 for unknown/unset */
@@ -249,6 +274,8 @@ typedef struct _meshtastic_Telemetry {
meshtastic_PowerMetrics power_metrics;
/* Local device mesh statistics */
meshtastic_LocalStats local_stats;
+ /* Health telemetry metrics */
+ meshtastic_HealthMetrics health_metrics;
} variant;
} meshtastic_Telemetry;
@@ -267,8 +294,9 @@ extern "C" {
/* Helper constants for enums */
#define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET
-#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_CUSTOM_SENSOR
-#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_CUSTOM_SENSOR+1))
+#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_MLX90614
+#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_MLX90614+1))
+
@@ -283,14 +311,16 @@ extern "C" {
#define meshtastic_EnvironmentMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_PowerMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_AirQualityMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
-#define meshtastic_LocalStats_init_default {0, 0, 0, 0, 0, 0, 0, 0}
+#define meshtastic_LocalStats_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+#define meshtastic_HealthMetrics_init_default {false, 0, false, 0, false, 0}
#define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}}
#define meshtastic_Nau7802Config_init_default {0, 0}
#define meshtastic_DeviceMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_EnvironmentMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_PowerMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_AirQualityMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
-#define meshtastic_LocalStats_init_zero {0, 0, 0, 0, 0, 0, 0, 0}
+#define meshtastic_LocalStats_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+#define meshtastic_HealthMetrics_init_zero {false, 0, false, 0, false, 0}
#define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}}
#define meshtastic_Nau7802Config_init_zero {0, 0}
@@ -343,12 +373,19 @@ extern "C" {
#define meshtastic_LocalStats_num_packets_rx_bad_tag 6
#define meshtastic_LocalStats_num_online_nodes_tag 7
#define meshtastic_LocalStats_num_total_nodes_tag 8
+#define meshtastic_LocalStats_num_rx_dupe_tag 9
+#define meshtastic_LocalStats_num_tx_relay_tag 10
+#define meshtastic_LocalStats_num_tx_relay_canceled_tag 11
+#define meshtastic_HealthMetrics_heart_bpm_tag 1
+#define meshtastic_HealthMetrics_spO2_tag 2
+#define meshtastic_HealthMetrics_temperature_tag 3
#define meshtastic_Telemetry_time_tag 1
#define meshtastic_Telemetry_device_metrics_tag 2
#define meshtastic_Telemetry_environment_metrics_tag 3
#define meshtastic_Telemetry_air_quality_metrics_tag 4
#define meshtastic_Telemetry_power_metrics_tag 5
#define meshtastic_Telemetry_local_stats_tag 6
+#define meshtastic_Telemetry_health_metrics_tag 7
#define meshtastic_Nau7802Config_zeroOffset_tag 1
#define meshtastic_Nau7802Config_calibrationFactor_tag 2
@@ -417,17 +454,28 @@ X(a, STATIC, SINGULAR, UINT32, num_packets_tx, 4) \
X(a, STATIC, SINGULAR, UINT32, num_packets_rx, 5) \
X(a, STATIC, SINGULAR, UINT32, num_packets_rx_bad, 6) \
X(a, STATIC, SINGULAR, UINT32, num_online_nodes, 7) \
-X(a, STATIC, SINGULAR, UINT32, num_total_nodes, 8)
+X(a, STATIC, SINGULAR, UINT32, num_total_nodes, 8) \
+X(a, STATIC, SINGULAR, UINT32, num_rx_dupe, 9) \
+X(a, STATIC, SINGULAR, UINT32, num_tx_relay, 10) \
+X(a, STATIC, SINGULAR, UINT32, num_tx_relay_canceled, 11)
#define meshtastic_LocalStats_CALLBACK NULL
#define meshtastic_LocalStats_DEFAULT NULL
+#define meshtastic_HealthMetrics_FIELDLIST(X, a) \
+X(a, STATIC, OPTIONAL, UINT32, heart_bpm, 1) \
+X(a, STATIC, OPTIONAL, UINT32, spO2, 2) \
+X(a, STATIC, OPTIONAL, FLOAT, temperature, 3)
+#define meshtastic_HealthMetrics_CALLBACK NULL
+#define meshtastic_HealthMetrics_DEFAULT NULL
+
#define meshtastic_Telemetry_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, FIXED32, time, 1) \
X(a, STATIC, ONEOF, MESSAGE, (variant,device_metrics,variant.device_metrics), 2) \
X(a, STATIC, ONEOF, MESSAGE, (variant,environment_metrics,variant.environment_metrics), 3) \
X(a, STATIC, ONEOF, MESSAGE, (variant,air_quality_metrics,variant.air_quality_metrics), 4) \
X(a, STATIC, ONEOF, MESSAGE, (variant,power_metrics,variant.power_metrics), 5) \
-X(a, STATIC, ONEOF, MESSAGE, (variant,local_stats,variant.local_stats), 6)
+X(a, STATIC, ONEOF, MESSAGE, (variant,local_stats,variant.local_stats), 6) \
+X(a, STATIC, ONEOF, MESSAGE, (variant,health_metrics,variant.health_metrics), 7)
#define meshtastic_Telemetry_CALLBACK NULL
#define meshtastic_Telemetry_DEFAULT NULL
#define meshtastic_Telemetry_variant_device_metrics_MSGTYPE meshtastic_DeviceMetrics
@@ -435,6 +483,7 @@ X(a, STATIC, ONEOF, MESSAGE, (variant,local_stats,variant.local_stats),
#define meshtastic_Telemetry_variant_air_quality_metrics_MSGTYPE meshtastic_AirQualityMetrics
#define meshtastic_Telemetry_variant_power_metrics_MSGTYPE meshtastic_PowerMetrics
#define meshtastic_Telemetry_variant_local_stats_MSGTYPE meshtastic_LocalStats
+#define meshtastic_Telemetry_variant_health_metrics_MSGTYPE meshtastic_HealthMetrics
#define meshtastic_Nau7802Config_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, INT32, zeroOffset, 1) \
@@ -447,6 +496,7 @@ extern const pb_msgdesc_t meshtastic_EnvironmentMetrics_msg;
extern const pb_msgdesc_t meshtastic_PowerMetrics_msg;
extern const pb_msgdesc_t meshtastic_AirQualityMetrics_msg;
extern const pb_msgdesc_t meshtastic_LocalStats_msg;
+extern const pb_msgdesc_t meshtastic_HealthMetrics_msg;
extern const pb_msgdesc_t meshtastic_Telemetry_msg;
extern const pb_msgdesc_t meshtastic_Nau7802Config_msg;
@@ -456,6 +506,7 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg;
#define meshtastic_PowerMetrics_fields &meshtastic_PowerMetrics_msg
#define meshtastic_AirQualityMetrics_fields &meshtastic_AirQualityMetrics_msg
#define meshtastic_LocalStats_fields &meshtastic_LocalStats_msg
+#define meshtastic_HealthMetrics_fields &meshtastic_HealthMetrics_msg
#define meshtastic_Telemetry_fields &meshtastic_Telemetry_msg
#define meshtastic_Nau7802Config_fields &meshtastic_Nau7802Config_msg
@@ -464,7 +515,8 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg;
#define meshtastic_AirQualityMetrics_size 72
#define meshtastic_DeviceMetrics_size 27
#define meshtastic_EnvironmentMetrics_size 85
-#define meshtastic_LocalStats_size 42
+#define meshtastic_HealthMetrics_size 11
+#define meshtastic_LocalStats_size 60
#define meshtastic_Nau7802Config_size 16
#define meshtastic_PowerMetrics_size 30
#define meshtastic_Telemetry_size 92
diff --git a/src/mesh/generated/meshtastic/xmodem.pb.cpp b/src/mesh/generated/meshtastic/xmodem.pb.cpp
index 8e5cde457..3960ccdaa 100644
--- a/src/mesh/generated/meshtastic/xmodem.pb.cpp
+++ b/src/mesh/generated/meshtastic/xmodem.pb.cpp
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#include "meshtastic/xmodem.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
@@ -11,3 +11,4 @@ PB_BIND(meshtastic_XModem, meshtastic_XModem, AUTO)
+
diff --git a/src/mesh/generated/meshtastic/xmodem.pb.h b/src/mesh/generated/meshtastic/xmodem.pb.h
index 67bd0869f..76edc0df6 100644
--- a/src/mesh/generated/meshtastic/xmodem.pb.h
+++ b/src/mesh/generated/meshtastic/xmodem.pb.h
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
-/* Generated by nanopb-0.4.8 */
+/* Generated by nanopb-0.4.9 */
#ifndef PB_MESHTASTIC_MESHTASTIC_XMODEM_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_XMODEM_PB_H_INCLUDED
diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp
index 772b3e821..c5ea86429 100644
--- a/src/mesh/http/ContentHandler.cpp
+++ b/src/mesh/http/ContentHandler.cpp
@@ -776,7 +776,7 @@ void handleRestart(HTTPRequest *req, HTTPResponse *res)
res->println("Meshtastic
\n");
res->println("Restarting");
- LOG_DEBUG("***** Restarted on HTTP(s) Request *****\n");
+ LOG_DEBUG("Restarted on HTTP(s) Request\n");
webServerThread->requestRestart = (millis() / 1000) + 5;
}
diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp
index e733d1801..c835878d3 100644
--- a/src/mesh/wifi/WiFiAPClient.cpp
+++ b/src/mesh/wifi/WiFiAPClient.cpp
@@ -15,16 +15,15 @@
#include
#ifdef ARCH_ESP32
#if !MESHTASTIC_EXCLUDE_WEBSERVER
-#if !MESHTASTIC_EXCLUDE_WEBSERVER
#include "mesh/http/WebServer.h"
#endif
-#endif
#include
#include
static void WiFiEvent(WiFiEvent_t event);
#endif
#ifndef DISABLE_NTP
+#include "Throttle.h"
#include
#endif
@@ -58,7 +57,7 @@ static void onNetworkConnected()
{
if (!APStartupComplete) {
// Start web server
- LOG_INFO("Starting network services\n");
+ LOG_INFO("Starting WiFi network services\n");
#ifdef ARCH_ESP32
// start mdns
@@ -144,7 +143,7 @@ static int32_t reconnectWiFi()
}
#ifndef DISABLE_NTP
- if (WiFi.isConnected() && (((millis() - lastrun_ntp) > 43200000) || (lastrun_ntp == 0))) { // every 12 hours
+ if (WiFi.isConnected() && (!Throttle::isWithinTimespanMs(lastrun_ntp, 43200000) || (lastrun_ntp == 0))) { // every 12 hours
LOG_DEBUG("Updating NTP time from %s\n", config.network.ntp_server);
if (timeClient.update()) {
LOG_DEBUG("NTP Request Success - Setting RTCQualityNTP if needed\n");
@@ -311,7 +310,12 @@ static void WiFiEvent(WiFiEvent_t event)
onNetworkConnected();
break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
+#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
+ LOG_INFO("Obtained Local IP6 address: %s\n", WiFi.linkLocalIPv6().toString().c_str());
+ LOG_INFO("Obtained GlobalIP6 address: %s\n", WiFi.globalIPv6().toString().c_str());
+#else
LOG_INFO("Obtained IP6 address: %s\n", WiFi.localIPv6().toString().c_str());
+#endif
break;
case ARDUINO_EVENT_WIFI_STA_LOST_IP:
LOG_INFO("Lost IP address and IP address is reset to 0\n");
diff --git a/src/meshUtils.cpp b/src/meshUtils.cpp
index c6f2c69b4..e98a6f1ce 100644
--- a/src/meshUtils.cpp
+++ b/src/meshUtils.cpp
@@ -60,10 +60,16 @@ char *strnstr(const char *s, const char *find, size_t slen)
void printBytes(const char *label, const uint8_t *p, size_t numbytes)
{
- LOG_DEBUG("%s: ", label);
- for (size_t i = 0; i < numbytes; i++)
- LOG_DEBUG("%02x ", p[i]);
- LOG_DEBUG("\n");
+ int labelSize = strlen(label);
+ if (labelSize < 100 && numbytes < 64) {
+ char *messageBuffer = new char[labelSize + (numbytes * 3) + 2];
+ strncpy(messageBuffer, label, labelSize);
+ for (size_t i = 0; i < numbytes; i++)
+ snprintf(messageBuffer + labelSize + i * 3, 4, " %02x", p[i]);
+ strcpy(messageBuffer + labelSize + numbytes * 3, "\n");
+ LOG_DEBUG(messageBuffer);
+ delete[] messageBuffer;
+ }
}
bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes)
@@ -73,4 +79,19 @@ bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes)
return false;
}
return true;
+}
+
+bool isOneOf(int item, int count, ...)
+{
+ va_list args;
+ va_start(args, count);
+ bool found = false;
+ for (int i = 0; i < count; ++i) {
+ if (item == va_arg(args, int)) {
+ found = true;
+ break;
+ }
+ }
+ va_end(args);
+ return found;
}
\ No newline at end of file
diff --git a/src/meshUtils.h b/src/meshUtils.h
index ce063cb6a..aff3976f4 100644
--- a/src/meshUtils.h
+++ b/src/meshUtils.h
@@ -1,5 +1,8 @@
#pragma once
#include "DebugConfiguration.h"
+#include
+#include
+#include
#include
/// C++ v17+ clamp function, limits a given value to a range defined by lo and hi
@@ -17,4 +20,8 @@ char *strnstr(const char *s, const char *find, size_t slen);
void printBytes(const char *label, const uint8_t *p, size_t numbytes);
// is the memory region filled with a single character?
-bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes);
\ No newline at end of file
+bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes);
+
+bool isOneOf(int item, int count, ...);
+
+#define IS_ONE_OF(item, ...) isOneOf(item, sizeof((int[]){__VA_ARGS__}) / sizeof(int), __VA_ARGS__)
diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp
index a88079aab..90e3b43c6 100644
--- a/src/modules/AdminModule.cpp
+++ b/src/modules/AdminModule.cpp
@@ -34,8 +34,8 @@
#include "modules/PositionModule.h"
#endif
-#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
-#include "AccelerometerThread.h"
+#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C
+#include "motion/AccelerometerThread.h"
#endif
AdminModule *adminModule;
@@ -66,19 +66,45 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
// if handled == false, then let others look at this message also if they want
bool handled = false;
assert(r);
- bool fromOthers = mp.from != 0 && mp.from != nodeDB->getNodeNum();
+ bool fromOthers = !isFromUs(&mp);
if (mp.which_payload_variant != meshtastic_MeshPacket_decoded_tag) {
return handled;
}
meshtastic_Channel *ch = &channels.getByIndex(mp.channel);
// Could tighten this up further by tracking the last public_key we went an AdminMessage request to
// and only allowing responses from that remote.
- if (!((mp.from == 0 && !config.security.is_managed) || messageIsResponse(r) ||
- (strcasecmp(ch->settings.name, Channels::adminChannel) == 0 && config.security.admin_channel_enabled) ||
- (mp.pki_encrypted && memcmp(mp.public_key.bytes, config.security.admin_key[0].bytes, 32) == 0))) {
- LOG_INFO("Ignoring admin payload %i\n", r->which_payload_variant);
+ if (messageIsResponse(r)) {
+ LOG_DEBUG("Allowing admin response message\n");
+ } else if (mp.from == 0) {
+ if (config.security.is_managed) {
+ LOG_INFO("Ignoring local admin payload because is_managed.\n");
+ return handled;
+ }
+ } else if (strcasecmp(ch->settings.name, Channels::adminChannel) == 0) {
+ if (!config.security.admin_channel_enabled) {
+ LOG_INFO("Ignoring admin channel, as legacy admin is disabled.\n");
+ myReply = allocErrorResponse(meshtastic_Routing_Error_NOT_AUTHORIZED, &mp);
+ return handled;
+ }
+ } else if (mp.pki_encrypted) {
+ if ((config.security.admin_key[0].size == 32 &&
+ memcmp(mp.public_key.bytes, config.security.admin_key[0].bytes, 32) == 0) ||
+ (config.security.admin_key[1].size == 32 &&
+ memcmp(mp.public_key.bytes, config.security.admin_key[1].bytes, 32) == 0) ||
+ (config.security.admin_key[2].size == 32 &&
+ memcmp(mp.public_key.bytes, config.security.admin_key[2].bytes, 32) == 0)) {
+ LOG_INFO("PKC admin payload with authorized sender key.\n");
+ } else {
+ myReply = allocErrorResponse(meshtastic_Routing_Error_ADMIN_PUBLIC_KEY_UNAUTHORIZED, &mp);
+ LOG_INFO("Received PKC admin payload, but the sender public key does not match the admin authorized key!\n");
+ return handled;
+ }
+ } else {
+ LOG_INFO("Ignoring unauthorized admin payload %i\n", r->which_payload_variant);
+ myReply = allocErrorResponse(meshtastic_Routing_Error_NOT_AUTHORIZED, &mp);
return handled;
}
+
LOG_INFO("Handling admin payload %i\n", r->which_payload_variant);
// all of the get and set messages, including those for other modules, flow through here first.
@@ -86,6 +112,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
if (mp.from != 0 && !messageIsRequest(r) && !messageIsResponse(r)) {
if (!checkPassKey(r)) {
LOG_WARN("Admin message without session_key!\n");
+ myReply = allocErrorResponse(meshtastic_Routing_Error_ADMIN_BAD_SESSION_KEY, &mp);
return handled;
}
}
@@ -186,18 +213,22 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
break;
}
case meshtastic_AdminMessage_factory_reset_config_tag: {
+ disableBluetooth();
LOG_INFO("Initiating factory config reset\n");
nodeDB->factoryReset();
+ LOG_INFO("Factory config reset finished, rebooting soon.\n");
reboot(DEFAULT_REBOOT_SECONDS);
break;
}
case meshtastic_AdminMessage_factory_reset_device_tag: {
+ disableBluetooth();
LOG_INFO("Initiating full factory reset\n");
nodeDB->factoryReset(true);
reboot(DEFAULT_REBOOT_SECONDS);
break;
}
case meshtastic_AdminMessage_nodedb_reset_tag: {
+ disableBluetooth();
LOG_INFO("Initiating node-db reset\n");
nodeDB->resetNodes();
reboot(DEFAULT_REBOOT_SECONDS);
@@ -209,6 +240,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
break;
}
case meshtastic_AdminMessage_commit_edit_settings_tag: {
+ disableBluetooth();
LOG_INFO("Committing transaction for edited settings\n");
hasOpenEditTransaction = false;
saveChanges(SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS);
@@ -252,34 +284,26 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
break;
}
case meshtastic_AdminMessage_set_fixed_position_tag: {
- if (fromOthers) {
- LOG_INFO("Ignoring set_fixed_position command from another node.\n");
- } else {
- LOG_INFO("Client is receiving a set_fixed_position command.\n");
- meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
- node->has_position = true;
- node->position = TypeConversions::ConvertToPositionLite(r->set_fixed_position);
- nodeDB->setLocalPosition(r->set_fixed_position);
- config.position.fixed_position = true;
- saveChanges(SEGMENT_DEVICESTATE | SEGMENT_CONFIG, false);
+ LOG_INFO("Client is receiving a set_fixed_position command.\n");
+ meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
+ node->has_position = true;
+ node->position = TypeConversions::ConvertToPositionLite(r->set_fixed_position);
+ nodeDB->setLocalPosition(r->set_fixed_position);
+ config.position.fixed_position = true;
+ saveChanges(SEGMENT_DEVICESTATE | SEGMENT_CONFIG, false);
#if !MESHTASTIC_EXCLUDE_GPS
- if (gps != nullptr)
- gps->enable();
- // Send our new fixed position to the mesh for good measure
- positionModule->sendOurPosition();
+ if (gps != nullptr)
+ gps->enable();
+ // Send our new fixed position to the mesh for good measure
+ positionModule->sendOurPosition();
#endif
- }
break;
}
case meshtastic_AdminMessage_remove_fixed_position_tag: {
- if (fromOthers) {
- LOG_INFO("Ignoring remove_fixed_position command from another node.\n");
- } else {
- LOG_INFO("Client is receiving a remove_fixed_position command.\n");
- nodeDB->clearLocalPosition();
- config.position.fixed_position = false;
- saveChanges(SEGMENT_DEVICESTATE | SEGMENT_CONFIG, false);
- }
+ LOG_INFO("Client is receiving a remove_fixed_position command.\n");
+ nodeDB->clearLocalPosition();
+ config.position.fixed_position = false;
+ saveChanges(SEGMENT_DEVICESTATE | SEGMENT_CONFIG, false);
break;
}
case meshtastic_AdminMessage_set_time_only_tag: {
@@ -406,7 +430,10 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
LOG_INFO("Setting config: Device\n");
config.has_device = true;
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
- if (config.device.double_tap_as_button_press == false && c.payload_variant.device.double_tap_as_button_press == true) {
+ if (config.device.double_tap_as_button_press == false && c.payload_variant.device.double_tap_as_button_press == true &&
+ accelerometerThread->enabled == false) {
+ config.device.double_tap_as_button_press = c.payload_variant.device.double_tap_as_button_press;
+ accelerometerThread->enabled = true;
accelerometerThread->start();
}
#endif
@@ -440,7 +467,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
requiresReboot = true;
}
}
-#if EVENT_MODE
+#if USERPREFS_EVENT_MODE
// If we're in event mode, nobody is a Router or Repeater
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
@@ -484,7 +511,10 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
requiresReboot = false;
}
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
- if (config.display.wake_on_tap_or_motion == false && c.payload_variant.display.wake_on_tap_or_motion == true) {
+ if (config.display.wake_on_tap_or_motion == false && c.payload_variant.display.wake_on_tap_or_motion == true &&
+ accelerometerThread->enabled == false) {
+ config.display.wake_on_tap_or_motion = c.payload_variant.display.wake_on_tap_or_motion;
+ accelerometerThread->enabled = true;
accelerometerThread->start();
}
#endif
@@ -557,12 +587,17 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
break;
}
+ if (requiresReboot && !hasOpenEditTransaction) {
+ disableBluetooth();
+ }
saveChanges(changes, requiresReboot);
}
void AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c)
{
+ if (!hasOpenEditTransaction)
+ disableBluetooth();
switch (c.which_payload_variant) {
case meshtastic_ModuleConfig_mqtt_tag:
LOG_INFO("Setting module config: MQTT\n");
@@ -634,7 +669,6 @@ void AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c)
moduleConfig.paxcounter = c.payload_variant.paxcounter;
break;
}
-
saveChanges(SEGMENT_MODULECONFIG);
}
@@ -937,7 +971,7 @@ void AdminModule::saveChanges(int saveWhat, bool shouldReboot)
} else {
LOG_INFO("Delaying save of changes to disk until the open transaction is committed\n");
}
- if (shouldReboot) {
+ if (shouldReboot && !hasOpenEditTransaction) {
reboot(DEFAULT_REBOOT_SECONDS);
}
}
@@ -997,7 +1031,7 @@ bool AdminModule::checkPassKey(meshtastic_AdminMessage *res)
memcmp(res->session_passkey.bytes, session_passkey, 8) == 0);
}
-bool AdminModule::messageIsResponse(meshtastic_AdminMessage *r)
+bool AdminModule::messageIsResponse(const meshtastic_AdminMessage *r)
{
if (r->which_payload_variant == meshtastic_AdminMessage_get_channel_response_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_owner_response_tag ||
@@ -1014,7 +1048,7 @@ bool AdminModule::messageIsResponse(meshtastic_AdminMessage *r)
return false;
}
-bool AdminModule::messageIsRequest(meshtastic_AdminMessage *r)
+bool AdminModule::messageIsRequest(const meshtastic_AdminMessage *r)
{
if (r->which_payload_variant == meshtastic_AdminMessage_get_channel_request_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_owner_request_tag ||
@@ -1029,3 +1063,16 @@ bool AdminModule::messageIsRequest(meshtastic_AdminMessage *r)
else
return false;
}
+
+void disableBluetooth()
+{
+#if HAS_BLUETOOTH
+#ifdef ARCH_ESP32
+ if (nimbleBluetooth)
+ nimbleBluetooth->deinit();
+#elif defined(ARCH_NRF52)
+ if (nrf52Bluetooth)
+ nrf52Bluetooth->shutdown();
+#endif
+#endif
+}
\ No newline at end of file
diff --git a/src/modules/AdminModule.h b/src/modules/AdminModule.h
index 61c54d1b1..d6c0a7343 100644
--- a/src/modules/AdminModule.h
+++ b/src/modules/AdminModule.h
@@ -55,8 +55,10 @@ class AdminModule : public ProtobufModule, public Obser
void setPassKey(meshtastic_AdminMessage *res);
bool checkPassKey(meshtastic_AdminMessage *res);
- bool messageIsResponse(meshtastic_AdminMessage *r);
- bool messageIsRequest(meshtastic_AdminMessage *r);
+ bool messageIsResponse(const meshtastic_AdminMessage *r);
+ bool messageIsRequest(const meshtastic_AdminMessage *r);
};
-extern AdminModule *adminModule;
\ No newline at end of file
+extern AdminModule *adminModule;
+
+void disableBluetooth();
\ No newline at end of file
diff --git a/src/modules/AtakPluginModule.cpp b/src/modules/AtakPluginModule.cpp
index 437a341db..72d069619 100644
--- a/src/modules/AtakPluginModule.cpp
+++ b/src/modules/AtakPluginModule.cpp
@@ -52,6 +52,10 @@ meshtastic_TAKPacket AtakPluginModule::cloneTAKPacketData(meshtastic_TAKPacket *
} else if (t->which_payload_variant == meshtastic_TAKPacket_chat_tag) {
clone.which_payload_variant = meshtastic_TAKPacket_chat_tag;
clone.payload_variant.chat = {0};
+ } else if (t->which_payload_variant == meshtastic_TAKPacket_detail_tag) {
+ clone.which_payload_variant = meshtastic_TAKPacket_detail_tag;
+ clone.payload_variant.detail.size = t->payload_variant.detail.size;
+ memcpy(clone.payload_variant.detail.bytes, t->payload_variant.detail.bytes, t->payload_variant.detail.size);
}
return clone;
diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp
index 4df5a03fc..ed0dce25f 100644
--- a/src/modules/CannedMessageModule.cpp
+++ b/src/modules/CannedMessageModule.cpp
@@ -12,6 +12,7 @@
#include "detect/ScanI2C.h"
#include "input/ScanAndSelect.h"
#include "mesh/generated/meshtastic/cannedmessages.pb.h"
+#include "modules/AdminModule.h"
#include "main.h" // for cardkb_found
#include "modules/ExternalNotificationModule.h" // for buzzer control
@@ -27,6 +28,7 @@
#endif
#include "graphics/ScreenFonts.h"
+#include
// Remove Canned message screen if no action is taken for some milliseconds
#define INACTIVATE_AFTER_MS 20000
@@ -77,16 +79,16 @@ int CannedMessageModule::splitConfiguredMessages()
int messageIndex = 0;
int i = 0;
- String messages = cannedMessageModuleConfig.messages;
+ String canned_messages = cannedMessageModuleConfig.messages;
#if defined(T_WATCH_S3) || defined(RAK14014)
- String separator = messages.length() ? "|" : "";
+ String separator = canned_messages.length() ? "|" : "";
- messages = "[---- Free Text ----]" + separator + messages;
+ canned_messages = "[---- Free Text ----]" + separator + canned_messages;
#endif
// collect all the message parts
- strncpy(this->messageStore, messages.c_str(), sizeof(this->messageStore));
+ strncpy(this->messageStore, canned_messages.c_str(), sizeof(this->messageStore));
// The first message points to the beginning of the store.
this->messages[messageIndex++] = this->messageStore;
@@ -190,17 +192,17 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
#if defined(T_WATCH_S3) || defined(RAK14014)
if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) {
- this->payload = 0xb4;
+ this->payload = INPUT_BROKER_MSG_LEFT;
} else if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) {
- this->payload = 0xb7;
+ this->payload = INPUT_BROKER_MSG_RIGHT;
}
#else
// tweak for left/right events generated via trackball/touch with empty kbchar
if (!event->kbchar) {
if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) {
- this->payload = 0xb4;
+ this->payload = INPUT_BROKER_MSG_LEFT;
} else if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) {
- this->payload = 0xb7;
+ this->payload = INPUT_BROKER_MSG_RIGHT;
}
} else {
// pass the pressed key
@@ -222,26 +224,26 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
// Run modifier key code below, (doesnt inturrupt typing or reset to start screen page)
switch (event->kbchar) {
- case 0x11: // make screen brighter
+ case INPUT_BROKER_MSG_BRIGHTNESS_UP: // make screen brighter
if (screen)
screen->increaseBrightness();
LOG_DEBUG("increasing Screen Brightness\n");
break;
- case 0x12: // make screen dimmer
+ case INPUT_BROKER_MSG_BRIGHTNESS_DOWN: // make screen dimmer
if (screen)
screen->decreaseBrightness();
LOG_DEBUG("Decreasing Screen Brightness\n");
break;
- case 0xf1: // draw modifier (function) symbal
+ case INPUT_BROKER_MSG_FN_SYMBOL_ON: // draw modifier (function) symbal
if (screen)
screen->setFunctionSymbal("Fn");
break;
- case 0xf2: // remove modifier (function) symbal
+ case INPUT_BROKER_MSG_FN_SYMBOL_OFF: // remove modifier (function) symbal
if (screen)
screen->removeFunctionSymbal("Fn");
break;
// mute (switch off/toggle) external notifications on fn+m
- case 0xac:
+ case INPUT_BROKER_MSG_MUTE_TOGGLE:
if (moduleConfig.external_notification.enabled == true) {
if (externalNotificationModule->getMute()) {
externalNotificationModule->setMute(false);
@@ -257,7 +259,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
}
}
break;
- case 0x9e: // toggle GPS like triple press does
+ case INPUT_BROKER_MSG_GPS_TOGGLE: // toggle GPS like triple press does
#if !MESHTASTIC_EXCLUDE_GPS
if (gps != nullptr) {
gps->toggleGpsMode();
@@ -267,7 +269,22 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
showTemporaryMessage("GPS Toggled");
#endif
break;
- case 0xaf: // fn+space send network ping like double press does
+ case INPUT_BROKER_MSG_BLUETOOTH_TOGGLE: // toggle Bluetooth on/off
+ if (config.bluetooth.enabled == true) {
+ config.bluetooth.enabled = false;
+ LOG_INFO("User toggled Bluetooth");
+ nodeDB->saveToDisk();
+ disableBluetooth();
+ showTemporaryMessage("Bluetooth OFF");
+ } else if (config.bluetooth.enabled == false) {
+ config.bluetooth.enabled = true;
+ LOG_INFO("User toggled Bluetooth");
+ nodeDB->saveToDisk();
+ rebootAtMsec = millis() + 2000;
+ showTemporaryMessage("Bluetooth ON\nReboot");
+ }
+ break;
+ case INPUT_BROKER_MSG_SEND_PING: // fn+space send network ping like double press does
service->refreshLocalMeshNode();
if (service->trySendPosition(NODENUM_BROADCAST, true)) {
showTemporaryMessage("Position \nUpdate Sent");
@@ -275,6 +292,13 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
showTemporaryMessage("Node Info \nUpdate Sent");
}
break;
+ case INPUT_BROKER_MSG_DISMISS_FRAME: // fn+del: dismiss screen frames like text or waypoint
+ // Avoid opening the canned message screen frame
+ // We're only handling the keypress here by convention, this has nothing to do with canned messages
+ this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
+ // Attempt to close whatever frame is currently shown on display
+ screen->dismissCurrentFrame();
+ return 0;
default:
// pass the pressed key
// LOG_DEBUG("Canned message ANYKEY (%x)\n", event->kbchar);
@@ -283,7 +307,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
validEvent = true;
break;
}
- if (screen && (event->kbchar != 0xf1)) {
+ if (screen && (event->kbchar != INPUT_BROKER_MSG_FN_SYMBOL_ON)) {
screen->removeFunctionSymbal("Fn"); // remove modifier (function) symbal
}
}
@@ -422,7 +446,7 @@ int32_t CannedMessageModule::runOnce()
this->notifyObservers(&e);
} else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) &&
- ((millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS)) {
+ !Throttle::isWithinTimespanMs(this->lastTouchMillis, INACTIVATE_AFTER_MS)) {
// Reset module
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->currentMessageIndex = -1;
@@ -505,7 +529,7 @@ int32_t CannedMessageModule::runOnce()
}
} else if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT || this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) {
switch (this->payload) {
- case 0xb4: // left
+ case INPUT_BROKER_MSG_LEFT:
if (this->destSelect == CANNED_MESSAGE_DESTINATION_TYPE_NODE) {
size_t numMeshNodes = nodeDB->getNumMeshNodes();
if (this->dest == NODENUM_BROADCAST) {
@@ -540,7 +564,7 @@ int32_t CannedMessageModule::runOnce()
}
}
break;
- case 0xb7: // right
+ case INPUT_BROKER_MSG_RIGHT:
if (this->destSelect == CANNED_MESSAGE_DESTINATION_TYPE_NODE) {
size_t numMeshNodes = nodeDB->getNumMeshNodes();
if (this->dest == NODENUM_BROADCAST) {
@@ -602,19 +626,19 @@ int32_t CannedMessageModule::runOnce()
this->destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NODE;
}
break;
- case 0xb4: // left
- case 0xb7: // right
+ case INPUT_BROKER_MSG_LEFT:
+ case INPUT_BROKER_MSG_RIGHT:
// already handled above
break;
// handle fn+s for shutdown
- case 0x9b:
+ case INPUT_BROKER_MSG_SHUTDOWN:
if (screen)
screen->startAlert("Shutting down...");
shutdownAtMsec = millis() + DEFAULT_SHUTDOWN_SECONDS * 1000;
runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
break;
// and fn+r for reboot
- case 0x90:
+ case INPUT_BROKER_MSG_REBOOT:
if (screen)
screen->startAlert("Rebooting...");
rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000;
@@ -934,6 +958,19 @@ void CannedMessageModule::drawEnterIcon(OLEDDisplay *display, int x, int y, floa
#endif
+// Indicate to screen class that module is handling keyboard input specially (at certain times)
+// This prevents the left & right keys being used for nav. between screen frames during text entry.
+bool CannedMessageModule::interceptingKeyboardInput()
+{
+ switch (runState) {
+ case CANNED_MESSAGE_RUN_STATE_DISABLED:
+ case CANNED_MESSAGE_RUN_STATE_INACTIVE:
+ return false;
+ default:
+ return true;
+ }
+}
+
void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
char buffer[50];
@@ -977,7 +1014,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
int16_t rssiY = 130;
// If dislay is *slighly* too small for the original consants, squish up a bit
- if (display->getHeight() < rssiY) {
+ if (display->getHeight() < rssiY + FONT_HEIGHT_SMALL) {
snrY = display->getHeight() - ((1.5) * FONT_HEIGHT_SMALL);
rssiY = display->getHeight() - ((2.5) * FONT_HEIGHT_SMALL);
}
@@ -1124,7 +1161,6 @@ void CannedMessageModule::loadProtoForModule()
installDefaultCannedMessageModuleConfig();
}
}
-
/**
* @brief Save the module config to file.
*
diff --git a/src/modules/CannedMessageModule.h b/src/modules/CannedMessageModule.h
index 9e6af8890..e9dc2bda0 100644
--- a/src/modules/CannedMessageModule.h
+++ b/src/modules/CannedMessageModule.h
@@ -114,6 +114,7 @@ class CannedMessageModule : public SinglePortModule, public ObservableshouldDraw(); }
virtual Observable *getUIFrameObservable() override { return this; }
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
+ virtual bool interceptingKeyboardInput() override;
virtual AdminMessageHandleResult handleAdminMessageForModule(const meshtastic_MeshPacket &mp,
meshtastic_AdminMessage *request,
meshtastic_AdminMessage *response) override;
@@ -223,4 +224,4 @@ class CannedMessageModule : public SinglePortModule, public Observable
DetectionSensorModule *detectionSensorModule;
#define GPIO_POLLING_INTERVAL 100
#define DELAYED_INTERVAL 1000
+typedef enum {
+ DetectionSensorVerdictDetected,
+ DetectionSensorVerdictSendState,
+ DetectionSensorVerdictNoop,
+} DetectionSensorTriggerVerdict;
+
+typedef DetectionSensorTriggerVerdict (*DetectionSensorTriggerHandler)(bool prev, bool current);
+
+static DetectionSensorTriggerVerdict detection_trigger_logic_level(bool prev, bool current)
+{
+ return current ? DetectionSensorVerdictDetected : DetectionSensorVerdictNoop;
+}
+
+static DetectionSensorTriggerVerdict detection_trigger_single_edge(bool prev, bool current)
+{
+ return (!prev && current) ? DetectionSensorVerdictDetected : DetectionSensorVerdictNoop;
+}
+
+static DetectionSensorTriggerVerdict detection_trigger_either_edge(bool prev, bool current)
+{
+ if (prev == current) {
+ return DetectionSensorVerdictNoop;
+ }
+ return current ? DetectionSensorVerdictDetected : DetectionSensorVerdictSendState;
+}
+
+const static DetectionSensorTriggerHandler handlers[_meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_MAX + 1] = {
+ [meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_LOGIC_LOW] = detection_trigger_logic_level,
+ [meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_LOGIC_HIGH] = detection_trigger_logic_level,
+ [meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_FALLING_EDGE] = detection_trigger_single_edge,
+ [meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_RISING_EDGE] = detection_trigger_single_edge,
+ [meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_EITHER_EDGE_ACTIVE_LOW] = detection_trigger_either_edge,
+ [meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_EITHER_EDGE_ACTIVE_HIGH] = detection_trigger_either_edge,
+};
+
int32_t DetectionSensorModule::runOnce()
{
/*
@@ -21,7 +57,8 @@ int32_t DetectionSensorModule::runOnce()
// moduleConfig.detection_sensor.monitor_pin = 21; // WisBlock RAK12013 Radar IO6
// moduleConfig.detection_sensor.minimum_broadcast_secs = 30;
// moduleConfig.detection_sensor.state_broadcast_secs = 120;
- // moduleConfig.detection_sensor.detection_triggered_high = true;
+ // moduleConfig.detection_sensor.detection_trigger_type =
+ // meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_LOGIC_HIGH;
// strcpy(moduleConfig.detection_sensor.name, "Motion");
if (moduleConfig.detection_sensor.enabled == false)
@@ -49,18 +86,31 @@ int32_t DetectionSensorModule::runOnce()
// LOG_DEBUG("Detection Sensor Module: Current pin state: %i\n", digitalRead(moduleConfig.detection_sensor.monitor_pin));
- if ((millis() - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.detection_sensor.minimum_broadcast_secs) &&
- hasDetectionEvent()) {
- sendDetectionMessage();
- return DELAYED_INTERVAL;
+ if (!Throttle::isWithinTimespanMs(lastSentToMesh,
+ Default::getConfiguredOrDefaultMs(moduleConfig.detection_sensor.minimum_broadcast_secs))) {
+ bool isDetected = hasDetectionEvent();
+ DetectionSensorTriggerVerdict verdict =
+ handlers[moduleConfig.detection_sensor.detection_trigger_type](wasDetected, isDetected);
+ wasDetected = isDetected;
+ switch (verdict) {
+ case DetectionSensorVerdictDetected:
+ sendDetectionMessage();
+ return DELAYED_INTERVAL;
+ case DetectionSensorVerdictSendState:
+ sendCurrentStateMessage(isDetected);
+ return DELAYED_INTERVAL;
+ case DetectionSensorVerdictNoop:
+ break;
+ }
}
// Even if we haven't detected an event, broadcast our current state to the mesh on the scheduled interval as a sort
// of heartbeat. We only do this if the minimum broadcast interval is greater than zero, otherwise we'll only broadcast state
// change detections.
- else if (moduleConfig.detection_sensor.state_broadcast_secs > 0 &&
- (millis() - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.detection_sensor.state_broadcast_secs,
- default_telemetry_broadcast_interval_secs)) {
- sendCurrentStateMessage();
+ if (moduleConfig.detection_sensor.state_broadcast_secs > 0 &&
+ !Throttle::isWithinTimespanMs(lastSentToMesh,
+ Default::getConfiguredOrDefaultMs(moduleConfig.detection_sensor.state_broadcast_secs,
+ default_telemetry_broadcast_interval_secs))) {
+ sendCurrentStateMessage(hasDetectionEvent());
return DELAYED_INTERVAL;
}
return GPIO_POLLING_INTERVAL;
@@ -86,10 +136,10 @@ void DetectionSensorModule::sendDetectionMessage()
delete[] message;
}
-void DetectionSensorModule::sendCurrentStateMessage()
+void DetectionSensorModule::sendCurrentStateMessage(bool state)
{
char *message = new char[40];
- sprintf(message, "%s state: %i", moduleConfig.detection_sensor.name, hasDetectionEvent());
+ sprintf(message, "%s state: %i", moduleConfig.detection_sensor.name, state);
meshtastic_MeshPacket *p = allocDataPacket();
p->want_ack = false;
@@ -105,5 +155,5 @@ bool DetectionSensorModule::hasDetectionEvent()
{
bool currentState = digitalRead(moduleConfig.detection_sensor.monitor_pin);
// LOG_DEBUG("Detection Sensor Module: Current state: %i\n", currentState);
- return moduleConfig.detection_sensor.detection_triggered_high ? currentState : !currentState;
+ return (moduleConfig.detection_sensor.detection_trigger_type & 1) ? currentState : !currentState;
}
\ No newline at end of file
diff --git a/src/modules/DetectionSensorModule.h b/src/modules/DetectionSensorModule.h
index ed6cddda5..b960c8744 100644
--- a/src/modules/DetectionSensorModule.h
+++ b/src/modules/DetectionSensorModule.h
@@ -15,8 +15,9 @@ class DetectionSensorModule : public SinglePortModule, private concurrency::OSTh
private:
bool firstTime = true;
uint32_t lastSentToMesh = 0;
+ bool wasDetected = false;
void sendDetectionMessage();
- void sendCurrentStateMessage();
+ void sendCurrentStateMessage(bool state);
bool hasDetectionEvent();
};
diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp
index 652db04d3..8abc386ec 100644
--- a/src/modules/ExternalNotificationModule.cpp
+++ b/src/modules/ExternalNotificationModule.cpp
@@ -428,7 +428,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
drv.setWaveform(2, 0);
drv.go();
#endif
- if (getFrom(&mp) != nodeDB->getNodeNum()) {
+ if (!isFromUs(&mp)) {
// Check if the message contains a bell character. Don't do this loop for every pin, just once.
auto &p = mp.decoded;
bool containsBell = false;
@@ -593,4 +593,4 @@ void ExternalNotificationModule::handleSetRingtone(const char *from_msg)
if (changed) {
nodeDB->saveProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, &meshtastic_RTTTLConfig_msg, &rtttlConfig);
}
-}
+}
\ No newline at end of file
diff --git a/src/modules/ExternalNotificationModule.h b/src/modules/ExternalNotificationModule.h
index 000ef10f6..e86585c2f 100644
--- a/src/modules/ExternalNotificationModule.h
+++ b/src/modules/ExternalNotificationModule.h
@@ -3,7 +3,7 @@
#include "SinglePortModule.h"
#include "concurrency/OSThread.h"
#include "configuration.h"
-#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(ARCH_APOLLO3)
+#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(ARCH_APOLLO3) && !defined(CONFIG_IDF_TARGET_ESP32C6)
#include
#else
// Noop class for portduino.
diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp
index eca0e8ac9..ad3f0ace4 100644
--- a/src/modules/Modules.cpp
+++ b/src/modules/Modules.cpp
@@ -47,6 +47,9 @@
#endif
#if ARCH_PORTDUINO
#include "input/LinuxInputImpl.h"
+#if !MESHTASTIC_EXCLUDE_STOREFORWARD
+#include "modules/StoreForwardModule.h"
+#endif
#endif
#if HAS_TELEMETRY
#include "modules/Telemetry/DeviceTelemetry.h"
@@ -55,6 +58,7 @@
#include "main.h"
#include "modules/Telemetry/AirQualityTelemetry.h"
#include "modules/Telemetry/EnvironmentTelemetry.h"
+#include "modules/Telemetry/HealthTelemetry.h"
#endif
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_POWER_TELEMETRY
#include "modules/Telemetry/PowerTelemetry.h"
@@ -67,7 +71,7 @@
#include "modules/esp32/PaxcounterModule.h"
#endif
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
-#include "modules/esp32/StoreForwardModule.h"
+#include "modules/StoreForwardModule.h"
#endif
#endif
#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)
@@ -191,6 +195,10 @@ void setupModules()
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first > 0) {
new AirQualityTelemetryModule();
}
+ if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MAX30102].first > 0 ||
+ nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MLX90614].first > 0) {
+ new HealthTelemetryModule();
+ }
#endif
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_POWER_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
new PowerTelemetryModule();
@@ -206,13 +214,15 @@ void setupModules()
#if defined(USE_SX1280) && !MESHTASTIC_EXCLUDE_AUDIO
audioModule = new AudioModule();
#endif
-#if !MESHTASTIC_EXCLUDE_STOREFORWARD
- storeForwardModule = new StoreForwardModule();
-#endif
#if !MESHTASTIC_EXCLUDE_PAXCOUNTER
paxcounterModule = new PaxcounterModule();
#endif
#endif
+#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
+#if !MESHTASTIC_EXCLUDE_STOREFORWARD
+ storeForwardModule = new StoreForwardModule();
+#endif
+#endif
#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)
#if !MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION
externalNotificationModule = new ExternalNotificationModule();
diff --git a/src/modules/NeighborInfoModule.cpp b/src/modules/NeighborInfoModule.cpp
index 2af953b4e..163d3a897 100644
--- a/src/modules/NeighborInfoModule.cpp
+++ b/src/modules/NeighborInfoModule.cpp
@@ -3,6 +3,7 @@
#include "MeshService.h"
#include "NodeDB.h"
#include "gps/RTC.h"
+#include
NeighborInfoModule *neighborInfoModule;
@@ -61,7 +62,8 @@ uint32_t NeighborInfoModule::collectNeighborInfo(meshtastic_NeighborInfo *neighb
NodeNum my_node_id = nodeDB->getNodeNum();
neighborInfo->node_id = my_node_id;
neighborInfo->last_sent_by_id = my_node_id;
- neighborInfo->node_broadcast_interval_secs = moduleConfig.neighbor_info.update_interval;
+ neighborInfo->node_broadcast_interval_secs =
+ Default::getConfiguredOrDefault(moduleConfig.neighbor_info.update_interval, default_telemetry_broadcast_interval_secs);
cleanUpNeighbors();
@@ -87,6 +89,7 @@ void NeighborInfoModule::cleanUpNeighbors()
NodeNum my_node_id = nodeDB->getNodeNum();
for (auto it = neighbors.rbegin(); it != neighbors.rend();) {
// We will remove a neighbor if we haven't heard from them in twice the broadcast interval
+ // cannot use isWithinTimespanMs() as it->last_rx_time is seconds since 1970
if ((now - it->last_rx_time > it->node_broadcast_interval_secs * 2) && (it->node_id != my_node_id)) {
LOG_DEBUG("Removing neighbor with node ID 0x%x\n", it->node_id);
it = std::vector::reverse_iterator(
diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp
index cb047a4dc..61ec375cc 100644
--- a/src/modules/NodeInfoModule.cpp
+++ b/src/modules/NodeInfoModule.cpp
@@ -6,6 +6,7 @@
#include "Router.h"
#include "configuration.h"
#include "main.h"
+#include
NodeInfoModule *nodeInfoModule;
@@ -25,7 +26,7 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
}
// if user has changed while packet was not for us, inform phone
- if (hasChanged && !wasBroadcast && mp.to != nodeDB->getNodeNum())
+ if (hasChanged && !wasBroadcast && !isToUs(&mp))
service->sendToPhone(packetPool.allocCopy(mp));
// LOG_DEBUG("did handleReceived\n");
@@ -67,13 +68,12 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply()
LOG_DEBUG("Skip sending NodeInfo due to > 40 percent channel util.\n");
return NULL;
}
- uint32_t now = millis();
// If we sent our NodeInfo less than 5 min. ago, don't send it again as it may be still underway.
- if (!shorterTimeout && lastSentToMesh && (now - lastSentToMesh) < (5 * 60 * 1000)) {
+ if (!shorterTimeout && lastSentToMesh && Throttle::isWithinTimespanMs(lastSentToMesh, 5 * 60 * 1000)) {
LOG_DEBUG("Skip sending NodeInfo since we just sent it less than 5 minutes ago.\n");
ignoreRequest = true; // Mark it as ignored for MeshModule
return NULL;
- } else if (shorterTimeout && lastSentToMesh && (now - lastSentToMesh) < (60 * 1000)) {
+ } else if (shorterTimeout && lastSentToMesh && Throttle::isWithinTimespanMs(lastSentToMesh, 60 * 1000)) {
LOG_DEBUG("Skip sending actively requested NodeInfo since we just sent it less than 60 seconds ago.\n");
ignoreRequest = true; // Mark it as ignored for MeshModule
return NULL;
@@ -82,7 +82,7 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply()
meshtastic_User &u = owner;
LOG_INFO("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name);
- lastSentToMesh = now;
+ lastSentToMesh = millis();
return allocDataProtobuf(u);
}
}
diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp
index 450fcf7f8..a2693a814 100644
--- a/src/modules/PositionModule.cpp
+++ b/src/modules/PositionModule.cpp
@@ -12,6 +12,7 @@
#include "gps/RTC.h"
#include "main.h"
#include "mesh/compression/unishox2.h"
+#include "meshUtils.h"
#include "meshtastic/atak.pb.h"
#include "sleep.h"
#include "target_specific.h"
@@ -53,7 +54,7 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
// 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)
bool isLocal = false;
- if (nodeDB->getNodeNum() == getFrom(&mp)) {
+ if (isFromUs(&mp)) {
isLocal = true;
if (config.position.fixed_position) {
LOG_DEBUG("Ignore incoming position update from myself except for time, because position.fixed_position is true\n");
@@ -109,7 +110,7 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
void PositionModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtastic_Position *p)
{
// Phone position packets need to be truncated to the channel precision
- if (nodeDB->getNodeNum() == getFrom(&mp) && (precision < 32 && precision > 0)) {
+ if (isFromUs(&mp) && (precision < 32 && precision > 0)) {
LOG_DEBUG("Truncating phone position to channel precision %i\n", precision);
p->latitude_i = p->latitude_i & (UINT32_MAX << (32 - precision));
p->longitude_i = p->longitude_i & (UINT32_MAX << (32 - precision));
@@ -145,8 +146,12 @@ void PositionModule::trySetRtc(meshtastic_Position p, bool isLocal, bool forceUp
bool PositionModule::hasQualityTimesource()
{
bool setFromPhoneOrNtpToday =
- lastSetFromPhoneNtpOrGps == 0 ? false : (millis() - lastSetFromPhoneNtpOrGps) <= (SEC_PER_DAY * 1000UL);
+ lastSetFromPhoneNtpOrGps == 0 ? false : Throttle::isWithinTimespanMs(lastSetFromPhoneNtpOrGps, SEC_PER_DAY * 1000UL);
+#if MESHTASTIC_EXCLUDE_GPS
+ bool hasGpsOrRtc = (rtc_found.address != ScanI2C::ADDRESS_NONE.address);
+#else
bool hasGpsOrRtc = (gps && gps->isConnected()) || (rtc_found.address != ScanI2C::ADDRESS_NONE.address);
+#endif
return hasGpsOrRtc || setFromPhoneOrNtpToday;
}
@@ -269,7 +274,7 @@ meshtastic_MeshPacket *PositionModule::allocAtakPli()
meshtastic_TAKPacket takPacket = {.is_compressed = true,
.has_contact = true,
- .contact = {0},
+ .contact = meshtastic_Contact_init_default,
.has_group = true,
.group = {meshtastic_MemberRole_TeamMember, meshtastic_Team_Cyan},
.has_status = true,
@@ -278,13 +283,13 @@ meshtastic_MeshPacket *PositionModule::allocAtakPli()
.battery = powerStatus->getBatteryChargePercent(),
},
.which_payload_variant = meshtastic_TAKPacket_pli_tag,
- {.pli = {
- .latitude_i = localPosition.latitude_i,
- .longitude_i = localPosition.longitude_i,
- .altitude = localPosition.altitude_hae,
- .speed = localPosition.ground_speed,
- .course = static_cast(localPosition.ground_track),
- }}};
+ .payload_variant = {.pli = {
+ .latitude_i = localPosition.latitude_i,
+ .longitude_i = localPosition.longitude_i,
+ .altitude = localPosition.altitude_hae,
+ .speed = localPosition.ground_speed,
+ .course = static_cast(localPosition.ground_track),
+ }}};
auto length = unishox2_compress_lines(owner.long_name, strlen(owner.long_name), takPacket.contact.device_callsign,
sizeof(takPacket.contact.device_callsign) - 1, USX_PSET_DFLT, NULL);
@@ -343,8 +348,8 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t cha
service->sendToMesh(p, RX_SRC_LOCAL, true);
- if ((config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER ||
- config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) &&
+ if (IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_TRACKER,
+ meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) &&
config.power.is_power_saving) {
LOG_DEBUG("Starting next execution in 5 seconds and then going to sleep.\n");
sleepOnNextExecution = true;
diff --git a/src/modules/PositionModule.h b/src/modules/PositionModule.h
index c4ef66501..41b86b795 100644
--- a/src/modules/PositionModule.h
+++ b/src/modules/PositionModule.h
@@ -63,7 +63,7 @@ class PositionModule : public ProtobufModule, private concu
bool hasQualityTimesource();
const uint32_t minimumTimeThreshold =
- Default::getConfiguredOrDefaultMsScaled(config.position.broadcast_smart_minimum_interval_secs, 30, numOnlineNodes);
+ Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30);
};
struct SmartPosition {
diff --git a/src/modules/PowerStressModule.cpp b/src/modules/PowerStressModule.cpp
index 4c9f0df88..48159ba54 100644
--- a/src/modules/PowerStressModule.cpp
+++ b/src/modules/PowerStressModule.cpp
@@ -9,6 +9,7 @@
#include "main.h"
#include "sleep.h"
#include "target_specific.h"
+#include
extern void printInfo();
@@ -114,7 +115,7 @@ int32_t PowerStressModule::runOnce()
break;
case meshtastic_PowerStressMessage_Opcode_CPU_FULLON: {
uint32_t start_msec = millis();
- while ((millis() - start_msec) < (uint32_t)sleep_msec)
+ while (Throttle::isWithinTimespanMs(start_msec, sleep_msec))
; // Don't let CPU idle at all
sleep_msec = 0; // we already slept
break;
diff --git a/src/modules/RangeTestModule.cpp b/src/modules/RangeTestModule.cpp
index 8154a661e..e78b4e68d 100644
--- a/src/modules/RangeTestModule.cpp
+++ b/src/modules/RangeTestModule.cpp
@@ -19,6 +19,7 @@
#include "configuration.h"
#include "gps/GeoCoord.h"
#include
+#include
RangeTestModule *rangeTestModule;
RangeTestModuleRadio *rangeTestModuleRadio;
@@ -79,7 +80,7 @@ int32_t RangeTestModule::runOnce()
}
// If we have been running for more than 8 hours, turn module back off
- if (millis() - started > 28800000) {
+ if (!Throttle::isWithinTimespanMs(started, 28800000)) {
LOG_INFO("Range Test Module - Disabling after 8 hours\n");
return disable();
} else {
@@ -114,7 +115,7 @@ void RangeTestModuleRadio::sendPayload(NodeNum dest, bool wantReplies)
packetSequence++;
- static char heartbeatString[MAX_RHPACKETLEN];
+ static char heartbeatString[MAX_LORA_PAYLOAD_LEN + 1];
snprintf(heartbeatString, sizeof(heartbeatString), "seq %u", packetSequence);
p->decoded.payload.size = strlen(heartbeatString); // You must specify how many bytes are in the reply
@@ -138,7 +139,7 @@ ProcessMessage RangeTestModuleRadio::handleReceived(const meshtastic_MeshPacket
LOG_INFO.getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes);
*/
- if (getFrom(&mp) != nodeDB->getNodeNum()) {
+ if (!isFromUs(&mp)) {
if (moduleConfig.range_test.save) {
appendFile(mp);
diff --git a/src/modules/RemoteHardwareModule.cpp b/src/modules/RemoteHardwareModule.cpp
index 6910005a8..43612e450 100644
--- a/src/modules/RemoteHardwareModule.cpp
+++ b/src/modules/RemoteHardwareModule.cpp
@@ -5,6 +5,7 @@
#include "Router.h"
#include "configuration.h"
#include "main.h"
+#include
#define NUM_GPIOS 64
@@ -14,26 +15,44 @@
// a max of one change per 30 seconds
#define WATCH_INTERVAL_MSEC (30 * 1000)
+// Tests for access to read from or write to a specified GPIO pin
+static bool pinAccessAllowed(uint64_t mask, uint8_t pin)
+{
+ // If undefined pin access is allowed, don't check the pin and just return true
+ if (moduleConfig.remote_hardware.allow_undefined_pin_access) {
+ return true;
+ }
+
+ // Test to see if the pin is in the list of allowed pins and return true if found
+ if (mask & (1ULL << pin)) {
+ return true;
+ }
+
+ return false;
+}
+
/// Set pin modes for every set bit in a mask
-static void pinModes(uint64_t mask, uint8_t mode)
+static void pinModes(uint64_t mask, uint8_t mode, uint64_t maskAvailable)
{
for (uint64_t i = 0; i < NUM_GPIOS; i++) {
if (mask & (1ULL << i)) {
- pinMode(i, mode);
+ if (pinAccessAllowed(maskAvailable, i)) {
+ pinMode(i, mode);
+ }
}
}
}
/// Read all the pins mentioned in a mask
-static uint64_t digitalReads(uint64_t mask)
+static uint64_t digitalReads(uint64_t mask, uint64_t maskAvailable)
{
uint64_t res = 0;
- pinModes(mask, INPUT_PULLUP);
+ pinModes(mask, INPUT_PULLUP, maskAvailable);
for (uint64_t i = 0; i < NUM_GPIOS; i++) {
uint64_t m = 1ULL << i;
- if (mask & m) {
+ if (mask & m && pinAccessAllowed(maskAvailable, i)) {
if (digitalRead(i)) {
res |= m;
}
@@ -47,6 +66,13 @@ RemoteHardwareModule::RemoteHardwareModule()
: ProtobufModule("remotehardware", meshtastic_PortNum_REMOTE_HARDWARE_APP, &meshtastic_HardwareMessage_msg),
concurrency::OSThread("RemoteHardwareModule")
{
+ // restrict to the gpio channel for rx
+ boundChannel = Channels::gpioChannel;
+
+ // Pull available pin allowlist from config and build a bitmask out of it for fast comparisons later
+ for (uint8_t i = 0; i < 4; i++) {
+ availablePins += 1ULL << moduleConfig.remote_hardware.available_pins[i].gpio_pin;
+ }
}
bool RemoteHardwareModule::handleReceivedProtobuf(const meshtastic_MeshPacket &req, meshtastic_HardwareMessage *pptr)
@@ -60,10 +86,10 @@ bool RemoteHardwareModule::handleReceivedProtobuf(const meshtastic_MeshPacket &r
// Print notification to LCD screen
screen->print("Write GPIOs\n");
- pinModes(p.gpio_mask, OUTPUT);
+ pinModes(p.gpio_mask, OUTPUT, availablePins);
for (uint8_t i = 0; i < NUM_GPIOS; i++) {
uint64_t mask = 1ULL << i;
- if (p.gpio_mask & mask) {
+ if (p.gpio_mask & mask && pinAccessAllowed(availablePins, i)) {
digitalWrite(i, (p.gpio_value & mask) ? 1 : 0);
}
}
@@ -76,7 +102,7 @@ bool RemoteHardwareModule::handleReceivedProtobuf(const meshtastic_MeshPacket &r
if (screen)
screen->print("Read GPIOs\n");
- uint64_t res = digitalReads(p.gpio_mask);
+ uint64_t res = digitalReads(p.gpio_mask, availablePins);
// Send the reply
meshtastic_HardwareMessage r = meshtastic_HardwareMessage_init_default;
@@ -116,11 +142,10 @@ bool RemoteHardwareModule::handleReceivedProtobuf(const meshtastic_MeshPacket &r
int32_t RemoteHardwareModule::runOnce()
{
if (moduleConfig.remote_hardware.enabled && watchGpios) {
- uint32_t now = millis();
- if (now - lastWatchMsec >= WATCH_INTERVAL_MSEC) {
- uint64_t curVal = digitalReads(watchGpios);
- lastWatchMsec = now;
+ if (!Throttle::isWithinTimespanMs(lastWatchMsec, WATCH_INTERVAL_MSEC)) {
+ uint64_t curVal = digitalReads(watchGpios, availablePins);
+ lastWatchMsec = millis();
if (curVal != previousWatch) {
previousWatch = curVal;
diff --git a/src/modules/RemoteHardwareModule.h b/src/modules/RemoteHardwareModule.h
index dd39f5b69..4dc31d405 100644
--- a/src/modules/RemoteHardwareModule.h
+++ b/src/modules/RemoteHardwareModule.h
@@ -17,6 +17,9 @@ class RemoteHardwareModule : public ProtobufModule,
/// The timestamp of our last watch event (we throttle watches to 1 change every 30 seconds)
uint32_t lastWatchMsec = 0;
+ /// A bitmask of GPIOs that are exposed to the mesh if undefined access is not enabled
+ uint64_t availablePins = 0;
+
public:
/** Constructor
* name is for debugging output
diff --git a/src/modules/RoutingModule.cpp b/src/modules/RoutingModule.cpp
index b7be4abc9..3b7be1ab7 100644
--- a/src/modules/RoutingModule.cpp
+++ b/src/modules/RoutingModule.cpp
@@ -28,7 +28,7 @@ bool RoutingModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mesh
// FIXME - move this to a non promsicious PhoneAPI module?
// Note: we are careful not to send back packets that started with the phone back to the phone
- if ((mp.to == NODENUM_BROADCAST || mp.to == nodeDB->getNodeNum()) && (mp.from != 0)) {
+ if ((mp.to == NODENUM_BROADCAST || isToUs(&mp)) && (mp.from != 0)) {
printPacket("Delivering rx packet", &mp);
service->handleFromRadio(&mp);
}
diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp
index f0ba64f65..d40b59345 100644
--- a/src/modules/SerialModule.cpp
+++ b/src/modules/SerialModule.cpp
@@ -7,6 +7,7 @@
#include "Router.h"
#include "configuration.h"
#include
+#include
/*
SerialModule
@@ -62,6 +63,9 @@ SerialModuleRadio *serialModuleRadio;
#if defined(TTGO_T_ECHO) || defined(CANARYONE)
SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("SerialModule") {}
static Print *serialPrint = &Serial;
+#elif defined(CONFIG_IDF_TARGET_ESP32C6)
+SerialModule::SerialModule() : StreamAPI(&Serial1), concurrency::OSThread("SerialModule") {}
+static Print *serialPrint = &Serial1;
#else
SerialModule::SerialModule() : StreamAPI(&Serial2), concurrency::OSThread("SerialModule") {}
static Print *serialPrint = &Serial2;
@@ -97,8 +101,7 @@ SerialModuleRadio::SerialModuleRadio() : MeshModule("SerialModuleRadio")
*/
bool SerialModule::checkIsConnected()
{
- uint32_t now = millis();
- return (now - lastContactMsec) < SERIAL_CONNECTION_TIMEOUT;
+ return Throttle::isWithinTimespanMs(lastContactMsec, SERIAL_CONNECTION_TIMEOUT);
}
int32_t SerialModule::runOnce()
@@ -137,7 +140,16 @@ int32_t SerialModule::runOnce()
// Give it a chance to flush out 💩
delay(10);
}
-#ifdef ARCH_ESP32
+#if defined(CONFIG_IDF_TARGET_ESP32C6)
+ if (moduleConfig.serial.rxd && moduleConfig.serial.txd) {
+ Serial1.setRxBufferSize(RX_BUFFER);
+ Serial1.begin(baud, SERIAL_8N1, moduleConfig.serial.rxd, moduleConfig.serial.txd);
+ } else {
+ Serial.begin(baud);
+ Serial.setTimeout(moduleConfig.serial.timeout > 0 ? moduleConfig.serial.timeout : TIMEOUT);
+ }
+
+#elif defined(ARCH_ESP32)
if (moduleConfig.serial.rxd && moduleConfig.serial.txd) {
Serial2.setRxBufferSize(RX_BUFFER);
@@ -182,13 +194,13 @@ int32_t SerialModule::runOnce()
return runOncePart();
} else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_NMEA) && HAS_GPS) {
// in NMEA mode send out GGA every 2 seconds, Don't read from Port
- if (millis() - lastNmeaTime > 2000) {
+ if (!Throttle::isWithinTimespanMs(lastNmeaTime, 2000)) {
lastNmeaTime = millis();
printGGA(outbuf, sizeof(outbuf), localPosition);
serialPrint->printf("%s", outbuf);
}
} else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO) && HAS_GPS) {
- if (millis() - lastNmeaTime > 10000) {
+ if (!Throttle::isWithinTimespanMs(lastNmeaTime, 10000)) {
lastNmeaTime = millis();
uint32_t readIndex = 0;
const meshtastic_NodeInfoLite *tempNodeInfo = nodeDB->readNextMeshNode(readIndex);
@@ -205,8 +217,13 @@ int32_t SerialModule::runOnce()
processWXSerial();
} else {
+#if defined(CONFIG_IDF_TARGET_ESP32C6)
+ while (Serial1.available()) {
+ serialPayloadSize = Serial1.readBytes(serialBytes, meshtastic_Constants_DATA_PAYLOAD_LEN);
+#else
while (Serial2.available()) {
serialPayloadSize = Serial2.readBytes(serialBytes, meshtastic_Constants_DATA_PAYLOAD_LEN);
+#endif
serialModuleRadio->sendPayload();
}
}
@@ -293,7 +310,7 @@ ProcessMessage SerialModuleRadio::handleReceived(const meshtastic_MeshPacket &mp
// LOG_DEBUG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n",
// nodeDB->getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes);
- if (getFrom(&mp) == nodeDB->getNodeNum()) {
+ if (!isFromUs(&mp)) {
/*
* If moduleConfig.serial.echo is true, then echo the packets that are sent out
@@ -392,7 +409,7 @@ uint32_t SerialModule::getBaudRate()
*/
void SerialModule::processWXSerial()
{
-#if !defined(TTGO_T_ECHO) && !defined(CANARYONE)
+#if !defined(TTGO_T_ECHO) && !defined(CANARYONE) && !defined(CONFIG_IDF_TARGET_ESP32C6)
static unsigned int lastAveraged = 0;
static unsigned int averageIntervalMillis = 300000; // 5 minutes hard coded.
static double dir_sum_sin = 0;
@@ -500,7 +517,7 @@ void SerialModule::processWXSerial()
LOG_INFO("WS85 : %i %.1fg%.1f %.1fv %.1fv\n", atoi(windDir), strtof(windVel, nullptr), strtof(windGust, nullptr),
batVoltageF, capVoltageF);
}
- if (gotwind && millis() - lastAveraged > averageIntervalMillis) {
+ if (gotwind && !Throttle::isWithinTimespanMs(lastAveraged, averageIntervalMillis)) {
// calulate averages and send to the mesh
float velAvg = 1.0 * velSum / velCount;
diff --git a/src/modules/esp32/StoreForwardModule.cpp b/src/modules/StoreForwardModule.cpp
similarity index 89%
rename from src/modules/esp32/StoreForwardModule.cpp
rename to src/modules/StoreForwardModule.cpp
index c696d342a..e0092839f 100644
--- a/src/modules/esp32/StoreForwardModule.cpp
+++ b/src/modules/StoreForwardModule.cpp
@@ -17,6 +17,7 @@
#include "NodeDB.h"
#include "RTC.h"
#include "Router.h"
+#include "Throttle.h"
#include "airtime.h"
#include "configuration.h"
#include "memGet.h"
@@ -31,7 +32,7 @@ StoreForwardModule *storeForwardModule;
int32_t StoreForwardModule::runOnce()
{
-#ifdef ARCH_ESP32
+#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
if (moduleConfig.store_forward.enabled && is_server) {
// Send out the message queue.
if (this->busy) {
@@ -42,10 +43,10 @@ int32_t StoreForwardModule::runOnce()
this->busy = false;
}
}
- } else if (this->heartbeat && (millis() - lastHeartbeat > (heartbeatInterval * 1000)) &&
+ } else if (this->heartbeat && (!Throttle::isWithinTimespanMs(lastHeartbeat, heartbeatInterval * 1000)) &&
airTime->isTxAllowedChannelUtil(true)) {
lastHeartbeat = millis();
- LOG_INFO("*** Sending heartbeat\n");
+ LOG_INFO("Sending heartbeat\n");
meshtastic_StoreAndForward sf = meshtastic_StoreAndForward_init_zero;
sf.rr = meshtastic_StoreAndForward_RequestResponse_ROUTER_HEARTBEAT;
sf.which_variant = meshtastic_StoreAndForward_heartbeat_tag;
@@ -69,8 +70,8 @@ void StoreForwardModule::populatePSRAM()
https://learn.upesy.com/en/programmation/psram.html#psram-tab
*/
- LOG_DEBUG("*** Before PSRAM initialization: heap %d/%d PSRAM %d/%d\n", memGet.getFreeHeap(), memGet.getHeapSize(),
- memGet.getFreePsram(), memGet.getPsramSize());
+ LOG_DEBUG("Before PSRAM init: heap %d/%d PSRAM %d/%d\n", memGet.getFreeHeap(), memGet.getHeapSize(), memGet.getFreePsram(),
+ memGet.getPsramSize());
/* Use a maximum of 2/3 the available PSRAM unless otherwise specified.
Note: This needs to be done after every thing that would use PSRAM
@@ -78,12 +79,16 @@ void StoreForwardModule::populatePSRAM()
uint32_t numberOfPackets =
(this->records ? this->records : (((memGet.getFreePsram() / 3) * 2) / sizeof(PacketHistoryStruct)));
this->records = numberOfPackets;
-
+#if defined(ARCH_ESP32)
this->packetHistory = static_cast(ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct)));
+#elif defined(ARCH_PORTDUINO)
+ this->packetHistory = static_cast(calloc(numberOfPackets, sizeof(PacketHistoryStruct)));
- LOG_DEBUG("*** After PSRAM initialization: heap %d/%d PSRAM %d/%d\n", memGet.getFreeHeap(), memGet.getHeapSize(),
- memGet.getFreePsram(), memGet.getPsramSize());
- LOG_DEBUG("*** numberOfPackets for packetHistory - %u\n", numberOfPackets);
+#endif
+
+ LOG_DEBUG("After PSRAM init: heap %d/%d PSRAM %d/%d\n", memGet.getFreeHeap(), memGet.getHeapSize(), memGet.getFreePsram(),
+ memGet.getPsramSize());
+ LOG_DEBUG("numberOfPackets for packetHistory - %u\n", numberOfPackets);
}
/**
@@ -100,11 +105,11 @@ void StoreForwardModule::historySend(uint32_t secAgo, uint32_t to)
queueSize = this->historyReturnMax;
if (queueSize) {
- LOG_INFO("*** S&F - Sending %u message(s)\n", queueSize);
+ LOG_INFO("S&F - Sending %u message(s)\n", queueSize);
this->busy = true; // runOnce() will pickup the next steps once busy = true.
this->busyTo = to;
} else {
- LOG_INFO("*** S&F - No history to send\n");
+ LOG_INFO("S&F - No history\n");
}
meshtastic_StoreAndForward sf = meshtastic_StoreAndForward_init_zero;
sf.rr = meshtastic_StoreAndForward_RequestResponse_ROUTER_HISTORY;
@@ -127,7 +132,7 @@ uint32_t StoreForwardModule::getNumAvailablePackets(NodeNum dest, uint32_t last_
{
uint32_t count = 0;
if (lastRequest.find(dest) == lastRequest.end()) {
- lastRequest[dest] = 0;
+ lastRequest.emplace(dest, 0);
}
for (uint32_t i = lastRequest[dest]; i < this->packetHistoryTotalCount; i++) {
if (this->packetHistory[i].time && (this->packetHistory[i].time > last_time)) {
@@ -182,7 +187,7 @@ void StoreForwardModule::historyAdd(const meshtastic_MeshPacket &mp)
const auto &p = mp.decoded;
if (this->packetHistoryTotalCount == this->records) {
- LOG_WARN("*** S&F - PSRAM Full. Starting overwrite now.\n");
+ LOG_WARN("S&F - PSRAM Full. Starting overwrite.\n");
this->packetHistoryTotalCount = 0;
for (auto &i : lastRequest) {
i.second = 0; // Clear the last request index for each client device
@@ -210,7 +215,7 @@ bool StoreForwardModule::sendPayload(NodeNum dest, uint32_t last_time)
{
meshtastic_MeshPacket *p = preparePayload(dest, last_time);
if (p) {
- LOG_INFO("*** Sending S&F Payload\n");
+ LOG_INFO("Sending S&F Payload\n");
service->sendToMesh(p);
this->requestCount++;
return true;
@@ -326,9 +331,9 @@ void StoreForwardModule::sendErrorTextMessage(NodeNum dest, bool want_response)
pr->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
const char *str;
if (this->busy) {
- str = "** S&F - Busy. Try again shortly.";
+ str = "S&F - Busy. Try again shortly.";
} else {
- str = "** S&F - Not available on this channel.";
+ str = "S&F not permitted on the public channel.";
}
LOG_WARN("%s\n", str);
memcpy(pr->decoded.payload.bytes, str, strlen(str));
@@ -360,7 +365,7 @@ void StoreForwardModule::statsSend(uint32_t to)
sf.variant.stats.return_max = this->historyReturnMax;
sf.variant.stats.return_window = this->historyReturnWindow;
- LOG_DEBUG("*** Sending S&F Stats\n");
+ LOG_DEBUG("Sending S&F Stats\n");
storeForwardModule->sendMessage(to, sf);
}
@@ -372,14 +377,13 @@ void StoreForwardModule::statsSend(uint32_t to)
*/
ProcessMessage StoreForwardModule::handleReceived(const meshtastic_MeshPacket &mp)
{
-#ifdef ARCH_ESP32
+#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
if (moduleConfig.store_forward.enabled) {
if ((mp.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) && is_server) {
auto &p = mp.decoded;
- if (mp.to == nodeDB->getNodeNum() && (p.payload.bytes[0] == 'S') && (p.payload.bytes[1] == 'F') &&
- (p.payload.bytes[2] == 0x00)) {
- LOG_DEBUG("*** Legacy Request to send\n");
+ if (isToUs(&mp) && (p.payload.bytes[0] == 'S') && (p.payload.bytes[1] == 'F') && (p.payload.bytes[2] == 0x00)) {
+ LOG_DEBUG("Legacy Request to send\n");
// Send the last 60 minutes of messages.
if (this->busy || channels.isDefaultChannel(mp.channel)) {
@@ -389,9 +393,9 @@ ProcessMessage StoreForwardModule::handleReceived(const meshtastic_MeshPacket &m
}
} else {
storeForwardModule->historyAdd(mp);
- LOG_INFO("*** S&F stored. Message history contains %u records now.\n", this->packetHistoryTotalCount);
+ LOG_INFO("S&F stored. Message history contains %u records now.\n", this->packetHistoryTotalCount);
}
- } else if (getFrom(&mp) != nodeDB->getNodeNum() && mp.decoded.portnum == meshtastic_PortNum_STORE_FORWARD_APP) {
+ } else if (!isFromUs(&mp) && mp.decoded.portnum == meshtastic_PortNum_STORE_FORWARD_APP) {
auto &p = mp.decoded;
meshtastic_StoreAndForward scratch;
meshtastic_StoreAndForward *decoded = NULL;
@@ -435,7 +439,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp,
if (is_server) {
// stop sending stuff, the client wants to abort or has another error
if ((this->busy) && (this->busyTo == getFrom(&mp))) {
- LOG_ERROR("*** Client in ERROR or ABORT requested\n");
+ LOG_ERROR("Client in ERROR or ABORT requested\n");
this->requestCount = 0;
this->busy = false;
}
@@ -445,7 +449,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp,
case meshtastic_StoreAndForward_RequestResponse_CLIENT_HISTORY:
if (is_server) {
requests_history++;
- LOG_INFO("*** Client Request to send HISTORY\n");
+ LOG_INFO("Client Request to send HISTORY\n");
// Send the last 60 minutes of messages.
if (this->busy || channels.isDefaultChannel(mp.channel)) {
sendErrorTextMessage(getFrom(&mp), mp.decoded.want_response);
@@ -462,7 +466,6 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp,
case meshtastic_StoreAndForward_RequestResponse_CLIENT_PING:
if (is_server) {
- LOG_INFO("*** StoreAndForward_RequestResponse_CLIENT_PING\n");
// respond with a ROUTER PONG
storeForwardModule->sendMessage(getFrom(&mp), meshtastic_StoreAndForward_RequestResponse_ROUTER_PONG);
}
@@ -470,17 +473,16 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp,
case meshtastic_StoreAndForward_RequestResponse_CLIENT_PONG:
if (is_server) {
- LOG_INFO("*** StoreAndForward_RequestResponse_CLIENT_PONG\n");
// NodeDB is already updated
}
break;
case meshtastic_StoreAndForward_RequestResponse_CLIENT_STATS:
if (is_server) {
- LOG_INFO("*** Client Request to send STATS\n");
+ LOG_INFO("Client Request to send STATS\n");
if (this->busy) {
storeForwardModule->sendMessage(getFrom(&mp), meshtastic_StoreAndForward_RequestResponse_ROUTER_BUSY);
- LOG_INFO("*** S&F - Busy. Try again shortly.\n");
+ LOG_INFO("S&F - Busy. Try again shortly.\n");
} else {
storeForwardModule->statsSend(getFrom(&mp));
}
@@ -490,7 +492,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp,
case meshtastic_StoreAndForward_RequestResponse_ROUTER_ERROR:
case meshtastic_StoreAndForward_RequestResponse_ROUTER_BUSY:
if (is_client) {
- LOG_DEBUG("*** StoreAndForward_RequestResponse_ROUTER_BUSY\n");
+ LOG_DEBUG("StoreAndForward_RequestResponse_ROUTER_BUSY\n");
// retry in messages_saved * packetTimeMax ms
retry_delay = millis() + getNumAvailablePackets(this->busyTo, this->last_time) * packetTimeMax *
(meshtastic_StoreAndForward_RequestResponse_ROUTER_ERROR ? 2 : 1);
@@ -506,13 +508,12 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp,
heartbeatInterval = p->variant.heartbeat.period;
}
lastHeartbeat = millis();
- LOG_INFO("*** StoreAndForward Heartbeat received\n");
+ LOG_INFO("StoreAndForward Heartbeat received\n");
}
break;
case meshtastic_StoreAndForward_RequestResponse_ROUTER_PING:
if (is_client) {
- LOG_DEBUG("*** StoreAndForward_RequestResponse_ROUTER_PING\n");
// respond with a CLIENT PONG
storeForwardModule->sendMessage(getFrom(&mp), meshtastic_StoreAndForward_RequestResponse_CLIENT_PONG);
}
@@ -520,7 +521,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp,
case meshtastic_StoreAndForward_RequestResponse_ROUTER_STATS:
if (is_client) {
- LOG_DEBUG("*** Router Response STATS\n");
+ LOG_DEBUG("Router Response STATS\n");
// These fields only have informational purpose on a client. Fill them to consume later.
if (p->which_variant == meshtastic_StoreAndForward_stats_tag) {
this->records = p->variant.stats.messages_max;
@@ -538,7 +539,7 @@ bool StoreForwardModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp,
// These fields only have informational purpose on a client. Fill them to consume later.
if (p->which_variant == meshtastic_StoreAndForward_history_tag) {
this->historyReturnWindow = p->variant.history.window / 60000;
- LOG_INFO("*** Router Response HISTORY - Sending %d messages from last %d minutes\n",
+ LOG_INFO("Router Response HISTORY - Sending %d messages from last %d minutes\n",
p->variant.history.history_messages, this->historyReturnWindow);
}
}
@@ -555,7 +556,7 @@ StoreForwardModule::StoreForwardModule()
ProtobufModule("StoreForward", meshtastic_PortNum_STORE_FORWARD_APP, &meshtastic_StoreAndForward_msg)
{
-#ifdef ARCH_ESP32
+#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
isPromiscuous = true; // Brown chicken brown cow
@@ -572,7 +573,7 @@ StoreForwardModule::StoreForwardModule()
// Router
if ((config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER || moduleConfig.store_forward.is_server)) {
- LOG_INFO("*** Initializing Store & Forward Module in Server mode\n");
+ LOG_INFO("Initializing Store & Forward Module in Server mode\n");
if (memGet.getPsramSize() > 0) {
if (memGet.getFreePsram() >= 1024 * 1024) {
@@ -600,18 +601,17 @@ StoreForwardModule::StoreForwardModule()
this->populatePSRAM();
is_server = true;
} else {
- LOG_INFO("*** Device has less than 1M of PSRAM free.\n");
- LOG_INFO("*** Store & Forward Module - disabling server.\n");
+ LOG_INFO(".\n");
+ LOG_INFO("S&F: not enough PSRAM free, disabling.\n");
}
} else {
- LOG_INFO("*** Device doesn't have PSRAM.\n");
- LOG_INFO("*** Store & Forward Module - disabling server.\n");
+ LOG_INFO("S&F: device doesn't have PSRAM, disabling.\n");
}
// Client
} else {
is_client = true;
- LOG_INFO("*** Initializing Store & Forward Module in Client mode\n");
+ LOG_INFO("Initializing Store & Forward Module in Client mode\n");
}
} else {
disable();
diff --git a/src/modules/esp32/StoreForwardModule.h b/src/modules/StoreForwardModule.h
similarity index 100%
rename from src/modules/esp32/StoreForwardModule.h
rename to src/modules/StoreForwardModule.h
diff --git a/src/modules/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp
index d07296710..0b6be1b7e 100644
--- a/src/modules/Telemetry/AirQualityTelemetry.cpp
+++ b/src/modules/Telemetry/AirQualityTelemetry.cpp
@@ -10,11 +10,12 @@
#include "PowerFSM.h"
#include "RTC.h"
#include "Router.h"
+#include "detect/ScanI2CTwoWire.h"
#include "main.h"
+#include
int32_t AirQualityTelemetryModule::runOnce()
{
- int32_t result = INT32_MAX;
/*
Uncomment the preferences below if you want to use the module
without having to configure it from the PythonAPI or WebUI.
@@ -29,31 +30,45 @@ int32_t AirQualityTelemetryModule::runOnce()
if (firstTime) {
// This is the first time the OSThread library has called this function, so do some setup
- firstTime = 0;
+ firstTime = false;
if (moduleConfig.telemetry.air_quality_enabled) {
LOG_INFO("Air quality Telemetry: Initializing\n");
if (!aqi.begin_I2C()) {
- LOG_WARN("Could not establish i2c connection to AQI sensor\n");
+ LOG_WARN("Could not establish i2c connection to AQI sensor. Rescanning...\n");
+ // rescan for late arriving sensors. AQI Module starts about 10 seconds into the boot so this is plenty.
+ uint8_t i2caddr_scan[] = {PMSA0031_ADDR};
+ uint8_t i2caddr_asize = 1;
+ auto i2cScanner = std::unique_ptr(new ScanI2CTwoWire());
+#if defined(I2C_SDA1)
+ i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1, i2caddr_scan, i2caddr_asize);
+#endif
+ i2cScanner->scanPort(ScanI2C::I2CPort::WIRE, i2caddr_scan, i2caddr_asize);
+ auto found = i2cScanner->find(ScanI2C::DeviceType::PMSA0031);
+ if (found.type != ScanI2C::DeviceType::NONE) {
+ nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first = found.address.address;
+ nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].second =
+ i2cScanner->fetchI2CBus(found.address);
+ return 1000;
+ }
return disable();
}
return 1000;
}
- return result;
+ return disable();
} else {
// if we somehow got to a second run of this module with measurement disabled, then just wait forever
if (!moduleConfig.telemetry.air_quality_enabled)
- return result;
+ return disable();
- uint32_t now = millis();
if (((lastSentToMesh == 0) ||
- ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.air_quality_interval,
- default_telemetry_broadcast_interval_secs,
- numOnlineNodes))) &&
+ !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled(
+ moduleConfig.telemetry.air_quality_interval,
+ default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
airTime->isTxAllowedAirUtil()) {
sendTelemetry();
- lastSentToMesh = now;
+ lastSentToMesh = millis();
} else if (service->isToPhoneQueueEmpty()) {
// Just send to phone when it's not our time to send to mesh yet
// Only send while queue is empty (phone assumed connected)
diff --git a/src/modules/Telemetry/AirQualityTelemetry.h b/src/modules/Telemetry/AirQualityTelemetry.h
index 23df6ac58..fb8edd07e 100644
--- a/src/modules/Telemetry/AirQualityTelemetry.h
+++ b/src/modules/Telemetry/AirQualityTelemetry.h
@@ -44,7 +44,7 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf
private:
Adafruit_PM25AQI aqi;
PM25_AQI_Data data = {0};
- bool firstTime = 1;
+ bool firstTime = true;
meshtastic_MeshPacket *lastMeasurementPacket;
uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute
uint32_t lastSentToMesh = 0;
diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp
index f22685d43..eb3f67e96 100644
--- a/src/modules/Telemetry/DeviceTelemetry.cpp
+++ b/src/modules/Telemetry/DeviceTelemetry.cpp
@@ -11,14 +11,15 @@
#include "main.h"
#include
#include
+#include
#define MAGIC_USB_BATTERY_LEVEL 101
int32_t DeviceTelemetryModule::runOnce()
{
refreshUptime();
- bool isImpoliteRole = config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR ||
- config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER;
+ bool isImpoliteRole =
+ IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_SENSOR, meshtastic_Config_DeviceConfig_Role_ROUTER);
if (((lastSentToMesh == 0) ||
((uptimeLastMs - lastSentToMesh) >=
Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.device_update_interval,
@@ -100,8 +101,9 @@ meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry()
#if ARCH_PORTDUINO
t.variant.device_metrics.battery_level = MAGIC_USB_BATTERY_LEVEL;
#else
- t.variant.device_metrics.battery_level =
- powerStatus->getIsCharging() ? MAGIC_USB_BATTERY_LEVEL : powerStatus->getBatteryChargePercent();
+ t.variant.device_metrics.battery_level = (!powerStatus->getHasBattery() || powerStatus->getIsCharging())
+ ? MAGIC_USB_BATTERY_LEVEL
+ : powerStatus->getBatteryChargePercent();
#endif
t.variant.device_metrics.channel_utilization = airTime->channelUtilizationPercent();
t.variant.device_metrics.voltage = powerStatus->getBatteryVoltageMv() / 1000.0;
@@ -123,8 +125,13 @@ void DeviceTelemetryModule::sendLocalStatsToPhone()
telemetry.variant.local_stats.num_total_nodes = nodeDB->getNumMeshNodes();
if (RadioLibInterface::instance) {
telemetry.variant.local_stats.num_packets_tx = RadioLibInterface::instance->txGood;
- telemetry.variant.local_stats.num_packets_rx = RadioLibInterface::instance->rxGood;
+ telemetry.variant.local_stats.num_packets_rx = RadioLibInterface::instance->rxGood + RadioLibInterface::instance->rxBad;
telemetry.variant.local_stats.num_packets_rx_bad = RadioLibInterface::instance->rxBad;
+ telemetry.variant.local_stats.num_tx_relay = RadioLibInterface::instance->txRelay;
+ }
+ if (router) {
+ telemetry.variant.local_stats.num_rx_dupe = router->rxDupe;
+ telemetry.variant.local_stats.num_tx_relay_canceled = router->txRelayCanceled;
}
LOG_INFO(
diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp
index 31cb2f838..1ccdedeb7 100644
--- a/src/modules/Telemetry/EnvironmentTelemetry.cpp
+++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp
@@ -10,6 +10,7 @@
#include "PowerFSM.h"
#include "RTC.h"
#include "Router.h"
+#include "UnitConversions.h"
#include "main.h"
#include "power.h"
#include "sleep.h"
@@ -64,6 +65,7 @@ T1000xSensor t1000xSensor;
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
#include "graphics/ScreenFonts.h"
+#include
int32_t EnvironmentTelemetryModule::runOnce()
{
@@ -82,7 +84,7 @@ int32_t EnvironmentTelemetryModule::runOnce()
*/
// moduleConfig.telemetry.environment_measurement_enabled = 1;
- // moduleConfig.telemetry.environment_screen_enabled = 1;
+ // moduleConfig.telemetry.environment_screen_enabled = 1;
// moduleConfig.telemetry.environment_update_interval = 15;
if (!(moduleConfig.telemetry.environment_measurement_enabled || moduleConfig.telemetry.environment_screen_enabled)) {
@@ -143,6 +145,8 @@ int32_t EnvironmentTelemetryModule::runOnce()
result = mlx90632Sensor.runOnce();
if (nau7802Sensor.hasSensor())
result = nau7802Sensor.runOnce();
+ if (max17048Sensor.hasSensor())
+ result = max17048Sensor.runOnce();
#endif
}
return result;
@@ -155,21 +159,20 @@ int32_t EnvironmentTelemetryModule::runOnce()
result = bme680Sensor.runTrigger();
}
- uint32_t now = millis();
if (((lastSentToMesh == 0) ||
- ((now - lastSentToMesh) >=
- Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.environment_update_interval,
- default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
+ !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled(
+ moduleConfig.telemetry.environment_update_interval,
+ default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
airTime->isTxAllowedAirUtil()) {
sendTelemetry();
- lastSentToMesh = now;
- } else if (((lastSentToPhone == 0) || ((now - lastSentToPhone) >= sendToPhoneIntervalMs)) &&
+ lastSentToMesh = millis();
+ } else if (((lastSentToPhone == 0) || !Throttle::isWithinTimespanMs(lastSentToPhone, sendToPhoneIntervalMs)) &&
(service->isToPhoneQueueEmpty())) {
// Just send to phone when it's not our time to send to mesh yet
// Only send while queue is empty (phone assumed connected)
sendTelemetry(NODENUM_BROADCAST, true);
- lastSentToPhone = now;
+ lastSentToPhone = millis();
}
}
return min(sendToPhoneIntervalMs, result);
@@ -180,23 +183,6 @@ bool EnvironmentTelemetryModule::wantUIFrame()
return moduleConfig.telemetry.environment_screen_enabled;
}
-float EnvironmentTelemetryModule::CelsiusToFahrenheit(float c)
-{
- return (c * 9) / 5 + 32;
-}
-
-uint32_t GetTimeSinceMeshPacket(const meshtastic_MeshPacket *mp)
-{
- uint32_t now = getTime();
-
- uint32_t last_seen = mp->rx_time;
- int delta = (int)(now - last_seen);
- if (delta < 0) // our clock must be slightly off still - not set from GPS yet
- delta = 0;
-
- return delta;
-}
-
void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
display->setTextAlignment(TEXT_ALIGN_LEFT);
@@ -211,10 +197,10 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
// Decode the last measurement packet
meshtastic_Telemetry lastMeasurement;
- uint32_t agoSecs = GetTimeSinceMeshPacket(lastMeasurementPacket);
+ uint32_t agoSecs = service->GetTimeSinceMeshPacket(lastMeasurementPacket);
const char *lastSender = getSenderShortName(*lastMeasurementPacket);
- auto &p = lastMeasurementPacket->decoded;
+ const meshtastic_Data &p = lastMeasurementPacket->decoded;
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) {
display->drawString(x, y, "Measurement Error");
LOG_ERROR("Unable to decode last packet");
@@ -226,7 +212,8 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
String last_temp = String(lastMeasurement.variant.environment_metrics.temperature, 0) + "°C";
if (moduleConfig.telemetry.environment_display_fahrenheit) {
- last_temp = String(CelsiusToFahrenheit(lastMeasurement.variant.environment_metrics.temperature), 0) + "°F";
+ last_temp =
+ String(UnitConversions::CelsiusToFahrenheit(lastMeasurement.variant.environment_metrics.temperature), 0) + "°F";
}
// Continue with the remaining details
@@ -397,6 +384,10 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m
m->variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity;
}
}
+ if (max17048Sensor.hasSensor()) {
+ valid = valid && max17048Sensor.getMetrics(m);
+ hasSensor = true;
+ }
#endif
return valid && hasSensor;
@@ -587,6 +578,11 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
+ if (max17048Sensor.hasSensor()) {
+ result = max17048Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
return result;
}
diff --git a/src/modules/Telemetry/EnvironmentTelemetry.h b/src/modules/Telemetry/EnvironmentTelemetry.h
index 59d272e78..e680d8bbd 100644
--- a/src/modules/Telemetry/EnvironmentTelemetry.h
+++ b/src/modules/Telemetry/EnvironmentTelemetry.h
@@ -52,7 +52,6 @@ class EnvironmentTelemetryModule : private concurrency::OSThread, public Protobu
meshtastic_AdminMessage *response) override;
private:
- float CelsiusToFahrenheit(float c);
bool firstTime = 1;
meshtastic_MeshPacket *lastMeasurementPacket;
uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute
diff --git a/src/modules/Telemetry/HealthTelemetry.cpp b/src/modules/Telemetry/HealthTelemetry.cpp
new file mode 100644
index 000000000..bcf9d9d57
--- /dev/null
+++ b/src/modules/Telemetry/HealthTelemetry.cpp
@@ -0,0 +1,249 @@
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO)
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "Default.h"
+#include "HealthTelemetry.h"
+#include "MeshService.h"
+#include "NodeDB.h"
+#include "PowerFSM.h"
+#include "RTC.h"
+#include "Router.h"
+#include "UnitConversions.h"
+#include "main.h"
+#include "power.h"
+#include "sleep.h"
+#include "target_specific.h"
+#include
+#include
+
+// Sensors
+#include "Sensor/MAX30102Sensor.h"
+#include "Sensor/MLX90614Sensor.h"
+
+MAX30102Sensor max30102Sensor;
+MLX90614Sensor mlx90614Sensor;
+
+#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
+#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
+
+#if (HAS_SCREEN)
+#include "graphics/ScreenFonts.h"
+#endif
+#include
+
+int32_t HealthTelemetryModule::runOnce()
+{
+ if (sleepOnNextExecution == true) {
+ sleepOnNextExecution = false;
+ uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.health_update_interval,
+ default_telemetry_broadcast_interval_secs);
+ LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.\n", nightyNightMs);
+ doDeepSleep(nightyNightMs, true);
+ }
+
+ uint32_t result = UINT32_MAX;
+
+ if (!(moduleConfig.telemetry.health_measurement_enabled || moduleConfig.telemetry.health_screen_enabled)) {
+ // If this module is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
+ return disable();
+ }
+
+ if (firstTime) {
+ // This is the first time the OSThread library has called this function, so do some setup
+ firstTime = false;
+
+ if (moduleConfig.telemetry.health_measurement_enabled) {
+ LOG_INFO("Health Telemetry: Initializing\n");
+ // Initialize sensors
+ if (mlx90614Sensor.hasSensor())
+ result = mlx90614Sensor.runOnce();
+ if (max30102Sensor.hasSensor())
+ result = max30102Sensor.runOnce();
+ }
+ return result;
+ } else {
+ // if we somehow got to a second run of this module with measurement disabled, then just wait forever
+ if (!moduleConfig.telemetry.health_measurement_enabled) {
+ return disable();
+ }
+
+ if (((lastSentToMesh == 0) ||
+ !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled(
+ moduleConfig.telemetry.health_update_interval,
+ default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
+ airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
+ airTime->isTxAllowedAirUtil()) {
+ sendTelemetry();
+ lastSentToMesh = millis();
+ } else if (((lastSentToPhone == 0) || !Throttle::isWithinTimespanMs(lastSentToPhone, sendToPhoneIntervalMs)) &&
+ (service->isToPhoneQueueEmpty())) {
+ // Just send to phone when it's not our time to send to mesh yet
+ // Only send while queue is empty (phone assumed connected)
+ sendTelemetry(NODENUM_BROADCAST, true);
+ lastSentToPhone = millis();
+ }
+ }
+ return min(sendToPhoneIntervalMs, result);
+}
+
+bool HealthTelemetryModule::wantUIFrame()
+{
+ return moduleConfig.telemetry.health_screen_enabled;
+}
+
+void HealthTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->setFont(FONT_SMALL);
+
+ if (lastMeasurementPacket == nullptr) {
+ // If there's no valid packet, display "Health"
+ display->drawString(x, y, "Health");
+ display->drawString(x, y += _fontHeight(FONT_SMALL), "No measurement");
+ return;
+ }
+
+ // Decode the last measurement packet
+ meshtastic_Telemetry lastMeasurement;
+ uint32_t agoSecs = service->GetTimeSinceMeshPacket(lastMeasurementPacket);
+ const char *lastSender = getSenderShortName(*lastMeasurementPacket);
+
+ const meshtastic_Data &p = lastMeasurementPacket->decoded;
+ if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) {
+ display->drawString(x, y, "Measurement Error");
+ LOG_ERROR("Unable to decode last packet");
+ return;
+ }
+
+ // Display "Health From: ..." on its own
+ display->drawString(x, y, "Health From: " + String(lastSender) + "(" + String(agoSecs) + "s)");
+
+ String last_temp = String(lastMeasurement.variant.health_metrics.temperature, 0) + "°C";
+ if (moduleConfig.telemetry.environment_display_fahrenheit) {
+ last_temp = String(UnitConversions::CelsiusToFahrenheit(lastMeasurement.variant.health_metrics.temperature), 0) + "°F";
+ }
+
+ // Continue with the remaining details
+ display->drawString(x, y += _fontHeight(FONT_SMALL), "Temp: " + last_temp);
+ if (lastMeasurement.variant.health_metrics.has_heart_bpm) {
+ display->drawString(x, y += _fontHeight(FONT_SMALL),
+ "Heart Rate: " + String(lastMeasurement.variant.health_metrics.heart_bpm, 0) + " bpm");
+ }
+ if (lastMeasurement.variant.health_metrics.has_spO2) {
+ display->drawString(x, y += _fontHeight(FONT_SMALL),
+ "spO2: " + String(lastMeasurement.variant.health_metrics.spO2, 0) + " %");
+ }
+}
+
+bool HealthTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t)
+{
+ if (t->which_variant == meshtastic_Telemetry_health_metrics_tag) {
+#ifdef DEBUG_PORT
+ const char *sender = getSenderShortName(mp);
+
+ LOG_INFO("(Received from %s): temperature=%f, heart_bpm=%d, spO2=%d,\n", sender, t->variant.health_metrics.temperature,
+ t->variant.health_metrics.heart_bpm, t->variant.health_metrics.spO2);
+
+#endif
+ // release previous packet before occupying a new spot
+ if (lastMeasurementPacket != nullptr)
+ packetPool.release(lastMeasurementPacket);
+
+ lastMeasurementPacket = packetPool.allocCopy(mp);
+ }
+
+ return false; // Let others look at this message also if they want
+}
+
+bool HealthTelemetryModule::getHealthTelemetry(meshtastic_Telemetry *m)
+{
+ bool valid = true;
+ bool hasSensor = false;
+ m->time = getTime();
+ m->which_variant = meshtastic_Telemetry_health_metrics_tag;
+ m->variant.health_metrics = meshtastic_HealthMetrics_init_zero;
+
+ if (max30102Sensor.hasSensor()) {
+ valid = valid && max30102Sensor.getMetrics(m);
+ hasSensor = true;
+ }
+ if (mlx90614Sensor.hasSensor()) {
+ valid = valid && mlx90614Sensor.getMetrics(m);
+ hasSensor = true;
+ }
+
+ return valid && hasSensor;
+}
+
+meshtastic_MeshPacket *HealthTelemetryModule::allocReply()
+{
+ if (currentRequest) {
+ auto req = *currentRequest;
+ const auto &p = req.decoded;
+ meshtastic_Telemetry scratch;
+ meshtastic_Telemetry *decoded = NULL;
+ memset(&scratch, 0, sizeof(scratch));
+ if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
+ decoded = &scratch;
+ } else {
+ LOG_ERROR("Error decoding HealthTelemetry module!\n");
+ return NULL;
+ }
+ // Check for a request for health metrics
+ if (decoded->which_variant == meshtastic_Telemetry_health_metrics_tag) {
+ meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
+ if (getHealthTelemetry(&m)) {
+ LOG_INFO("Health telemetry replying to request\n");
+ return allocDataProtobuf(m);
+ } else {
+ return NULL;
+ }
+ }
+ }
+ return NULL;
+}
+
+bool HealthTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
+{
+ meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
+ m.which_variant = meshtastic_Telemetry_health_metrics_tag;
+ m.time = getTime();
+ if (getHealthTelemetry(&m)) {
+ LOG_INFO("(Sending): temperature=%f, heart_bpm=%d, spO2=%d\n", m.variant.health_metrics.temperature,
+ m.variant.health_metrics.heart_bpm, m.variant.health_metrics.spO2);
+
+ sensor_read_error_count = 0;
+
+ meshtastic_MeshPacket *p = allocDataProtobuf(m);
+ p->to = dest;
+ p->decoded.want_response = false;
+ if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR)
+ p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
+ else
+ p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
+ // release previous packet before occupying a new spot
+ if (lastMeasurementPacket != nullptr)
+ packetPool.release(lastMeasurementPacket);
+
+ lastMeasurementPacket = packetPool.allocCopy(*p);
+ if (phoneOnly) {
+ LOG_INFO("Sending packet to phone\n");
+ service->sendToPhone(p);
+ } else {
+ LOG_INFO("Sending packet to mesh\n");
+ service->sendToMesh(p, RX_SRC_LOCAL, true);
+
+ if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR && config.power.is_power_saving) {
+ LOG_DEBUG("Starting next execution in 5 seconds and then going to sleep.\n");
+ sleepOnNextExecution = true;
+ setIntervalFromNow(5000);
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+#endif
diff --git a/src/modules/Telemetry/HealthTelemetry.h b/src/modules/Telemetry/HealthTelemetry.h
new file mode 100644
index 000000000..4ad0da838
--- /dev/null
+++ b/src/modules/Telemetry/HealthTelemetry.h
@@ -0,0 +1,60 @@
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO)
+
+#pragma once
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "NodeDB.h"
+#include "ProtobufModule.h"
+#include
+#include
+
+class HealthTelemetryModule : private concurrency::OSThread, public ProtobufModule